aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-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)80
-rw-r--r--HOWTO/MARKDOWN.md (renamed from README.md.txt)6
-rw-r--r--HOWTO/SYSTEMTAP.md75
-rw-r--r--HOWTO/TESTING.md150
l---------INSTALL-CROSS.md1
-rw-r--r--Makefile.in226
-rw-r--r--README.md6
-rw-r--r--README.systemtap.md72
-rw-r--r--TAR.include8
-rw-r--r--aclocal.m427
-rw-r--r--bootstrap/bin/start.bootbin5254 -> 5246 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin5254 -> 5246 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin0 -> 2236 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11576 -> 11604 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin14388 -> 14424 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bool.beambin16156 -> 16532 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_bsm.beambin13724 -> 12536 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 -> 25124 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin3484 -> 3472 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 -> 6616 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_split.beambin2048 -> 2048 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin8568 -> 7672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_type.beambin14032 -> 14328 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin14604 -> 13220 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin34536 -> 34488 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 -> 37568 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin18952 -> 18952 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin37184 -> 37684 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app70
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.appup1
-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 -> 37664 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 -> 2168 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 -> 47268 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin4208 -> 4276 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.beambin51700 -> 51992 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin51160 -> 51096 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin42904 -> 43292 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin11828 -> 11780 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_life.beambin19924 -> 19992 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin3772 -> 3860 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30996 -> 31280 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.beambin6716 -> 7020 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin26256 -> 26384 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin37260 -> 37112 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin24896 -> 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 -> 26632 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.beambin2560 -> 2916 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/error_logger.beambin4344 -> 4416 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin3316 -> 4004 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin14244 -> 14740 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin14248 -> 14548 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_server.beambin5328 -> 5440 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 -> 32576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin17728 -> 17740 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_search.beambin3088 -> 3084 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin11784 -> 13532 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin3936 -> 3952 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12500 -> 12828 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin20700 -> 21260 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp.beambin2684 -> 2680 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin6256 -> 6240 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_udp.beambin1732 -> 1728 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin8192 -> 7784 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin26424 -> 26516 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19728 -> 19772 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin10484 -> 10536 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13056 -> 12936 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin14816 -> 14864 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp.beambin2492 -> 2488 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin6540 -> 6524 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin1920 -> 1916 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app119
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.appup27
-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.beambin22720 -> 22832 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5228 -> 5276 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/packages.beambin2244 -> 0 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7696 -> 7888 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 -> 3668 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin12332 -> 11572 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin10308 -> 10308 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin3384 -> 3380 bytes
-rw-r--r--bootstrap/lib/kernel/include/dist.hrl3
-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 -> 18112 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2640 -> 3644 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin13932 -> 13824 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin53456 -> 53560 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin7020 -> 7064 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin28864 -> 28824 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v8.beambin27516 -> 27588 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin50072 -> 50164 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 -> 6848 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin7912 -> 9260 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin3044 -> 3096 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin24188 -> 27456 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2568 -> 2552 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin4896 -> 4880 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin23752 -> 23220 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin21900 -> 21788 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin5016 -> 5176 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin85172 -> 83332 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin71552 -> 69072 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin23344 -> 24776 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_scan.beambin31784 -> 30092 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin15352 -> 15336 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin17056 -> 17300 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin20008 -> 21812 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin6748 -> 8068 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin30592 -> 30752 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin7308 -> 7932 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin12464 -> 12436 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin8256 -> 8284 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 -> 17608 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin15348 -> 15360 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin17260 -> 17264 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin6608 -> 6872 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin8896 -> 9472 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin11960 -> 12408 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 -> 14124 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lib.beambin9068 -> 9652 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29204 -> 29592 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2644 -> 2640 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/math.beambin308 -> 1136 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin20180 -> 20236 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin7160 -> 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 -> 9968 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proplists.beambin4852 -> 4948 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin69376 -> 70172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin71584 -> 73056 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin5900 -> 6184 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/random.beambin1552 -> 1568 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin12348 -> 12928 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin7020 -> 7096 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin30628 -> 29644 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4424 -> 4424 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin41100 -> 40832 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app105
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.appup27
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin4744 -> 4928 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin22940 -> 23084 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin7328 -> 7332 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin11412 -> 11584 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5652 -> 5652 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin26800 -> 26848 bytes
-rw-r--r--configure.in17
-rw-r--r--erts/Makefile.in20
-rw-r--r--erts/aclocal.m427
-rwxr-xr-xerts/autoconf/config.guess541
-rwxr-xr-xerts/autoconf/config.sub367
-rw-r--r--erts/autoconf/vxworks/sed.general2
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_ppc324
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simlinux5
-rw-r--r--erts/autoconf/vxworks/sed.vxworks_simso4
-rw-r--r--erts/configure.in83
-rw-r--r--erts/doc/src/Makefile5
-rw-r--r--erts/doc/src/absform.xml9
-rw-r--r--erts/doc/src/communication.xml89
-rw-r--r--erts/doc/src/driver_entry.xml16
-rw-r--r--erts/doc/src/erl.xml173
-rw-r--r--erts/doc/src/erl_dist_protocol.xml301
-rw-r--r--erts/doc/src/erl_driver.xml253
-rw-r--r--erts/doc/src/erl_ext_dist.xml123
-rw-r--r--erts/doc/src/erl_nif.xml39
-rw-r--r--erts/doc/src/erl_set_memory_block.xml172
-rw-r--r--erts/doc/src/erlang.xml4249
-rw-r--r--erts/doc/src/erts_alloc.xml10
-rw-r--r--erts/doc/src/notes.xml850
-rw-r--r--erts/doc/src/part.xml3
-rw-r--r--erts/doc/src/ref_man.xml3
-rw-r--r--erts/emulator/Makefile.in169
-rw-r--r--erts/emulator/beam/atom.c181
-rw-r--r--erts/emulator/beam/atom.h64
-rw-r--r--erts/emulator/beam/atom.names17
-rw-r--r--erts/emulator/beam/beam_bif_load.c810
-rw-r--r--erts/emulator/beam/beam_bp.c2004
-rw-r--r--erts/emulator/beam/beam_bp.h235
-rw-r--r--erts/emulator/beam/beam_catches.c143
-rw-r--r--erts/emulator/beam/beam_catches.h13
-rw-r--r--erts/emulator/beam/beam_debug.c32
-rw-r--r--erts/emulator/beam/beam_emu.c428
-rw-r--r--erts/emulator/beam/beam_load.c619
-rw-r--r--erts/emulator/beam/beam_load.h51
-rw-r--r--erts/emulator/beam/beam_ranges.c349
-rw-r--r--erts/emulator/beam/bif.c1133
-rw-r--r--erts/emulator/beam/bif.h50
-rw-r--r--erts/emulator/beam/bif.tab322
-rw-r--r--erts/emulator/beam/big.c247
-rw-r--r--erts/emulator/beam/big.h5
-rw-r--r--erts/emulator/beam/binary.c100
-rw-r--r--erts/emulator/beam/break.c180
-rw-r--r--erts/emulator/beam/code_ix.c168
-rw-r--r--erts/emulator/beam/code_ix.h142
-rw-r--r--erts/emulator/beam/copy.c2
-rw-r--r--erts/emulator/beam/dist.c366
-rw-r--r--erts/emulator/beam/dist.h19
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c13
-rw-r--r--erts/emulator/beam/erl_afit_alloc.h7
-rw-r--r--erts/emulator/beam/erl_alloc.c59
-rw-r--r--erts/emulator/beam/erl_alloc.h2
-rw-r--r--erts/emulator/beam/erl_alloc.types53
-rw-r--r--erts/emulator/beam/erl_alloc_util.c936
-rw-r--r--erts/emulator/beam/erl_alloc_util.h55
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c18
-rw-r--r--erts/emulator/beam/erl_async.c20
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c47
-rw-r--r--erts/emulator/beam/erl_bif_binary.c71
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c15
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c207
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c456
-rw-r--r--erts/emulator/beam/erl_bif_op.c7
-rw-r--r--erts/emulator/beam/erl_bif_os.c2
-rw-r--r--erts/emulator/beam/erl_bif_port.c871
-rw-r--r--erts/emulator/beam/erl_bif_re.c32
-rw-r--r--erts/emulator/beam/erl_bif_timer.c22
-rw-r--r--erts/emulator/beam/erl_bif_trace.c821
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c45
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h4
-rw-r--r--erts/emulator/beam/erl_db.c179
-rw-r--r--erts/emulator/beam/erl_db.h12
-rw-r--r--erts/emulator/beam/erl_db_hash.h4
-rw-r--r--erts/emulator/beam/erl_db_tree.c14
-rw-r--r--erts/emulator/beam/erl_db_util.c79
-rw-r--r--erts/emulator/beam/erl_db_util.h10
-rw-r--r--erts/emulator/beam/erl_debug.c28
-rw-r--r--erts/emulator/beam/erl_driver.h62
-rw-r--r--erts/emulator/beam/erl_fun.h2
-rw-r--r--erts/emulator/beam/erl_gc.c87
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c15
-rw-r--r--erts/emulator/beam/erl_init.c319
-rw-r--r--erts/emulator/beam/erl_instrument.c6
-rw-r--r--erts/emulator/beam/erl_lock_check.c27
-rw-r--r--erts/emulator/beam/erl_lock_check.h1
-rw-r--r--erts/emulator/beam/erl_message.c279
-rw-r--r--erts/emulator/beam/erl_message.h29
-rw-r--r--erts/emulator/beam/erl_monitors.c15
-rw-r--r--erts/emulator/beam/erl_monitors.h4
-rw-r--r--erts/emulator/beam/erl_mtrace.c4
-rw-r--r--erts/emulator/beam/erl_nif.c170
-rw-r--r--erts/emulator/beam/erl_nif.h13
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h8
-rw-r--r--erts/emulator/beam/erl_node_container_utils.h69
-rw-r--r--erts/emulator/beam/erl_node_tables.c77
-rw-r--r--erts/emulator/beam/erl_node_tables.h9
-rw-r--r--erts/emulator/beam/erl_port.h959
-rw-r--r--erts/emulator/beam/erl_port_task.c2279
-rw-r--r--erts/emulator/beam/erl_port_task.h169
-rw-r--r--erts/emulator/beam/erl_printf_term.c70
-rw-r--r--erts/emulator/beam/erl_printf_term.h6
-rw-r--r--erts/emulator/beam/erl_process.c4897
-rw-r--r--erts/emulator/beam/erl_process.h717
-rw-r--r--erts/emulator/beam/erl_process_dict.c8
-rw-r--r--erts/emulator/beam/erl_process_dump.c35
-rw-r--r--erts/emulator/beam/erl_process_lock.c547
-rw-r--r--erts/emulator/beam/erl_process_lock.h419
-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_smp.h48
-rw-r--r--erts/emulator/beam/erl_sys_driver.h3
-rw-r--r--erts/emulator/beam/erl_term.c8
-rw-r--r--erts/emulator/beam/erl_term.h38
-rw-r--r--erts/emulator/beam/erl_thr_progress.c292
-rw-r--r--erts/emulator/beam/erl_thr_progress.h73
-rw-r--r--erts/emulator/beam/erl_thr_queue.c163
-rw-r--r--erts/emulator/beam/erl_thr_queue.h15
-rw-r--r--erts/emulator/beam/erl_threads.h413
-rw-r--r--erts/emulator/beam/erl_time.h4
-rw-r--r--erts/emulator/beam/erl_time_sup.c85
-rw-r--r--erts/emulator/beam/erl_trace.c740
-rw-r--r--erts/emulator/beam/erl_trace.h141
-rw-r--r--erts/emulator/beam/erl_unicode.c380
-rw-r--r--erts/emulator/beam/erl_utils.h215
-rw-r--r--erts/emulator/beam/erlang_dtrace.d6
-rw-r--r--erts/emulator/beam/export.c361
-rw-r--r--erts/emulator/beam/export.h50
-rw-r--r--erts/emulator/beam/external.c302
-rw-r--r--erts/emulator/beam/external.h9
-rwxr-xr-xerts/emulator/beam/global.h906
-rw-r--r--erts/emulator/beam/index.c23
-rw-r--r--erts/emulator/beam/index.h16
-rw-r--r--erts/emulator/beam/io.c4619
-rw-r--r--erts/emulator/beam/module.c161
-rw-r--r--erts/emulator/beam/module.h66
-rw-r--r--erts/emulator/beam/ops.tab22
-rw-r--r--erts/emulator/beam/packet_parser.c10
-rw-r--r--erts/emulator/beam/register.c63
-rw-r--r--erts/emulator/beam/register.h19
-rw-r--r--erts/emulator/beam/sys.h244
-rw-r--r--erts/emulator/beam/time.c26
-rw-r--r--erts/emulator/beam/utils.c332
-rw-r--r--erts/emulator/drivers/common/efile_drv.c148
-rw-r--r--erts/emulator/drivers/common/erl_efile.h1
-rw-r--r--erts/emulator/drivers/common/gzio.c10
-rw-r--r--erts/emulator/drivers/common/inet_drv.c474
-rw-r--r--erts/emulator/drivers/common/ram_file_drv.c8
-rw-r--r--erts/emulator/drivers/unix/ttsl_drv.c14
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c669
-rw-r--r--erts/emulator/drivers/win32/registry_drv.c4
-rw-r--r--erts/emulator/drivers/win32/win_efile.c25
-rw-r--r--erts/emulator/hipe/hipe_bif0.c20
-rw-r--r--erts/emulator/hipe/hipe_bif2.c7
-rw-r--r--erts/emulator/hipe/hipe_bif2.tab1
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m42
-rw-r--r--erts/emulator/hipe/hipe_debug.c15
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c97
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c12
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c6
-rw-r--r--erts/emulator/hipe/hipe_native_bif.h3
-rw-r--r--erts/emulator/hipe/hipe_primops.h1
-rw-r--r--erts/emulator/hipe/hipe_stack.c10
-rw-r--r--erts/emulator/hipe/hipe_stack.h4
-rw-r--r--erts/emulator/hipe/hipe_x86.c4
-rw-r--r--erts/emulator/hipe/hipe_x86_gc.h7
-rw-r--r--erts/emulator/hipe/hipe_x86_glue.h3
-rw-r--r--erts/emulator/pcre/pcre.mk8
-rw-r--r--erts/emulator/sys/common/erl_check_io.c68
-rw-r--r--erts/emulator/sys/common/erl_mseg.c1207
-rw-r--r--erts/emulator/sys/common/erl_mseg.h40
-rw-r--r--erts/emulator/sys/common/erl_poll.c55
-rw-r--r--erts/emulator/sys/common/erl_poll.h4
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c205
-rw-r--r--erts/emulator/sys/common/erl_util_queue.h77
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys_ddll.c6
-rw-r--r--erts/emulator/sys/unix/sys.c77
-rw-r--r--erts/emulator/sys/unix/sys_float.c15
-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.c6
-rw-r--r--erts/emulator/sys/win32/erl_win_dyn_driver.h18
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h5
-rwxr-xr-xerts/emulator/sys/win32/sys.c252
-rw-r--r--erts/emulator/sys/win32/sys_float.c15
-rw-r--r--erts/emulator/sys/win32/sys_time.c6
-rw-r--r--erts/emulator/test/Makefile9
-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.c65
-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.erl79
-rw-r--r--erts/emulator/test/bif_SUITE.erl287
-rw-r--r--erts/emulator/test/binary_SUITE.erl26
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl42
-rw-r--r--erts/emulator/test/busy_port_SUITE.erl336
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/Makefile.src5
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/hs_busy_drv.c6
-rw-r--r--erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c190
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl133
-rw-r--r--erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl26
-rw-r--r--erts/emulator/test/code_parallel_load_SUITE.erl198
-rw-r--r--erts/emulator/test/ddll_SUITE.erl4
-rw-r--r--erts/emulator/test/decode_packet_SUITE.erl5
-rw-r--r--erts/emulator/test/distribution_SUITE.erl348
-rw-r--r--erts/emulator/test/driver_SUITE.erl390
-rw-r--r--erts/emulator/test/driver_SUITE_data/Makefile.src4
-rw-r--r--erts/emulator/test/driver_SUITE_data/async_blast_drv.c10
-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/consume_timeslice_drv.c172
-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.c4
-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.c6
-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/efile_SUITE.erl22
-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.erl18
-rw-r--r--erts/emulator/test/estone_SUITE_data/estone_cat.c4
-rw-r--r--erts/emulator/test/fun_SUITE.erl4
-rw-r--r--erts/emulator/test/gc_SUITE.erl61
-rw-r--r--erts/emulator/test/hash_SUITE.erl100
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl18
-rw-r--r--erts/emulator/test/mtx_SUITE.erl10
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src9
-rw-r--r--erts/emulator/test/nif_SUITE.erl109
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c26
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl28
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl518
-rw-r--r--erts/emulator/test/port_SUITE.erl1244
-rw-r--r--erts/emulator/test/port_SUITE_data/dead_port.c26
-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_data/port_test.c37
-rw-r--r--erts/emulator/test/port_bif_SUITE_data/reclaim.h60
-rw-r--r--erts/emulator/test/process_SUITE.erl2079
-rw-r--r--erts/emulator/test/save_calls_SUITE.erl29
-rw-r--r--erts/emulator/test/send_term_SUITE_data/send_term_drv.c8
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl69
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl188
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl7
-rw-r--r--erts/emulator/test/tuple_SUITE.erl307
-rwxr-xr-xerts/emulator/utils/beam_makeops21
-rwxr-xr-xerts/emulator/utils/gen_git_version39
-rwxr-xr-xerts/emulator/utils/make_tables4
-rw-r--r--erts/emulator/zlib/zlib.mk6
-rw-r--r--erts/epmd/src/Makefile.in6
-rw-r--r--erts/epmd/src/epmd.c4
-rw-r--r--erts/epmd/src/epmd_cli.c15
-rw-r--r--erts/epmd/src/epmd_int.h22
-rw-r--r--erts/epmd/src/epmd_srv.c218
-rw-r--r--erts/epmd/test/epmd_SUITE.erl49
-rw-r--r--erts/etc/common/Makefile.in198
-rw-r--r--erts/etc/common/ct_run.c14
-rw-r--r--erts/etc/common/erlc.c3
-rw-r--r--erts/etc/common/erlexec.c30
-rw-r--r--erts/etc/common/escript.c4
-rw-r--r--erts/etc/common/heart.c85
-rw-r--r--erts/etc/common/inet_gethost.c21
-rw-r--r--erts/etc/unix/Install.src5
-rw-r--r--erts/etc/unix/cerl.src30
-rw-r--r--erts/etc/unix/etp-commands916
-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/Install.c16
-rwxr-xr-xerts/etc/win32/nsis/dll_version_helper.sh2
-rw-r--r--erts/include/internal/erl_printf_format.h36
-rw-r--r--erts/include/internal/ethread.h12
-rw-r--r--erts/lib_src/Makefile.in109
-rw-r--r--erts/lib_src/common/erl_misc_utils.c13
-rw-r--r--erts/lib_src/common/erl_printf_format.c88
-rw-r--r--erts/lib_src/common/ethr_mutex.c10
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin55108 -> 54532 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin42400 -> 93468 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin0 -> 3612 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48588 -> 48632 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1448 -> 1444 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin40604 -> 44244 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin69964 -> 70048 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23460 -> 23404 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin12796 -> 12780 bytes
-rw-r--r--erts/preloaded/src/Makefile3
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl53
-rw-r--r--erts/preloaded/src/erlang.erl2608
-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.erl262
-rw-r--r--erts/preloaded/src/prim_inet.erl16
-rw-r--r--erts/start_scripts/Makefile52
-rw-r--r--erts/test/Makefile12
-rw-r--r--erts/test/autoimport_SUITE.erl196
-rw-r--r--erts/test/otp_SUITE.erl25
-rw-r--r--erts/test/system.spec.vxworks2
-rw-r--r--erts/vsn.mk6
-rw-r--r--lib/Makefile13
-rw-r--r--lib/appmon/doc/src/notes.xml27
-rw-r--r--lib/appmon/src/Makefile10
-rw-r--r--lib/appmon/src/appmon.app.src4
-rw-r--r--lib/appmon/vsn.mk2
-rw-r--r--lib/asn1/c_src/Makefile9
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c56
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml519
-rw-r--r--lib/asn1/doc/src/asn1ct.xml128
-rw-r--r--lib/asn1/doc/src/asn1rt.xml41
-rw-r--r--lib/asn1/doc/src/notes.xml108
-rw-r--r--lib/asn1/src/.gitignore2
-rw-r--r--lib/asn1/src/Makefile94
-rw-r--r--lib/asn1/src/asn1.app.src6
-rw-r--r--lib/asn1/src/asn1_records.hrl8
-rw-r--r--lib/asn1/src/asn1ct.erl269
-rw-r--r--lib/asn1/src/asn1ct_check.erl129
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber.erl1596
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl132
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl1095
-rw-r--r--lib/asn1/src/asn1ct_eval_ext.funcs1
-rw-r--r--lib/asn1/src/asn1ct_eval_per.funcs2
-rw-r--r--lib/asn1/src/asn1ct_eval_uper.funcs2
-rw-r--r--lib/asn1/src/asn1ct_func.erl105
-rw-r--r--lib/asn1/src/asn1ct_gen.erl477
-rw-r--r--lib/asn1/src/asn1ct_gen_ber.erl1749
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl462
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl719
-rw-r--r--lib/asn1/src/asn1ct_gen_per_rt2ct.erl702
-rw-r--r--lib/asn1/src/asn1ct_imm.erl766
-rw-r--r--lib/asn1/src/asn1ct_parser2.erl31
-rw-r--r--lib/asn1/src/asn1ct_value.erl67
-rw-r--r--lib/asn1/src/asn1rt_ber_bin.erl2471
-rw-r--r--lib/asn1/src/asn1rt_ber_bin_v2.erl2035
-rw-r--r--lib/asn1/src/asn1rt_check.erl360
-rw-r--r--lib/asn1/src/asn1rt_nif.erl27
-rw-r--r--lib/asn1/src/asn1rt_per_bin.erl2285
-rw-r--r--lib/asn1/src/asn1rt_per_bin_rt2ct.erl1748
-rw-r--r--lib/asn1/src/asn1rt_uper_bin.erl1618
-rw-r--r--lib/asn1/src/asn1rtt_ber.erl1561
-rw-r--r--lib/asn1/src/asn1rtt_check.erl276
-rw-r--r--lib/asn1/src/asn1rtt_ext.erl72
-rw-r--r--lib/asn1/src/asn1rtt_per.erl979
-rw-r--r--lib/asn1/src/asn1rtt_per_common.erl126
-rw-r--r--lib/asn1/src/asn1rtt_real_common.erl292
-rw-r--r--lib/asn1/src/asn1rtt_uper.erl1045
-rw-r--r--lib/asn1/src/prepare_templates.erl135
-rw-r--r--lib/asn1/test/Makefile5
-rw-r--r--lib/asn1/test/asn1_SUITE.erl597
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn142
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Def.py31
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/MultipleLevels.asn19
-rw-r--r--lib/asn1/test/asn1_SUITE_data/NullTest.asn114
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Opt.py31
-rw-r--r--lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Prim.asn119
-rw-r--r--lib/asn1/test/asn1_SUITE_data/PrimStrings.asn129
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SeqExtension.asn128
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SeqOf.py45
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SetOf.py42
-rw-r--r--lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl4
-rw-r--r--lib/asn1/test/asn1_SUITE_data/testobj.erl55
-rw-r--r--lib/asn1/test/asn1_app_test.erl4
-rw-r--r--lib/asn1/test/asn1_test_lib.erl25
-rw-r--r--lib/asn1/test/asn1_wrapper.erl37
-rw-r--r--lib/asn1/test/ber_decode_error.erl45
-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.erl191
-rw-r--r--lib/asn1/test/testContextSwitchingTypes.erl54
-rw-r--r--lib/asn1/test/testDeepTConstr.erl26
-rw-r--r--lib/asn1/test/testDoubleEllipses.erl10
-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/testNBAPsystem.erl5
-rw-r--r--lib/asn1/test/testParamBasic.erl10
-rw-r--r--lib/asn1/test/testParameterizedInfObj.erl4
-rw-r--r--lib/asn1/test/testPrim.erl8
-rw-r--r--lib/asn1/test/testPrimStrings.erl642
-rw-r--r--lib/asn1/test/testSSLspecs.erl31
-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/testSeqSetDefaultVal.erl6
-rw-r--r--lib/asn1/test/testSetIndefinite.erl5
-rw-r--r--lib/asn1/test/testSetOptional.erl212
-rw-r--r--lib/asn1/test/testTCAP.erl6
-rw-r--r--lib/asn1/test/testTimer.erl53
-rw-r--r--lib/asn1/test/testTypeValueNotation.erl35
-rw-r--r--lib/asn1/test/testWSParamClass.erl17
-rw-r--r--lib/asn1/test/testX420.erl4
-rw-r--r--lib/asn1/test/test_compile_options.erl13
-rw-r--r--lib/asn1/test/test_inline.erl270
-rw-r--r--lib/asn1/test/test_modified_x420.erl2
-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/common_test_app.xml11
-rw-r--r--lib/common_test/doc/src/cover_chapter.xml112
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml27
-rw-r--r--lib/common_test/doc/src/ct_run.xml5
-rw-r--r--lib/common_test/doc/src/notes.xml291
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml816
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml52
-rw-r--r--lib/common_test/priv/Makefile.in6
-rwxr-xr-xlib/common_test/priv/auxdir/config.guess541
-rwxr-xr-xlib/common_test/priv/auxdir/config.sub367
-rw-r--r--lib/common_test/src/Makefile8
-rw-r--r--lib/common_test/src/ct.erl13
-rw-r--r--lib/common_test/src/ct_config.erl13
-rw-r--r--lib/common_test/src/ct_config_plain.erl4
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl33
-rw-r--r--lib/common_test/src/ct_cover.erl32
-rw-r--r--lib/common_test/src/ct_event.erl4
-rw-r--r--lib/common_test/src/ct_framework.erl33
-rw-r--r--lib/common_test/src/ct_gen_conn.erl4
-rw-r--r--lib/common_test/src/ct_groups.erl4
-rw-r--r--lib/common_test/src/ct_hooks.erl6
-rw-r--r--lib/common_test/src/ct_logs.erl154
-rw-r--r--lib/common_test/src/ct_make.erl8
-rw-r--r--lib/common_test/src/ct_master.erl192
-rw-r--r--lib/common_test/src/ct_master_event.erl28
-rw-r--r--lib/common_test/src/ct_master_logs.erl63
-rw-r--r--lib/common_test/src/ct_master_status.erl4
-rw-r--r--lib/common_test/src/ct_netconfc.erl48
-rw-r--r--lib/common_test/src/ct_run.erl658
-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_telnet.erl32
-rw-r--r--lib/common_test/src/ct_telnet_client.erl4
-rw-r--r--lib/common_test/src/ct_testspec.erl358
-rw-r--r--lib/common_test/src/ct_util.erl33
-rw-r--r--lib/common_test/src/ct_util.hrl4
-rw-r--r--lib/common_test/src/cth_conn_log.erl9
-rw-r--r--lib/common_test/src/cth_log_redirect.erl2
-rw-r--r--lib/common_test/src/cth_surefire.erl4
-rw-r--r--lib/common_test/src/unix_telnet.erl4
-rw-r--r--lib/common_test/test/Makefile9
-rw-r--r--lib/common_test/test/common_test.cover10
-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/.gitignore (renamed from lib/inviso/doc/html/.gitignore)0
-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.erl511
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl175
-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_error_SUITE_data/error/test/verify_config.erl239
-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_hooks_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl76
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl11
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE.erl5
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl7
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/ns.erl21
-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.erl77
-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.erl111
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl108
-rw-r--r--lib/common_test/test/ct_testspec_2_SUITE.erl43
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE.erl745
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg111
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg121
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg131
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/config2/cfg211
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/flat_spec14
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join12
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join_sep15
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep12
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep_join12
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both12
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both_join16
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_join11
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_sep13
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/flat_spec25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join_sep25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep_join25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both24
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both_join29
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_join25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_sep25
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t11_SUITE.erl175
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t12_SUITE.erl175
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t21_SUITE.erl174
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t22_SUITE.erl177
-rw-r--r--lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t23_SUITE.erl158
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE.erl35
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl171
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/compile.xml26
-rw-r--r--lib/compiler/doc/src/notes.xml100
-rw-r--r--lib/compiler/src/Makefile17
-rw-r--r--lib/compiler/src/beam_a.erl97
-rw-r--r--lib/compiler/src/beam_asm.erl4
-rw-r--r--lib/compiler/src/beam_block.erl79
-rw-r--r--lib/compiler/src/beam_bool.erl43
-rw-r--r--lib/compiler/src/beam_bsm.erl159
-rw-r--r--lib/compiler/src/beam_clean.erl47
-rw-r--r--lib/compiler/src/beam_dead.erl18
-rw-r--r--lib/compiler/src/beam_dict.erl2
-rw-r--r--lib/compiler/src/beam_disasm.erl9
-rw-r--r--lib/compiler/src/beam_except.erl28
-rw-r--r--lib/compiler/src/beam_flatten.erl55
-rw-r--r--lib/compiler/src/beam_jump.erl129
-rw-r--r--lib/compiler/src/beam_peep.erl10
-rw-r--r--lib/compiler/src/beam_receive.erl58
-rw-r--r--lib/compiler/src/beam_trim.erl70
-rw-r--r--lib/compiler/src/beam_type.erl25
-rw-r--r--lib/compiler/src/beam_utils.erl284
-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.erl107
-rw-r--r--lib/compiler/src/compiler.app.src5
-rw-r--r--lib/compiler/src/core_lint.erl7
-rw-r--r--lib/compiler/src/core_scan.erl27
-rw-r--r--lib/compiler/src/erl_bifs.erl6
-rw-r--r--lib/compiler/src/sys_core_fold.erl160
-rw-r--r--lib/compiler/src/sys_expand_pmod.erl433
-rw-r--r--lib/compiler/src/sys_pre_expand.erl150
-rw-r--r--lib/compiler/src/v3_codegen.erl100
-rw-r--r--lib/compiler/src/v3_kernel.erl55
-rw-r--r--lib/compiler/test/Makefile6
-rw-r--r--lib/compiler/test/andor_SUITE.erl9
-rw-r--r--lib/compiler/test/beam_except_SUITE.erl (renamed from lib/compiler/test/beam_expect_SUITE.erl)4
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl21
-rw-r--r--lib/compiler/test/bs_bit_binaries_SUITE.erl14
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl34
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl125
-rw-r--r--lib/compiler/test/compilation_SUITE.erl62
-rw-r--r--lib/compiler/test/compile_SUITE.erl30
-rw-r--r--lib/compiler/test/core_SUITE.erl8
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl58
-rw-r--r--lib/compiler/test/error_SUITE.erl43
-rw-r--r--lib/compiler/test/guard_SUITE.erl21
-rw-r--r--lib/compiler/test/inline_SUITE.erl76
-rw-r--r--lib/compiler/test/match_SUITE.erl10
-rw-r--r--lib/compiler/test/misc_SUITE.erl22
-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.erl8
-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.erl12
-rw-r--r--lib/compiler/test/test_lib.erl17
-rw-r--r--lib/compiler/test/trycatch_SUITE.erl12
-rw-r--r--lib/compiler/test/unused_multiple_values_error.core11
-rw-r--r--lib/compiler/test/warnings_SUITE.erl16
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/doc/src/notes.xml19
-rw-r--r--lib/cosEvent/src/Makefile16
-rw-r--r--lib/cosEvent/vsn.mk2
-rw-r--r--lib/cosEventDomain/doc/src/notes.xml19
-rw-r--r--lib/cosEventDomain/src/Makefile10
-rw-r--r--lib/cosEventDomain/vsn.mk2
-rw-r--r--lib/cosFileTransfer/doc/src/notes.xml19
-rw-r--r--lib/cosFileTransfer/src/Makefile10
-rw-r--r--lib/cosFileTransfer/test/fileTransfer_SUITE.erl45
-rw-r--r--lib/cosFileTransfer/vsn.mk2
-rw-r--r--lib/cosNotification/doc/src/notes.xml19
-rw-r--r--lib/cosNotification/src/Makefile24
-rw-r--r--lib/cosNotification/vsn.mk2
-rw-r--r--lib/cosProperty/doc/src/notes.xml19
-rw-r--r--lib/cosProperty/src/Makefile10
-rw-r--r--lib/cosProperty/vsn.mk2
-rw-r--r--lib/cosTime/doc/src/notes.xml19
-rw-r--r--lib/cosTime/src/Makefile18
-rw-r--r--lib/cosTime/vsn.mk2
-rw-r--r--lib/cosTransactions/doc/src/notes.xml19
-rw-r--r--lib/cosTransactions/src/Makefile10
-rw-r--r--lib/cosTransactions/vsn.mk2
-rw-r--r--lib/crypto/c_src/Makefile.in55
-rw-r--r--lib/crypto/c_src/crypto.c326
-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.xml39
-rw-r--r--lib/crypto/doc/src/crypto_app.xml4
-rw-r--r--lib/crypto/doc/src/notes.xml32
-rw-r--r--lib/crypto/src/Makefile8
-rw-r--r--lib/crypto/src/crypto.erl136
-rw-r--r--lib/crypto/test/crypto_SUITE.erl356
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/notes.xml63
-rw-r--r--lib/debugger/src/Makefile8
-rw-r--r--lib/debugger/src/dbg_debugged.erl5
-rw-r--r--lib/debugger/src/dbg_ieval.erl14
-rw-r--r--lib/debugger/src/dbg_ui_trace.erl14
-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.erl50
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl18
-rw-r--r--lib/debugger/src/dbg_wx_trace_win.erl21
-rw-r--r--lib/debugger/src/dbg_wx_win.erl10
-rw-r--r--lib/debugger/src/i.erl4
-rw-r--r--lib/debugger/src/int.erl93
-rw-r--r--lib/debugger/test/erl_eval_SUITE.erl36
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl8
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml17
-rw-r--r--lib/dialyzer/doc/src/dialyzer_chapter.xml6
-rw-r--r--lib/dialyzer/doc/src/notes.xml86
-rw-r--r--lib/dialyzer/src/Makefile12
-rw-r--r--lib/dialyzer/src/dialyzer.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl37
-rw-r--r--lib/dialyzer/src/dialyzer_callgraph.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl27
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl9
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl27
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl18
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl11
-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/inets18
-rw-r--r--lib/dialyzer/test/r9c_SUITE_data/results/mnesia7
-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/extract_translations4
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify1
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/comm_layer2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes16
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/empty_list_infimum2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/flatten2
-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/results/types_arity2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_acceptor.erl119
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl206
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.erl83
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.hrl29
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_logger.erl143
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl240
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port_sup.erl88
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/empty_list_infimum.erl57
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_api.erl99
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_publish.erl49
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/record_creation_diffs.erl11
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/types_arity.erl20
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/unknown_arity_function_spec.erl10
-rw-r--r--lib/dialyzer/test/user_SUITE_data/results/wsp_pdu2
-rw-r--r--lib/dialyzer/vsn.mk2
-rwxr-xr-xlib/diameter/bin/diameterc8
-rw-r--r--lib/diameter/doc/src/Makefile47
-rw-r--r--lib/diameter/doc/src/diameter.xml173
-rw-r--r--lib/diameter/doc/src/diameter_app.xml48
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml16
-rw-r--r--lib/diameter/doc/src/diameter_sctp.xml3
-rw-r--r--lib/diameter/doc/src/diameter_soc.xml6
-rw-r--r--lib/diameter/doc/src/diameter_soc_rfc6733.xml8692
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml24
-rw-r--r--lib/diameter/doc/src/files.mk9
-rw-r--r--lib/diameter/doc/src/notes.gifbin2005 -> 0 bytes
-rw-r--r--lib/diameter/doc/src/notes.xml205
-rw-r--r--lib/diameter/doc/src/seealso.ent6
-rw-r--r--lib/diameter/include/diameter.hrl12
-rw-r--r--lib/diameter/include/diameter_gen.hrl6
-rw-r--r--lib/diameter/src/Makefile38
-rw-r--r--lib/diameter/src/base/diameter.app.src4
-rw-r--r--lib/diameter/src/base/diameter.appup.src25
-rw-r--r--lib/diameter/src/base/diameter.erl11
-rw-r--r--lib/diameter/src/base/diameter_capx.erl188
-rw-r--r--lib/diameter/src/base/diameter_codec.erl63
-rw-r--r--lib/diameter/src/base/diameter_config.erl83
-rw-r--r--lib/diameter/src/base/diameter_internal.hrl4
-rw-r--r--lib/diameter/src/base/diameter_peer.erl24
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl357
-rw-r--r--lib/diameter/src/base/diameter_reg.erl11
-rw-r--r--lib/diameter/src/base/diameter_service.erl2732
-rw-r--r--lib/diameter/src/base/diameter_stats.erl83
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl1705
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl413
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl6
-rw-r--r--lib/diameter/src/compiler/diameter_exprecs.erl62
-rw-r--r--lib/diameter/src/compiler/diameter_forms.hrl3
-rw-r--r--lib/diameter/src/compiler/diameter_nowarn.erl41
-rw-r--r--lib/diameter/src/dict/acct_rfc6733.dia72
-rw-r--r--lib/diameter/src/dict/base_rfc6733.dia415
-rw-r--r--lib/diameter/src/dict/capup_rfc6737.dia49
-rw-r--r--lib/diameter/src/modules.mk19
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl15
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl125
-rw-r--r--lib/diameter/test/.gitignore1
-rw-r--r--lib/diameter/test/Makefile38
-rw-r--r--lib/diameter/test/coverspec.sed33
-rw-r--r--lib/diameter/test/depend.sed4
-rw-r--r--lib/diameter/test/diameter.cover12
-rw-r--r--lib/diameter/test/diameter_3xxx_SUITE.erl509
-rw-r--r--lib/diameter/test/diameter_app_SUITE.erl3
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl157
-rw-r--r--lib/diameter/test/diameter_codec_test.erl8
-rw-r--r--lib/diameter/test/diameter_ct.erl19
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl180
-rw-r--r--lib/diameter/test/diameter_failover_SUITE.erl143
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_length_SUITE.erl289
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_stats_SUITE.erl23
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl580
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl467
-rw-r--r--lib/diameter/test/modules.mk29
-rw-r--r--lib/diameter/test/testspec3
-rw-r--r--lib/diameter/vsn.mk7
-rw-r--r--lib/edoc/doc/src/notes.xml65
-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.erl90
-rw-r--r--lib/edoc/src/edoc.hrl6
-rw-r--r--lib/edoc/src/edoc_data.erl3
-rw-r--r--lib/edoc/src/edoc_doclet.erl34
-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.erl98
-rw-r--r--lib/edoc/src/edoc_macros.erl6
-rw-r--r--lib/edoc/src/edoc_parser.yrl14
-rw-r--r--lib/edoc/src/edoc_refs.erl19
-rw-r--r--lib/edoc/src/edoc_report.erl8
-rw-r--r--lib/edoc/src/edoc_run.erl6
-rw-r--r--lib/edoc/src/edoc_tags.erl4
-rw-r--r--lib/edoc/src/edoc_wiki.erl5
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/eldap.xml4
-rw-r--r--lib/eldap/doc/src/notes.xml25
-rw-r--r--lib/eldap/src/Makefile10
-rw-r--r--lib/eldap/src/eldap.erl33
-rw-r--r--lib/eldap/vsn.mk2
-rw-r--r--lib/erl_docgen/doc/src/doc-build.xml5
-rw-r--r--lib/erl_docgen/doc/src/notes.xml44
-rwxr-xr-xlib/erl_docgen/priv/bin/xml_from_edoc.escript8
-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/fop.xconf45
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl14
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf_params.xsl30
-rw-r--r--lib/erl_docgen/src/Makefile4
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl37
-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.m427
-rw-r--r--lib/erl_interface/configure.in4
-rw-r--r--lib/erl_interface/doc/src/ei.xml71
-rw-r--r--lib/erl_interface/doc/src/erl_eterm.xml20
-rw-r--r--lib/erl_interface/doc/src/notes.xml63
-rw-r--r--lib/erl_interface/include/ei.h38
-rw-r--r--lib/erl_interface/include/erl_interface.h42
-rw-r--r--lib/erl_interface/src/Makefile7
-rw-r--r--lib/erl_interface/src/Makefile.in145
-rwxr-xr-xlib/erl_interface/src/auxdir/config.guess541
-rwxr-xr-xlib/erl_interface/src/auxdir/config.sub367
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c8
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h4
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c10
-rw-r--r--lib/erl_interface/src/connect/eirecv.c14
-rw-r--r--lib/erl_interface/src/decode/decode_atom.c161
-rw-r--r--lib/erl_interface/src/decode/decode_boolean.c38
-rw-r--r--lib/erl_interface/src/decode/decode_fun.c39
-rw-r--r--lib/erl_interface/src/decode/decode_pid.c24
-rw-r--r--lib/erl_interface/src/decode/decode_port.c23
-rw-r--r--lib/erl_interface/src/decode/decode_ref.c47
-rw-r--r--lib/erl_interface/src/decode/decode_skip.c5
-rw-r--r--lib/erl_interface/src/decode/decode_trace.c30
-rw-r--r--lib/erl_interface/src/encode/encode_atom.c175
-rw-r--r--lib/erl_interface/src/encode/encode_fun.c6
-rw-r--r--lib/erl_interface/src/encode/encode_pid.c27
-rw-r--r--lib/erl_interface/src/encode/encode_port.c25
-rw-r--r--lib/erl_interface/src/encode/encode_ref.c26
-rw-r--r--lib/erl_interface/src/legacy/erl_connect.c18
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c160
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.h6
-rw-r--r--lib/erl_interface/src/legacy/erl_format.c22
-rw-r--r--lib/erl_interface/src/legacy/erl_malloc.c22
-rw-r--r--lib/erl_interface/src/legacy/erl_marshal.c415
-rw-r--r--lib/erl_interface/src/legacy/global_whereis.c13
-rw-r--r--lib/erl_interface/src/misc/ei_decode_term.c42
-rw-r--r--lib/erl_interface/src/misc/ei_format.c6
-rw-r--r--lib/erl_interface/src/misc/ei_printterm.c9
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c72
-rw-r--r--lib/erl_interface/src/misc/get_type.c81
-rw-r--r--lib/erl_interface/src/misc/putget.h9
-rw-r--r--lib/erl_interface/src/misc/show_msg.c16
-rw-r--r--lib/erl_interface/src/prog/ei_fake_prog.c8
-rw-r--r--lib/erl_interface/test/all_SUITE_data/init_tc.erl4
-rw-r--r--lib/erl_interface/test/all_SUITE_data/runner.c3
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE.erl87
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c158
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE.erl123
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c427
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE.erl52
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c246
-rw-r--r--lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c3
-rw-r--r--lib/erl_interface/test/ei_print_SUITE.erl4
-rw-r--r--lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c5
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c3
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/et/doc/src/notes.xml19
-rw-r--r--lib/et/src/Makefile4
-rw-r--r--lib/et/src/et.erl4
-rw-r--r--lib/et/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml19
-rw-r--r--lib/eunit/include/eunit.hrl157
-rw-r--r--lib/eunit/src/Makefile4
-rw-r--r--lib/eunit/src/eunit.erl5
-rw-r--r--lib/eunit/src/eunit_autoexport.erl7
-rw-r--r--lib/eunit/src/eunit_lib.erl5
-rw-r--r--lib/eunit/src/eunit_surefire.erl5
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/contribs/bonk/Makefile8
-rw-r--r--lib/gs/contribs/cols/Makefile12
-rw-r--r--lib/gs/contribs/mandel/Makefile12
-rw-r--r--lib/gs/contribs/mandel/mandel.erl3
-rw-r--r--lib/gs/contribs/othello/Makefile8
-rw-r--r--lib/gs/doc/src/notes.xml21
-rw-r--r--lib/gs/src/Makefile6
-rw-r--r--lib/gs/src/gstk_editor.erl6
-rw-r--r--lib/gs/vsn.mk2
-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.erl2640
-rw-r--r--lib/hipe/cerl/erl_types.erl90
-rw-r--r--lib/hipe/doc/src/notes.xml72
-rw-r--r--lib/hipe/flow/hipe_dominators.erl6
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl32
-rw-r--r--lib/hipe/icode/hipe_icode_mulret.erl14
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl8
-rw-r--r--lib/hipe/icode/hipe_icode_range.erl2
-rw-r--r--lib/hipe/main/Makefile8
-rw-r--r--lib/hipe/main/hipe.erl14
-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.erl8
-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/ssa/hipe_ssa.inc6
-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.in10
-rw-r--r--lib/ic/c_src/ic.c2
-rw-r--r--lib/ic/doc/src/notes.xml45
-rw-r--r--lib/ic/examples/all-against-all/client.c2
-rw-r--r--lib/ic/examples/c-client/client.c2
-rw-r--r--lib/ic/examples/c-server/client.c2
-rw-r--r--lib/ic/examples/pre_post_condition/Makefile4
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/Any.java4
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/Environment.java6
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/Makefile5
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/Term.java7
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/TermHelper.java4
-rw-r--r--lib/ic/java_src/com/ericsson/otp/ic/TypeCode.java9
-rw-r--r--lib/ic/src/Makefile4
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/c_client.c2
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c2
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c2
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c2
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c2
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src2
-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.xml35
-rw-r--r--lib/inets/doc/src/httpd.xml74
-rw-r--r--lib/inets/doc/src/notes.xml73
-rw-r--r--lib/inets/src/ftp/ftp.erl4
-rw-r--r--lib/inets/src/ftp/ftp_response.erl5
-rw-r--r--lib/inets/src/http_client/httpc.erl12
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl1164
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl2
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl23
-rw-r--r--lib/inets/src/http_client/httpc_response.erl7
-rw-r--r--lib/inets/src/http_lib/http_transport.erl99
-rw-r--r--lib/inets/src/http_server/httpd_acceptor.erl38
-rw-r--r--lib/inets/src/http_server/httpd_acceptor_sup.erl20
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl20
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl8
-rw-r--r--lib/inets/src/http_server/httpd_response.erl6
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl112
-rw-r--r--lib/inets/src/inets_app/Makefile4
-rw-r--r--lib/inets/src/inets_app/inets.appup.src111
-rw-r--r--lib/inets/src/inets_app/inets.erl9
-rw-r--r--lib/inets/test/Makefile9
-rw-r--r--lib/inets/test/erl_make_certs.erl429
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl4
-rw-r--r--lib/inets/test/ftp_suite_lib.erl3
-rw-r--r--lib/inets/test/httpc_SUITE.erl4869
-rw-r--r--lib/inets/test/httpc_cookie_SUITE.erl5
-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.erl400
-rw-r--r--lib/inets/test/httpd_mod.erl27
-rw-r--r--lib/inets/test/httpd_test_lib.erl19
-rw-r--r--lib/inets/test/inets.spec.vxworks5
-rw-r--r--lib/inets/test/inets_SUITE.erl9
-rw-r--r--lib/inets/test/inets_appup_test.erl17
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl8
-rw-r--r--lib/inets/test/inets_test_lib.erl108
-rw-r--r--lib/inets/test/rules.mk7
-rw-r--r--lib/inets/test/uri_SUITE.erl159
-rw-r--r--lib/inets/vsn.mk4
-rw-r--r--lib/inviso/AUTHORS4
-rw-r--r--lib/inviso/Makefile37
-rw-r--r--lib/inviso/doc/pdf/.gitignore0
-rw-r--r--lib/inviso/doc/src/Makefile122
-rw-r--r--lib/inviso/doc/src/book.xml48
-rw-r--r--lib/inviso/doc/src/fascicules.xml18
-rw-r--r--lib/inviso/doc/src/inviso.xml693
-rw-r--r--lib/inviso/doc/src/inviso_as_lib.xml94
-rw-r--r--lib/inviso/doc/src/inviso_chapter.xml362
-rw-r--r--lib/inviso/doc/src/inviso_lfm.xml93
-rw-r--r--lib/inviso/doc/src/inviso_lfm_tpfreader.xml83
-rw-r--r--lib/inviso/doc/src/inviso_rt.xml246
-rw-r--r--lib/inviso/doc/src/inviso_rt_meta.xml78
-rw-r--r--lib/inviso/doc/src/inviso_users_guide_pic1.gifbin13514 -> 0 bytes
-rw-r--r--lib/inviso/doc/src/inviso_users_guide_pic1.ps24489
-rw-r--r--lib/inviso/doc/src/notes.xml278
-rw-r--r--lib/inviso/doc/src/ref_man.xml40
-rw-r--r--lib/inviso/ebin/.gitignore0
-rw-r--r--lib/inviso/include/.gitignore0
-rw-r--r--lib/inviso/info2
-rw-r--r--lib/inviso/priv0
-rw-r--r--lib/inviso/src/Makefile104
-rw-r--r--lib/inviso/src/inviso.app.src13
-rw-r--r--lib/inviso/src/inviso.appup.src1
-rw-r--r--lib/inviso/src/inviso.erl1056
-rw-r--r--lib/inviso/src/inviso_c.erl1335
-rw-r--r--lib/inviso/src/inviso_lfm.erl431
-rw-r--r--lib/inviso/src/inviso_lfm_tpfreader.erl388
-rw-r--r--lib/inviso/src/inviso_tool.erl3255
-rw-r--r--lib/inviso/src/inviso_tool_lib.erl379
-rw-r--r--lib/inviso/src/inviso_tool_sh.erl1749
-rw-r--r--lib/inviso/test/Makefile61
-rw-r--r--lib/inviso/test/inviso.cover2
-rw-r--r--lib/inviso/test/inviso.spec1
-rw-r--r--lib/inviso/test/inviso_tool_SUITE.erl1166
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc12
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc17
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc12
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc16
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc9
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc9
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc11
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc11
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt8
-rw-r--r--lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc10
-rw-r--r--lib/inviso/vsn.mk1
-rw-r--r--lib/jinterface/doc/src/notes.xml85
-rw-r--r--lib/jinterface/java_src/Makefile4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java22
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile19
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java78
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java200
-rw-r--r--lib/jinterface/test/nc_SUITE.erl181
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/Makefile1
-rw-r--r--lib/kernel/doc/src/application.xml11
-rw-r--r--lib/kernel/doc/src/code.xml26
-rw-r--r--lib/kernel/doc/src/erl_ddll.xml159
-rw-r--r--lib/kernel/doc/src/error_handler.xml53
-rw-r--r--lib/kernel/doc/src/error_logger.xml5
-rw-r--r--lib/kernel/doc/src/file.xml189
-rw-r--r--lib/kernel/doc/src/global.xml14
-rw-r--r--lib/kernel/doc/src/inet.xml207
-rw-r--r--lib/kernel/doc/src/notes.xml231
-rw-r--r--lib/kernel/doc/src/os.xml49
-rw-r--r--lib/kernel/doc/src/packages.xml208
-rw-r--r--lib/kernel/doc/src/ref_man.xml3
-rw-r--r--lib/kernel/doc/src/specs.xml1
-rw-r--r--lib/kernel/include/dist.hrl3
-rw-r--r--lib/kernel/include/file.hrl59
-rw-r--r--lib/kernel/internal_doc/distribution_handshake.txt216
-rw-r--r--lib/kernel/src/Makefile15
-rw-r--r--lib/kernel/src/application.erl21
-rw-r--r--lib/kernel/src/application_controller.erl88
-rw-r--r--lib/kernel/src/auth.erl6
-rw-r--r--lib/kernel/src/code.erl91
-rw-r--r--lib/kernel/src/code_server.erl18
-rw-r--r--lib/kernel/src/disk_log.erl50
-rw-r--r--lib/kernel/src/disk_log_1.erl10
-rw-r--r--lib/kernel/src/dist_util.erl8
-rw-r--r--lib/kernel/src/erl_ddll.erl92
-rw-r--r--lib/kernel/src/error_handler.erl57
-rw-r--r--lib/kernel/src/error_logger.erl14
-rw-r--r--lib/kernel/src/erts_debug.erl129
-rw-r--r--lib/kernel/src/file.erl203
-rw-r--r--lib/kernel/src/file_io_server.erl22
-rw-r--r--lib/kernel/src/file_server.erl6
-rw-r--r--lib/kernel/src/gen_sctp.erl2
-rw-r--r--lib/kernel/src/gen_tcp.erl14
-rw-r--r--lib/kernel/src/gen_udp.erl6
-rw-r--r--lib/kernel/src/global.erl12
-rw-r--r--lib/kernel/src/group.erl155
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl80
-rw-r--r--lib/kernel/src/inet.erl76
-rw-r--r--lib/kernel/src/inet6_tcp_dist.erl10
-rw-r--r--lib/kernel/src/inet_config.erl96
-rw-r--r--lib/kernel/src/inet_int.hrl12
-rw-r--r--lib/kernel/src/inet_parse.erl31
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl10
-rw-r--r--lib/kernel/src/kernel.app.src3
-rw-r--r--lib/kernel/src/kernel.appup.src12
-rw-r--r--lib/kernel/src/kernel_config.erl4
-rw-r--r--lib/kernel/src/net_kernel.erl21
-rw-r--r--lib/kernel/src/os.erl81
-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.erl2
-rw-r--r--lib/kernel/src/user.erl342
-rw-r--r--lib/kernel/src/user_drv.erl4
-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.erl15
-rw-r--r--lib/kernel/test/code_SUITE.erl110
-rw-r--r--lib/kernel/test/code_SUITE_data/other.erl38
-rw-r--r--lib/kernel/test/code_SUITE_data/upgrade_client.erl259
-rw-r--r--lib/kernel/test/code_SUITE_data/upgradee.erl123
-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.erl84
-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.erl885
-rw-r--r--lib/kernel/test/file_name_SUITE.erl214
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl50
-rw-r--r--lib/kernel/test/gen_tcp_echo_SUITE.erl27
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl212
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl5
-rw-r--r--lib/kernel/test/global_SUITE.erl37
-rw-r--r--lib/kernel/test/heart_SUITE.erl44
-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.erl42
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE.erl157
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE_data/sockopt_helper.c11
-rw-r--r--lib/kernel/test/init_SUITE.erl151
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl2
-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.erl4
-rw-r--r--lib/kernel/test/pdict_SUITE.erl3
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl338
-rw-r--r--lib/kernel/test/ram_file_SUITE.erl17
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/aclocal.m41905
-rw-r--r--lib/megaco/configure.in9
-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.xml25
-rw-r--r--lib/megaco/doc/src/notes.xml21
-rw-r--r--lib/megaco/examples/meas/Makefile.in8
-rw-r--r--lib/megaco/examples/meas/megaco_codec_meas.erl16
-rw-r--r--lib/megaco/examples/meas/megaco_codec_mstone_lib.erl38
-rw-r--r--lib/megaco/examples/meas/megaco_codec_transform.erl6
-rw-r--r--lib/megaco/src/app/Makefile4
-rw-r--r--lib/megaco/src/app/megaco.app.src28
-rw-r--r--lib/megaco/src/binary/Makefile39
-rw-r--r--lib/megaco/src/binary/depend.mk331
-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.erl287
-rw-r--r--lib/megaco/src/binary/megaco_binary_encoder_lib.erl17
-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.mk52
-rw-r--r--lib/megaco/src/flex/Makefile.in55
-rw-r--r--lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src12
-rw-r--r--lib/megaco/src/rules.mk6
-rw-r--r--lib/megaco/test/megaco.spec.vxworks5
-rw-r--r--lib/megaco/test/megaco_actions_test.erl38
-rw-r--r--lib/megaco/test/megaco_call_flow_test.erl24
-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.erl8
-rw-r--r--lib/megaco/test/megaco_test_mg.erl4
-rw-r--r--lib/megaco/test/megaco_test_mgc.erl4
-rw-r--r--lib/megaco/vsn.mk4
-rw-r--r--lib/mnesia/doc/src/notes.xml60
-rw-r--r--lib/mnesia/examples/mnesia_tpcb.erl56
-rw-r--r--lib/mnesia/include/mnemosyne.hrl18
-rw-r--r--lib/mnesia/src/Makefile11
-rw-r--r--lib/mnesia/src/mnesia.erl4
-rw-r--r--lib/mnesia/src/mnesia_controller.erl17
-rw-r--r--lib/mnesia/src/mnesia_event.erl4
-rw-r--r--lib/mnesia/src/mnesia_locker.erl6
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl6
-rw-r--r--lib/mnesia/src/mnesia_recover.erl68
-rw-r--r--lib/mnesia/src/mnesia_sup.erl15
-rw-r--r--lib/mnesia/src/mnesia_tm.erl5
-rw-r--r--lib/mnesia/test/Makefile3
-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.erl3
-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.erl44
-rw-r--r--lib/mnesia/test/mnesia_measure_test.erl5
-rw-r--r--lib/mnesia/test/mnesia_recovery_test.erl92
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl18
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml19
-rw-r--r--lib/observer/src/Makefile9
-rw-r--r--lib/observer/src/observer_app_wx.erl7
-rw-r--r--lib/observer/src/observer_perf_wx.erl14
-rw-r--r--lib/observer/src/observer_pro_wx.erl2
-rw-r--r--lib/observer/src/observer_tv_table.erl2
-rw-r--r--lib/observer/src/observer_wx.erl100
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl66
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/aclocal.m427
-rw-r--r--lib/odbc/c_src/Makefile.in6
-rw-r--r--lib/odbc/c_src/odbcserver.c180
-rw-r--r--lib/odbc/c_src/odbcserver.h6
-rw-r--r--lib/odbc/configure.in7
-rw-r--r--lib/odbc/doc/src/notes.xml72
-rw-r--r--lib/odbc/doc/src/odbc.xml37
-rw-r--r--lib/odbc/src/Makefile4
-rw-r--r--lib/odbc/src/odbc.erl11
-rw-r--r--lib/odbc/test/odbc_connect_SUITE.erl82
-rw-r--r--lib/odbc/test/odbc_data_type_SUITE.erl7
-rw-r--r--lib/odbc/test/odbc_query_SUITE.erl69
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/COSS/CosNaming/Makefile8
-rw-r--r--lib/orber/c_src/Makefile.in20
-rw-r--r--lib/orber/doc/src/ch_idl_to_erlang_mapping.xml5
-rw-r--r--lib/orber/doc/src/notes.xml32
-rw-r--r--lib/orber/examples/Stack/Makefile4
-rw-r--r--lib/orber/src/Makefile19
-rw-r--r--lib/orber/src/orber_ifr_exceptiondef.erl22
-rw-r--r--lib/orber/src/orber_iiop_net.erl24
-rw-r--r--lib/orber/test/csiv2_SUITE.erl106
-rw-r--r--lib/orber/test/orber_test_lib.erl24
-rw-r--r--lib/orber/vsn.mk3
-rw-r--r--lib/os_mon/c_src/Makefile.in24
-rw-r--r--lib/os_mon/c_src/memsup.c71
-rw-r--r--lib/os_mon/doc/src/notes.xml17
-rw-r--r--lib/os_mon/mibs/Makefile8
-rw-r--r--lib/os_mon/src/Makefile8
-rw-r--r--lib/os_mon/src/memsup.erl4
-rw-r--r--lib/os_mon/src/os_mon.erl8
-rw-r--r--lib/os_mon/test/Makefile1
-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/notes.xml17
-rw-r--r--lib/otp_mibs/mibs/Makefile2
-rw-r--r--lib/otp_mibs/src/Makefile4
-rw-r--r--lib/otp_mibs/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/notes.xml35
-rw-r--r--lib/parsetools/include/yeccpre.hrl4
-rw-r--r--lib/parsetools/src/Makefile9
-rw-r--r--lib/parsetools/src/esyntax.yrl360
-rw-r--r--lib/parsetools/src/leex.erl107
-rw-r--r--lib/parsetools/src/yecc.erl124
-rw-r--r--lib/parsetools/src/yeccparser.erl4
-rw-r--r--lib/parsetools/src/yeccscan.erl4
-rw-r--r--lib/parsetools/test/leex_SUITE.erl116
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl154
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/percept/doc/src/notes.xml17
-rw-r--r--lib/percept/src/Makefile4
-rw-r--r--lib/percept/vsn.mk2
-rw-r--r--lib/pman/doc/src/notes.xml34
-rw-r--r--lib/pman/src/Makefile4
-rw-r--r--lib/pman/src/pman_win.erl4
-rw-r--r--lib/pman/vsn.mk2
-rw-r--r--lib/public_key/asn1/Makefile8
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn16
-rw-r--r--lib/public_key/asn1/OTP-PUB-KEY.asn1config3
-rw-r--r--lib/public_key/asn1/PKCS-10.asn126
-rw-r--r--lib/public_key/doc/src/cert_records.xml56
-rw-r--r--lib/public_key/doc/src/introduction.xml20
-rw-r--r--lib/public_key/doc/src/notes.xml51
-rw-r--r--lib/public_key/doc/src/part.xml8
-rw-r--r--lib/public_key/doc/src/public_key.xml171
-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.hrl19
-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.erl6
-rw-r--r--lib/public_key/src/pubkey_crl.erl701
-rw-r--r--lib/public_key/src/pubkey_pem.erl12
-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.erl190
-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.erl332
-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/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/notes.xml76
-rw-r--r--lib/reltool/doc/src/reltool.xml31
-rw-r--r--lib/reltool/doc/src/reltool_usage.xml11
-rw-r--r--lib/reltool/examples/display_args5
-rw-r--r--lib/reltool/src/Makefile9
-rw-r--r--lib/reltool/src/reltool.erl4
-rw-r--r--lib/reltool/src/reltool_app_win.erl12
-rw-r--r--lib/reltool/src/reltool_fgraph_win.erl4
-rw-r--r--lib/reltool/src/reltool_mod_win.erl75
-rw-r--r--lib/reltool/src/reltool_server.erl180
-rw-r--r--lib/reltool/src/reltool_sys_win.erl132
-rw-r--r--lib/reltool/src/reltool_target.erl134
-rw-r--r--lib/reltool/src/reltool_utils.erl41
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl400
-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/escript/someapp-1.0/src/mymod.erl1
-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/unicode/ua-1.0/ebin/ua.app7
-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/trace_ip_drv.c23
-rw-r--r--lib/runtime_tools/doc/src/Makefile9
-rw-r--r--lib/runtime_tools/doc/src/book.xml5
-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.xml55
-rw-r--r--lib/runtime_tools/doc/src/part.xml (renamed from lib/inviso/doc/src/part.xml)19
-rw-r--r--lib/runtime_tools/src/Makefile16
-rw-r--r--lib/runtime_tools/src/appmon_info.erl (renamed from lib/appmon/src/appmon_info.erl)2
-rw-r--r--lib/runtime_tools/src/dbg.erl8
-rw-r--r--lib/runtime_tools/src/dyntrace.erl4
-rw-r--r--lib/runtime_tools/src/inviso_as_lib.erl155
-rw-r--r--lib/runtime_tools/src/inviso_autostart.erl201
-rw-r--r--lib/runtime_tools/src/inviso_autostart_server.erl311
-rw-r--r--lib/runtime_tools/src/inviso_rt.erl2885
-rw-r--r--lib/runtime_tools/src/inviso_rt_lib.erl474
-rw-r--r--lib/runtime_tools/src/inviso_rt_meta.erl1207
-rw-r--r--lib/runtime_tools/src/observer_backend.erl2
-rw-r--r--lib/runtime_tools/src/runtime_tools.app.src9
-rw-r--r--lib/runtime_tools/src/runtime_tools_sup.erl17
-rw-r--r--lib/runtime_tools/test/Makefile2
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl69
-rw-r--r--lib/runtime_tools/test/inviso_SUITE.erl2838
-rw-r--r--lib/runtime_tools/test/inviso_testmodule1_foo.erl9
-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.xml77
-rw-r--r--lib/sasl/src/Makefile8
-rw-r--r--lib/sasl/src/overload.erl5
-rw-r--r--lib/sasl/src/release_handler.erl23
-rw-r--r--lib/sasl/src/release_handler_1.erl8
-rw-r--r--lib/sasl/src/sasl.appup.src16
-rw-r--r--lib/sasl/src/si_sasl_supp.erl4
-rw-r--r--lib/sasl/src/systools_lib.erl15
-rw-r--r--lib/sasl/src/systools_make.erl158
-rw-r--r--lib/sasl/src/systools_rc.erl52
-rw-r--r--lib/sasl/src/systools_relup.erl39
-rw-r--r--lib/sasl/test/rb_SUITE.erl27
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl54
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/Makefile.src12
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl47
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl37
-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.erl72
-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_SUITE_data/d_unicode/lib/ua-1.0/ebin/ua.app9
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_unicode/lib/ua-1.0/src/ua1.erl2
-rw-r--r--lib/sasl/test/systools_rc_SUITE.erl18
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/src/Makefile2
-rw-r--r--lib/snmp/doc/src/notes.xml99
-rw-r--r--lib/snmp/doc/src/snmpm.xml3
-rw-r--r--lib/snmp/mibs/Makefile.in8
-rw-r--r--lib/snmp/src/agent/snmp_view_based_acm_mib.erl2
-rw-r--r--lib/snmp/src/agent/snmpa_agent.erl6
-rw-r--r--lib/snmp/src/app/Makefile4
-rw-r--r--lib/snmp/src/app/snmp.appup.src580
-rw-r--r--lib/snmp/src/compile/depend.mk6
-rw-r--r--lib/snmp/src/compile/snmpc.erl2
-rw-r--r--lib/snmp/src/manager/snmpm.erl622
-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/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.erl63
-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.erl128
-rw-r--r--lib/snmp/test/snmp_manager_user_test.erl4
-rw-r--r--lib/snmp/test/snmp_test_lib.erl4
-rw-r--r--lib/snmp/test/snmp_test_manager.erl10
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl1
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/man6/.gitignore (renamed from lib/inviso/doc/man3/.gitignore)0
-rw-r--r--lib/ssh/doc/src/Makefile35
-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.xml55
-rw-r--r--lib/ssh/doc/src/notes.xml181
-rw-r--r--lib/ssh/doc/src/ref_man.xml15
-rw-r--r--lib/ssh/doc/src/ssh.xml216
-rw-r--r--lib/ssh/doc/src/ssh_app.xml100
-rw-r--r--lib/ssh/doc/src/ssh_channel.xml362
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml125
-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.xml91
-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.xml (renamed from lib/inviso/doc/src/part_notes.xml)26
-rw-r--r--lib/ssh/doc/src/using_ssh.xml299
-rw-r--r--lib/ssh/src/Makefile10
-rw-r--r--lib/ssh/src/ssh.app.src6
-rw-r--r--lib/ssh/src/ssh.appup.src12
-rw-r--r--lib/ssh/src/ssh.erl118
-rw-r--r--lib/ssh/src/ssh_auth.erl26
-rw-r--r--lib/ssh/src/ssh_auth.hrl6
-rw-r--r--lib/ssh/src/ssh_channel.erl36
-rw-r--r--lib/ssh/src/ssh_cli.erl7
-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.erl76
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl82
-rw-r--r--lib/ssh/src/ssh_connection_manager.erl179
-rw-r--r--lib/ssh/src/ssh_daemon_channel.erl68
-rw-r--r--lib/ssh/src/ssh_file.erl29
-rw-r--r--lib/ssh/src/ssh_key_api.erl45
-rw-r--r--lib/ssh/src/ssh_server_key.erl (renamed from lib/stdlib/test/erl_eval_helper.erl)25
-rw-r--r--lib/ssh/src/ssh_server_key_api.erl (renamed from lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl)55
-rw-r--r--lib/ssh/src/ssh_sftp.erl12
-rw-r--r--lib/ssh/src/ssh_sftpd.erl94
-rw-r--r--lib/ssh/src/ssh_shell.erl8
-rw-r--r--lib/ssh/src/ssh_transport.erl10
-rw-r--r--lib/ssh/src/ssh_xfer.erl5
-rw-r--r--lib/ssh/src/ssh_xfer.hrl14
-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.erl331
-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.erl310
-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.erl4
-rw-r--r--lib/ssh/test/ssh_test_lib.erl19
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl194
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml1424
-rw-r--r--lib/ssl/doc/src/ssl.xml80
-rw-r--r--lib/ssl/doc/src/ssl_app.xml23
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml7
-rw-r--r--lib/ssl/src/Makefile29
-rw-r--r--lib/ssl/src/ssl.app.src2
-rw-r--r--lib/ssl/src/ssl.appup.src17
-rw-r--r--lib/ssl/src/ssl.erl401
-rw-r--r--lib/ssl/src/ssl_alert.erl11
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl51
-rw-r--r--lib/ssl/src/ssl_connection.erl485
-rw-r--r--lib/ssl/src/ssl_debug.erl99
-rw-r--r--lib/ssl/src/ssl_handshake.erl400
-rw-r--r--lib/ssl/src/ssl_handshake.hrl29
-rw-r--r--lib/ssl/src/ssl_internal.hrl6
-rw-r--r--lib/ssl/src/ssl_manager.erl48
-rw-r--r--lib/ssl/src/ssl_record.erl5
-rw-r--r--lib/ssl/src/ssl_session.erl18
-rw-r--r--lib/ssl/src/ssl_socket.erl35
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl50
-rw-r--r--lib/ssl/test/Makefile9
-rw-r--r--lib/ssl/test/erl_make_certs.erl6
-rw-r--r--lib/ssl/test/make_certs.erl19
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl2973
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl978
-rw-r--r--lib/ssl/test/ssl_cipher_SUITE.erl122
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl32
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl8
-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.erl1413
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl276
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl132
-rw-r--r--lib/ssl/test/ssl_test_lib.erl165
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl881
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/Makefile11
-rw-r--r--lib/stdlib/doc/src/base64.xml7
-rw-r--r--lib/stdlib/doc/src/binary.xml224
-rw-r--r--lib/stdlib/doc/src/epp.xml76
-rw-r--r--lib/stdlib/doc/src/erl_pp.xml8
-rw-r--r--lib/stdlib/doc/src/ets.xml545
-rw-r--r--lib/stdlib/doc/src/filelib.xml16
-rw-r--r--lib/stdlib/doc/src/io.xml364
-rw-r--r--lib/stdlib/doc/src/io_lib.xml82
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml476
-rw-r--r--lib/stdlib/doc/src/lists.xml68
-rw-r--r--lib/stdlib/doc/src/math.xml57
-rw-r--r--lib/stdlib/doc/src/notes.xml277
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml9
-rw-r--r--lib/stdlib/doc/src/proplists.xml24
-rw-r--r--lib/stdlib/doc/src/re.xml100
-rw-r--r--lib/stdlib/doc/src/shell.xml22
-rw-r--r--lib/stdlib/doc/src/stdlib_app.xml9
-rw-r--r--lib/stdlib/doc/src/string.xml26
-rw-r--r--lib/stdlib/doc/src/supervisor.xml8
-rw-r--r--lib/stdlib/doc/src/unicode.xml133
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml1396
-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.xml6
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl14
-rw-r--r--lib/stdlib/src/Makefile18
-rw-r--r--lib/stdlib/src/base64.erl31
-rw-r--r--lib/stdlib/src/beam_lib.erl30
-rw-r--r--lib/stdlib/src/binary.erl196
-rw-r--r--lib/stdlib/src/c.erl43
-rw-r--r--lib/stdlib/src/dets.erl162
-rw-r--r--lib/stdlib/src/dets_utils.erl4
-rw-r--r--lib/stdlib/src/dets_v8.erl4
-rw-r--r--lib/stdlib/src/dets_v9.erl4
-rw-r--r--lib/stdlib/src/dict.erl5
-rw-r--r--lib/stdlib/src/edlin.erl116
-rw-r--r--lib/stdlib/src/epp.erl240
-rw-r--r--lib/stdlib/src/erl_compile.erl12
-rw-r--r--lib/stdlib/src/erl_eval.erl54
-rw-r--r--lib/stdlib/src/erl_expand_records.erl10
-rw-r--r--lib/stdlib/src/erl_internal.erl10
-rw-r--r--lib/stdlib/src/erl_lint.erl398
-rw-r--r--lib/stdlib/src/erl_parse.yrl202
-rw-r--r--lib/stdlib/src/erl_pp.erl776
-rw-r--r--lib/stdlib/src/erl_scan.erl359
-rw-r--r--lib/stdlib/src/erl_tar.erl20
-rw-r--r--lib/stdlib/src/escript.erl35
-rw-r--r--lib/stdlib/src/ets.erl446
-rw-r--r--lib/stdlib/src/eval_bits.erl64
-rw-r--r--lib/stdlib/src/file_sorter.erl8
-rw-r--r--lib/stdlib/src/filelib.erl234
-rw-r--r--lib/stdlib/src/filename.erl80
-rw-r--r--lib/stdlib/src/gb_sets.erl7
-rw-r--r--lib/stdlib/src/gb_trees.erl7
-rw-r--r--lib/stdlib/src/io.erl211
-rw-r--r--lib/stdlib/src/io_lib.erl303
-rw-r--r--lib/stdlib/src/io_lib_format.erl122
-rw-r--r--lib/stdlib/src/io_lib_fread.erl6
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl307
-rw-r--r--lib/stdlib/src/lib.erl187
-rw-r--r--lib/stdlib/src/lists.erl65
-rw-r--r--lib/stdlib/src/log_mf_h.erl4
-rw-r--r--lib/stdlib/src/math.erl112
-rw-r--r--lib/stdlib/src/ms_transform.erl6
-rw-r--r--lib/stdlib/src/otp_internal.erl131
-rw-r--r--lib/stdlib/src/proc_lib.erl100
-rw-r--r--lib/stdlib/src/proplists.erl74
-rw-r--r--lib/stdlib/src/qlc.erl16
-rw-r--r--lib/stdlib/src/qlc_pt.erl20
-rw-r--r--lib/stdlib/src/queue.erl16
-rw-r--r--lib/stdlib/src/re.erl72
-rw-r--r--lib/stdlib/src/sets.erl5
-rw-r--r--lib/stdlib/src/shell.erl248
-rw-r--r--lib/stdlib/src/slave.erl4
-rw-r--r--lib/stdlib/src/stdlib.appup.src12
-rw-r--r--lib/stdlib/src/string.erl37
-rw-r--r--lib/stdlib/src/supervisor.erl25
-rw-r--r--lib/stdlib/src/sys.erl99
-rw-r--r--lib/stdlib/src/unicode.erl71
-rw-r--r--lib/stdlib/src/win32reg.erl4
-rw-r--r--lib/stdlib/src/zip.erl12
-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.erl22
-rw-r--r--lib/stdlib/test/dict_test_lib.erl57
-rw-r--r--lib/stdlib/test/digraph_SUITE.erl4
-rw-r--r--lib/stdlib/test/epp_SUITE.erl125
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl109
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl49
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl201
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl167
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl301
-rw-r--r--lib/stdlib/test/escript_SUITE.erl44
-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.erl194
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl97
-rw-r--r--lib/stdlib/test/filename_SUITE.erl248
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl19
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl11
-rw-r--r--lib/stdlib/test/id_transform_SUITE.erl15
-rw-r--r--lib/stdlib/test/io_SUITE.erl201
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl10
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl31
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl48
-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.erl344
-rw-r--r--lib/stdlib/test/sets_test_lib.erl84
-rw-r--r--lib/stdlib/test/shell_SUITE.erl245
-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.erl90
-rw-r--r--lib/stdlib/test/timer_SUITE.erl10
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml30
-rw-r--r--lib/syntax_tools/src/Makefile8
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl2
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl16
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl45
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl219
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl5
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl44
-rw-r--r--lib/syntax_tools/src/igor.erl79
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/test_server/doc/src/notes.xml204
-rw-r--r--lib/test_server/doc/src/test_server.xml14
-rw-r--r--lib/test_server/doc/src/test_server_ctrl.xml93
-rw-r--r--lib/test_server/doc/src/ts.xml35
-rw-r--r--lib/test_server/src/Makefile21
-rwxr-xr-xlib/test_server/src/config.guess541
-rwxr-xr-xlib/test_server/src/config.sub367
-rw-r--r--lib/test_server/src/erl2html2.erl296
-rw-r--r--lib/test_server/src/test_server.app.src3
-rw-r--r--lib/test_server/src/test_server.erl1375
-rw-r--r--lib/test_server/src/test_server_ctrl.erl1934
-rw-r--r--lib/test_server/src/test_server_gl.erl300
-rw-r--r--lib/test_server/src/test_server_h.erl4
-rw-r--r--lib/test_server/src/test_server_internal.hrl6
-rw-r--r--lib/test_server/src/test_server_io.erl319
-rw-r--r--lib/test_server/src/test_server_node.erl307
-rw-r--r--lib/test_server/src/test_server_sup.erl94
-rw-r--r--lib/test_server/src/ts.erl149
-rw-r--r--lib/test_server/src/ts_autoconf_vxworks.erl191
-rw-r--r--lib/test_server/src/ts_benchmark.erl91
-rw-r--r--lib/test_server/src/ts_erl_config.erl12
-rw-r--r--lib/test_server/src/ts_install.erl19
-rw-r--r--lib/test_server/src/ts_lib.erl84
-rw-r--r--lib/test_server/src/ts_reports.erl545
-rw-r--r--lib/test_server/src/ts_run.erl55
-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/Makefile5
-rw-r--r--lib/test_server/test/erl2html2_SUITE.erl264
-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.hrl (renamed from lib/dialyzer/test/small_SUITE_data/results/pubsub)0
-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.erl346
-rw-r--r--lib/test_server/test/test_server_SUITE_data/Makefile.src6
-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_SUITE_data/test_server_unicode_SUITE.erl82
-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.erl78
-rw-r--r--lib/test_server/vsn.mk2
-rw-r--r--lib/toolbar/doc/src/notes.xml17
-rw-r--r--lib/toolbar/src/Makefile4
-rw-r--r--lib/toolbar/vsn.mk2
-rw-r--r--lib/tools/c_src/Makefile.in10
-rw-r--r--lib/tools/doc/src/cover.xml20
-rw-r--r--lib/tools/doc/src/notes.xml110
-rw-r--r--lib/tools/doc/src/xref.xml4
-rw-r--r--lib/tools/emacs/erlang-pkg.el3
-rw-r--r--lib/tools/emacs/erlang.el196
-rw-r--r--lib/tools/emacs/vsn.mk3
-rw-r--r--lib/tools/src/Makefile8
-rw-r--r--lib/tools/src/cover.erl326
-rw-r--r--lib/tools/src/lcnt.erl13
-rw-r--r--lib/tools/src/make.erl14
-rw-r--r--lib/tools/src/tags.erl8
-rw-r--r--lib/tools/src/xref.erl4
-rw-r--r--lib/tools/src/xref_base.erl60
-rw-r--r--lib/tools/src/xref_compiler.erl26
-rw-r--r--lib/tools/src/xref_reader.erl11
-rw-r--r--lib/tools/src/xref_utils.erl12
-rw-r--r--lib/tools/test/Makefile1
-rw-r--r--lib/tools/test/cover_SUITE.erl250
-rw-r--r--lib/tools/test/cover_SUITE_data/compile_beam/v.erl7
-rw-r--r--lib/tools/test/cover_SUITE_data/compile_beam/z.erl1
-rw-r--r--lib/tools/test/cover_SUITE_data/f.erl11
-rw-r--r--lib/tools/test/cprof_SUITE.erl2
-rw-r--r--lib/tools/test/emacs_SUITE.erl76
-rw-r--r--lib/tools/test/xref_SUITE.erl65
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/tv/doc/src/notes.xml17
-rw-r--r--lib/tv/src/Makefile4
-rw-r--r--lib/tv/src/tv_main.hrl3
-rw-r--r--lib/tv/vsn.mk2
-rw-r--r--lib/typer/src/Makefile6
-rw-r--r--lib/typer/src/typer.erl2
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/webtool/doc/src/notes.xml17
-rw-r--r--lib/webtool/src/Makefile4
-rw-r--r--lib/webtool/vsn.mk2
-rw-r--r--lib/wx/.gitignore5
-rw-r--r--lib/wx/aclocal.m427
-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/bugs.h6
-rw-r--r--lib/wx/api_gen/wx_extra/wxListCtrl.c_src4
-rw-r--r--lib/wx/api_gen/wx_extra/wxPrintout.erl5
-rw-r--r--lib/wx/api_gen/wx_extra/wxTreeCtrl.c_src (renamed from erts/test/autoimport_SUITE_data/dummy.txt)13
-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.erl61
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl173
-rw-r--r--lib/wx/api_gen/wxapi.conf151
-rwxr-xr-xlib/wx/autoconf/config.guess564
-rwxr-xr-xlib/wx/autoconf/config.sub544
-rw-r--r--lib/wx/c_src/Makefile.in5
-rw-r--r--lib/wx/c_src/egl_impl.cpp135
-rw-r--r--lib/wx/c_src/gen/wxe_derived_dest.h16
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp431
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp2219
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h2544
-rw-r--r--lib/wx/c_src/wxePrintout.cpp6
-rw-r--r--lib/wx/c_src/wxe_driver.c22
-rw-r--r--lib/wx/c_src/wxe_driver.h10
-rw-r--r--lib/wx/c_src/wxe_events.h6
-rw-r--r--lib/wx/c_src/wxe_gl.cpp6
-rw-r--r--lib/wx/c_src/wxe_impl.cpp54
-rw-r--r--lib/wx/c_src/wxe_impl.h24
-rw-r--r--lib/wx/c_src/wxe_ps_init.c7
-rw-r--r--lib/wx/c_src/wxe_return.cpp10
-rw-r--r--lib/wx/c_src/wxe_return.h10
-rwxr-xr-xlib/wx/configure.in98
-rw-r--r--lib/wx/doc/src/notes.xml48
-rw-r--r--lib/wx/examples/demo/demo.erl53
-rw-r--r--lib/wx/examples/demo/demo_html_tagger.erl3
-rw-r--r--lib/wx/examples/demo/ex_aui.erl22
-rw-r--r--lib/wx/examples/demo/ex_button.erl12
-rw-r--r--lib/wx/examples/demo/ex_canvas.erl7
-rw-r--r--lib/wx/examples/demo/ex_canvas_paint.erl25
-rw-r--r--lib/wx/examples/demo/ex_choices.erl8
-rw-r--r--lib/wx/examples/demo/ex_cursor.erl6
-rw-r--r--lib/wx/examples/demo/ex_dialogs.erl6
-rw-r--r--lib/wx/examples/demo/ex_frame_utils.erl6
-rw-r--r--lib/wx/examples/demo/ex_gauge.erl6
-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.erl6
-rw-r--r--lib/wx/examples/demo/ex_htmlWindow.erl6
-rw-r--r--lib/wx/examples/demo/ex_listCtrl.erl10
-rw-r--r--lib/wx/examples/demo/ex_notebook.erl6
-rw-r--r--lib/wx/examples/demo/ex_pickers.erl6
-rw-r--r--lib/wx/examples/demo/ex_popupMenu.erl6
-rw-r--r--lib/wx/examples/demo/ex_radioBox.erl6
-rw-r--r--lib/wx/examples/demo/ex_sashWindow.erl6
-rw-r--r--lib/wx/examples/demo/ex_sizers.erl6
-rw-r--r--lib/wx/examples/demo/ex_slider.erl6
-rw-r--r--lib/wx/examples/demo/ex_splitterWindow.erl6
-rw-r--r--lib/wx/examples/demo/ex_static.erl6
-rw-r--r--lib/wx/examples/demo/ex_textCtrl.erl24
-rw-r--r--lib/wx/examples/demo/ex_treeCtrl.erl6
-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/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/wxNcPaintEvent.erl64
-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/wxTreeCtrl.erl56
-rw-r--r--lib/wx/src/gen/wxWindowDC.erl2
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl2546
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl2544
-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.erl16
-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.erl27
-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/vsn.mk2
-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/motorcycles_dtd.txt3
-rw-r--r--lib/xmerl/doc/src/notes.xml25
-rw-r--r--lib/xmerl/src/Makefile20
-rw-r--r--lib/xmerl/src/xmerl_scan.erl5
-rw-r--r--lib/xmerl/src/xmerl_xsd.erl3
-rw-r--r--lib/xmerl/test/xmerl_SUITE.erl7
-rw-r--r--lib/xmerl/vsn.mk2
-rw-r--r--make/emd2exml.in25
-rwxr-xr-xmake/fakefop118
-rw-r--r--make/otp.mk.in71
-rw-r--r--make/otp_ded.mk.in4
-rw-r--r--make/otp_release_targets.mk13
-rw-r--r--make/output.mk.in139
-rw-r--r--make/run_make.mk7
-rwxr-xr-xotp_build2
-rw-r--r--system/README11
-rw-r--r--system/doc/efficiency_guide/advanced.xml26
-rw-r--r--system/doc/efficiency_guide/drivers.xml16
-rw-r--r--system/doc/embedded/part.xml3
-rw-r--r--system/doc/embedded/vxworks.xml193
-rw-r--r--system/doc/embedded/xmlfiles.mk5
-rw-r--r--system/doc/installation_guide/Makefile8
-rw-r--r--system/doc/installation_guide/part.xml6
-rw-r--r--system/doc/installation_guide/xmlfiles.mk5
-rw-r--r--system/doc/reference_manual/data_types.xml12
-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.xml15
-rw-r--r--system/doc/reference_manual/typespec.xml359
-rw-r--r--system/doc/top/Makefile6
-rw-r--r--xcomp/erl-xcomp-powerpc64-bgq-linux.conf272
2267 files changed, 126608 insertions, 147101 deletions
diff --git a/.gitignore b/.gitignore
index 0986bb6e4c..5804e36f9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@ autom4te.cache
i686-pc-linux-gnu
x86_64-unknown-linux-gnu
i386-apple-darwin[0-9]*.[0-9]*.[0-9]*
+x86_64-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]*
@@ -86,6 +87,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 +130,7 @@ JAVADOC-GENERATED
/release
+/make/output.mk
/make/emd2exml
# Created by "out_build update_primary"
@@ -148,7 +152,10 @@ JAVADOC-GENERATED
/lib/*/SKIP
/lib/*/doc/html/*.html
+/lib/*/doc/html/*.css
+/lib/*/doc/html/js
/lib/*/doc/html/*.gif
+/lib/*/doc/html/*.png
/lib/*/doc/html/*.eix
/lib/*/doc/man[0-9]/*.[0-9]
/lib/*/doc/pdf/*.fo
@@ -240,6 +247,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 +344,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..fbcb5f83c6
--- /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-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%
+
+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 34dd9fed8e..8051bc4393 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
@@ -656,40 +656,6 @@ suffix.
Use the `--enable-darwin-64bit` configure flag to build a 64-bit
binaries on Mac OS X.
-Building universal binaries on Mac OS X (obsolete information)
---------------------------------------------------------------
-
-(This information was written when Mac OS X Leopard was the current
-release. It may no longer work.)
-
-Universal 32bit binaries can be built on an Intel Mac using the
-`--enable-darwin-universal` configure option. There still may occur
-problems with certain applications using this option, but the base
-system should run smoothly.
-
-When building universal binaries on a PowerPC Mac (at least on Tiger),
-you must point out a suitable SDK that contains universal binaries.
-For instance, to build universal binaries for Tiger (10.4):
-
- $ CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk" \
- LDFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk" \
- ./configure --enable-darwin-universal
-
-Also, if you run Leopard, but want to build for Tiger, you must do by
-setting the `MACOSX_DEPLOYMENT_TARGET` environmental variable.
-
- $ export MACOSX_DEPLOYMENT_TARGET=10.4
-
-Experimental support for 64bit x86 darwin binaries can be enabled
-using the `--enable-darwin-64bit` configure flag. The 64bit binaries are
-best built and run on Leopard, but most of the system also works on
-Tiger (Tiger's 64bit libraries are, however, limited; therefore e.g. `odbc`,
-`crypto`, `ssl` etc. are not supported in Tiger). 64bit PPC binaries are not
-supported and we have no plans to add such support (no machines to
-test on).
-
-Universal binaries and 64bit binaries are mutually exclusive options.
-
Building a fast Erlang VM on Mac OS Lion
----------------------------------------
@@ -722,39 +688,36 @@ Install MacPorts (<http://www.macports.org/>). Then:
### 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 install it.
+If you want to build the `wx` application, you will need to get wxWidgets-2.9.4 (or later)
+(`wxWidgets-2.9.4.tar.bz2` from <http://sourceforge.net/projects/wxwindows/files/2.9.4/>)
+or get it from github:
+ $ git clone [email protected]:wxWidgets/wxWidgets.git
-Export the path for MacOSX10.6.sdk:
+Be aware that the wxWidgets-2.9 branch is a development branch of wxWidgets and the MacOsX
+port still lags behind the other ports.
- $ export SDK=/Developer/SDKs/MacOSX10.6.sdk
+Configure and build wxMac:
-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 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
+ $ ./configure --with-cocoa --prefix=/usr/local
$ make
$ sudo make install
+ $ export PATH=/usr/local/bin:$PATH
-To link wx properly you will also need to build and install `wxStyledTextCtrl`:
+Check that you got the correct wx-config
- $ cd contrib/src/stc
- $ make
- $ sudo make install
+ $ which wx-config
### Finish up ###
Build Erlang with the MacPorts GCC as the main compiler (using `clang`
for the Objective-C Cocoa code in the `wx` application):
- $ PATH=/usr/local/bin:$PATH CC=/opt/local/bin/gcc-mp-4.5 CXX=/opt/local/bin/g++-mp-4.5 ./configure --enable-m32-build make
+ $ export PATH=/usr/local/bin:$PATH
+ $ cd $ERL_TOP
+ $ CC=/opt/local/bin/gcc-mp-4.5 CXX=/opt/local/bin/g++-mp-4.5 ./configure --enable-darwin-64bit
+ $ make
$ sudo make install
-
How to Build a Debug Enabled Erlang RunTime System
--------------------------------------------------
@@ -801,7 +764,7 @@ Copyright and License
%CopyrightBegin%
-Copyright Ericsson AB 1998-2012. 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
@@ -825,12 +788,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
@@ -838,5 +801,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..c9ad09a1be 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
@@ -241,7 +241,7 @@ Copyright and License
%CopyrightBegin%
-Copyright Ericsson AB 2010. 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
@@ -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..d1fd64632c 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2012. 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
@@ -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
+
# ----------------------------------------------------------------------
#
@@ -121,7 +124,7 @@ BINDIR = $(DESTDIR)$(EXTRA_PREFIX)$(bindir)
#
# Erlang base public files
#
-ERL_BASE_PUB_FILES=erl erlc epmd run_erl to_erl dialyzer typer escript ct_run run_test
+ERL_BASE_PUB_FILES=erl erlc epmd run_erl to_erl dialyzer typer escript ct_run
# ERLANG_INST_LIBDIR is the top directory where the Erlang installation
# will be located when running.
@@ -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 e87b75607d..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
@@ -74,3 +74,5 @@ Copyright and License
[1]: http://www.erlang.org
[2]: http://wiki.github.com/erlang/otp/submitting-patches
[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 9578cd35c4..25f40944e7 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2013. 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
@@ -1849,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 84f470c905..05f177db91 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 84f470c905..05f177db91 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 415bb13093..cbb89b8618 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..46e884bd56 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..9e3d9832b1 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..959e989b6d 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..e340e0f4c6 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..afc5eaa333 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..b655cdb4cf 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 3cb5f06859..5fd60ebee1 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 523a6f03fb..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 41523ed185..69539e400c 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..0d7a4c0e87 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 36b2b331ba..609d7e703f 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
new file mode 100644
index 0000000000..e3ff9ac11e
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -0,0 +1,70 @@
+% This is an -*- erlang -*- file.
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+{application, compiler,
+ [{description, "ERTS CXC 138 10"},
+ {vsn, "4.9"},
+ {modules, [
+ beam_a,
+ beam_asm,
+ beam_block,
+ beam_bool,
+ beam_bsm,
+ beam_clean,
+ beam_dead,
+ beam_dict,
+ beam_disasm,
+ beam_except,
+ beam_flatten,
+ beam_jump,
+ beam_listing,
+ beam_opcodes,
+ beam_peep,
+ beam_receive,
+ beam_split,
+ beam_trim,
+ beam_type,
+ beam_utils,
+ beam_validator,
+ beam_z,
+ cerl,
+ cerl_clauses,
+ cerl_inline,
+ cerl_trees,
+ compile,
+ core_scan,
+ core_lint,
+ core_parse,
+ core_pp,
+ core_lib,
+ erl_bifs,
+ rec_env,
+ sys_core_dsetel,
+ sys_core_fold,
+ sys_core_inline,
+ sys_pre_attributes,
+ sys_pre_expand,
+ v3_codegen,
+ v3_core,
+ v3_kernel,
+ v3_kernel_pp,
+ v3_life
+ ]},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {env, []}]}.
diff --git a/bootstrap/lib/compiler/ebin/compiler.appup b/bootstrap/lib/compiler/ebin/compiler.appup
new file mode 100644
index 0000000000..8cc82fff38
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/compiler.appup
@@ -0,0 +1 @@
+{"4.9",[],[]}.
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..f600432713 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..80820731c1 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..e2dc90c887 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..330654dbf0 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..d8eabeda7c 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 1a176b2e3f..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 8a4debc1bc..5b06d8656e 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 6c3c6c9202..7d11d6576e 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 dbde58a39d..52f588c1bf 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..2392863c3d 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..b7081212cb 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..049f4056ae 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 b7e4a46066..2994e5257a 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 dc10d111d2..89417b095b 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..96f63b364c 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 5e286cdd48..3e69311b53 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..857e91c66f 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..84738305f7 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 b909dd2786..90c831b045 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/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index e07bef5657..bad3d6211f 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index 83a981cb1d..cc7d774ee5 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 aa7dcf388d..d74d356bfb 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..9da86c5d44 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..8ba005c6da 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..d84e01aa51 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..c34d495e04 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 864a2bdb2a..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 c827162acc..0bfad88d8c 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 4f39eb886a..06010a82dd 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..4b5543b994 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..7db3eddc6e 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..116aacacdc 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..b962e1ef21 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..f1f3bdcd8d 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..18ccc23eb0 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..358d9ae403 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
new file mode 100644
index 0000000000..ab1ae605b6
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -0,0 +1,119 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 an -*- erlang -*- file.
+%%
+{application, kernel,
+ [
+ {description, "ERTS CXC 138 10"},
+ {vsn, "2.16.1"},
+ {modules, [application,
+ application_controller,
+ application_master,
+ application_starter,
+ auth,
+ code,
+ code_server,
+ dist_util,
+ erl_boot_server,
+ erl_distribution,
+ erl_reply,
+ error_handler,
+ error_logger,
+ file,
+ file_server,
+ file_io_server,
+ global,
+ global_group,
+ global_search,
+ group,
+ heart,
+ hipe_unified_loader,
+ inet6_tcp,
+ inet6_tcp_dist,
+ inet6_udp,
+ inet6_sctp,
+ inet_config,
+ inet_hosts,
+ inet_gethost_native,
+ inet_tcp_dist,
+ kernel,
+ kernel_config,
+ net,
+ net_adm,
+ net_kernel,
+ os,
+ ram_file,
+ rpc,
+ user,
+ user_drv,
+ user_sup,
+ disk_log,
+ disk_log_1,
+ disk_log_server,
+ disk_log_sup,
+ dist_ac,
+ erl_ddll,
+ erl_epmd,
+ erts_debug,
+ gen_tcp,
+ gen_udp,
+ gen_sctp,
+ inet,
+ inet_db,
+ inet_dns,
+ inet_parse,
+ inet_res,
+ inet_tcp,
+ inet_udp,
+ inet_sctp,
+ pg2,
+ seq_trace,
+ standard_error,
+ wrap_log_reader]},
+ {registered, [application_controller,
+ erl_reply,
+ auth,
+ boot_server,
+ code_server,
+ disk_log_server,
+ disk_log_sup,
+ erl_prim_loader,
+ error_logger,
+ file_server_2,
+ fixtable_server,
+ global_group,
+ global_name_server,
+ heart,
+ init,
+ kernel_config,
+ kernel_sup,
+ net_kernel,
+ net_sup,
+ rex,
+ user,
+ os_server,
+ ddll_server,
+ erl_epmd,
+ inet_db,
+ pg2]},
+ {applications, []},
+ {env, [{error_logger, tty}]},
+ {mod, {kernel, []}}
+ ]
+}.
diff --git a/bootstrap/lib/kernel/ebin/kernel.appup b/bootstrap/lib/kernel/ebin/kernel.appup
new file mode 100644
index 0000000000..5a6be8c68e
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/kernel.appup
@@ -0,0 +1,27 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"2.16.1",
+ %% Up from - max two major revisions back
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
+ %% Down to - max two major revisions back
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
+}.
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..ce8077390c 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 a81f3e47d5..c5e31f18dc 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 1291f1cdfa..3972fe7b91 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..06347b102c 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 bfb80cf827..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..21b5722bb2 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..2a72398b7c 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/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 15b09a5f93..723343519d 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.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/dist.hrl b/bootstrap/lib/kernel/include/dist.hrl
index 5b52f6f294..e32c112e63 100644
--- a/bootstrap/lib/kernel/include/dist.hrl
+++ b/bootstrap/lib/kernel/include/dist.hrl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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/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..37ed5b2bf8 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 0cc6c6fce9..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..95eeda70bb 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..92522cadbb 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..215dda572c 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..2895659a45 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..32e872a31b 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..615db8ab9b 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..95592947b8 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/edlin_expand.beam b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
index 04177ed645..01570cb1d4 100644
--- a/bootstrap/lib/stdlib/ebin/edlin_expand.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index 55f5adc24d..59e07c65c2 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..1311018a7f 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..07f663b2c6 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..411f453a95 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..d8c77627f1 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 875ccf94c2..ac593149b8 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..849b0671ee 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..8e531126d2 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..99cb155374 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..80e49621dd 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 e832f973d7..fdb4030d91 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 642689d749..81ff105494 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..2342b1c81d 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..a1be03ba3a 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..14ab68a7e1 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 856816c553..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..444c2745a0 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..cee9abfd5d 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 5d68dda4cf..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..73cf837dfb 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..08e497e35a 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..a476dcc6be 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..b9bcaf8cf2 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..0de626c384 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 5e23551fb3..cf189f2ae5 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/math.beam b/bootstrap/lib/stdlib/ebin/math.beam
index e97284ca1e..c2e8dfb874 100644
--- a/bootstrap/lib/stdlib/ebin/math.beam
+++ b/bootstrap/lib/stdlib/ebin/math.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index bc16416161..f532375e3a 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 1a9a55dc24..c853cf37bd 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..a297dff09c 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/proplists.beam b/bootstrap/lib/stdlib/ebin/proplists.beam
index 6f0e5d6409..c768955ca7 100644
--- a/bootstrap/lib/stdlib/ebin/proplists.beam
+++ b/bootstrap/lib/stdlib/ebin/proplists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index 716f28b661..b26f9ea172 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 f51bc3ccbd..2f1db1d159 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/random.beam b/bootstrap/lib/stdlib/ebin/random.beam
index bc8c5ef31f..4a95240dfe 100644
--- a/bootstrap/lib/stdlib/ebin/random.beam
+++ b/bootstrap/lib/stdlib/ebin/random.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index 4b9cbb7cfc..17b5c0bd75 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..74eb720790 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..f4e416e5ad 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..b6a3f88679 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/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
new file mode 100644
index 0000000000..56b384ea89
--- /dev/null
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -0,0 +1,105 @@
+%% This is an -*- erlang -*- file.
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+{application, stdlib,
+ [{description, "ERTS CXC 138 10"},
+ {vsn, "1.19.1"},
+ {modules, [array,
+ base64,
+ beam_lib,
+ binary,
+ c,
+ calendar,
+ dets,
+ dets_server,
+ dets_sup,
+ dets_utils,
+ dets_v8,
+ dets_v9,
+ dict,
+ digraph,
+ digraph_utils,
+ edlin,
+ edlin_expand,
+ epp,
+ eval_bits,
+ erl_bits,
+ erl_compile,
+ erl_eval,
+ erl_expand_records,
+ erl_internal,
+ erl_lint,
+ erl_parse,
+ erl_posix_msg,
+ erl_pp,
+ erl_scan,
+ erl_tar,
+ error_logger_file_h,
+ error_logger_tty_h,
+ escript,
+ ets,
+ file_sorter,
+ filelib,
+ filename,
+ gb_trees,
+ gb_sets,
+ gen,
+ gen_event,
+ gen_fsm,
+ gen_server,
+ io,
+ io_lib,
+ io_lib_format,
+ io_lib_fread,
+ io_lib_pretty,
+ lib,
+ lists,
+ log_mf_h,
+ math,
+ ms_transform,
+ orddict,
+ ordsets,
+ otp_internal,
+ pg,
+ pool,
+ proc_lib,
+ proplists,
+ qlc,
+ qlc_pt,
+ queue,
+ random,
+ re,
+ sets,
+ shell,
+ shell_default,
+ slave,
+ sofs,
+ string,
+ supervisor,
+ supervisor_bridge,
+ sys,
+ timer,
+ unicode,
+ win32reg,
+ zip]},
+ {registered,[timer_server,rsh_starter,take_over_monitor,pool_master,
+ dets]},
+ {applications, [kernel]},
+ {env, []}]}.
+
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.appup b/bootstrap/lib/stdlib/ebin/stdlib.appup
new file mode 100644
index 0000000000..0f374eb279
--- /dev/null
+++ b/bootstrap/lib/stdlib/ebin/stdlib.appup
@@ -0,0 +1,27 @@
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"1.19.1",
+ %% Up from - max two major revisions back
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
+ %% Down to - max two major revisions back
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
+}.
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index 3ace8184d0..79e579f0aa 100644
--- a/bootstrap/lib/stdlib/ebin/string.beam
+++ b/bootstrap/lib/stdlib/ebin/string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 837027e93a..9bf3439674 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 7dcf140730..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 9b650dc872..17006f313d 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..92fdc7a862 100644
--- a/erts/Makefile.in
+++ b/erts/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2012. All Rights Reserved.
+# Copyright Ericsson AB 2006-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,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,20 +68,18 @@ $(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 \
$(ERL_TOP)/bin/dialyzer $(ERL_TOP)/bin/dialyzer.exe \
$(ERL_TOP)/bin/typer $(ERL_TOP)/bin/typer.exe \
- $(ERL_TOP)/bin/run_test $(ERL_TOP)/bin/run_test.exe \
$(ERL_TOP)/bin/ct_run $(ERL_TOP)/bin/ct_run.exe \
$(ERL_TOP)/bin/start*.boot $(ERL_TOP)/bin/start*.script
@if [ "X$(TARGET)" = "Xwin32" ]; then \
cp $(ERL_TOP)/bin/$(TARGET)/dialyzer.exe $(ERL_TOP)/bin/dialyzer.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/typer.exe $(ERL_TOP)/bin/typer.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/ct_run.exe $(ERL_TOP)/bin/ct_run.exe; \
- cp $(ERL_TOP)/bin/$(TARGET)/ct_run.exe $(ERL_TOP)/bin/run_test.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/erlc.exe $(ERL_TOP)/bin/erlc.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/erl.exe $(ERL_TOP)/bin/erl.exe; \
cp $(ERL_TOP)/bin/$(TARGET)/werl.exe $(ERL_TOP)/bin/werl.exe; \
@@ -101,7 +100,6 @@ local_setup:
cp $(ERL_TOP)/bin/$(TARGET)/dialyzer $(ERL_TOP)/bin/dialyzer; \
cp $(ERL_TOP)/bin/$(TARGET)/typer $(ERL_TOP)/bin/typer; \
cp $(ERL_TOP)/bin/$(TARGET)/ct_run $(ERL_TOP)/bin/ct_run; \
- ln -s $(ERL_TOP)/bin/ct_run $(ERL_TOP)/bin/run_test; \
cp $(ERL_TOP)/bin/$(TARGET)/erlc $(ERL_TOP)/bin/erlc; \
cp $(ERL_TOP)/bin/$(TARGET)/escript $(ERL_TOP)/bin/escript; \
chmod 755 $(ERL_TOP)/bin/erl $(ERL_TOP)/bin/erlc \
@@ -128,10 +126,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 +137,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 9578cd35c4..25f40944e7 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2013. 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
@@ -1849,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/config.guess b/erts/autoconf/config.guess
index 38a833903b..f475ceb413 100755
--- a/erts/autoconf/config.guess
+++ b/erts/autoconf/config.guess
@@ -1,14 +1,12 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-05-17'
+timestamp='2013-02-12'
# 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
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@@ -17,26 +15,22 @@ timestamp='2007-05-17'
# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
#
-# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# Please send patches with a ChangeLog entry to [email protected].
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -56,8 +50,7 @@ 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.
+Copyright 1992-2013 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."
@@ -144,7 +137,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
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*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -170,7 +163,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 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
+ | grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -180,7 +173,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
;;
esac
# The OS release
@@ -201,6 +194,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -223,7 +220,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -269,7 +266,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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 ;;
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
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
@@ -295,12 +295,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-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:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -324,14 +324,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
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/[^.]*//'`
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
@@ -375,23 +394,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -461,8 +480,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ # 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 ] || \
@@ -475,7 +494,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -532,7 +551,7 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[45])
+ *:AIX:*:[4567])
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
@@ -575,52 +594,52 @@ EOF
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" ;;
+ 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
+ esac ;;
+ esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ sed 's/^ //' << EOF >$dummy.c
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
+ #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);
+ 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);
- }
+ 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
@@ -640,7 +659,7 @@ EOF
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
+ grep -q __LP64__
then
HP_ARCH="hppa2.0w"
else
@@ -711,22 +730,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -750,14 +769,14 @@ EOF
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 ;;
+ 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}"
+ 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}
@@ -769,40 +788,51 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
amd64)
echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
+ exit ;;
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
+ i*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ # 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)
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
- EM64T | authenticamd)
+ authenticamd | genuineintel | EM64T)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
+ IA64)
+ echo ia64-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 ;;
+ 8664:Windows_NT:*)
+ echo x86_64-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
@@ -832,20 +862,68 @@ EOF
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
- arm*:Linux:*:*)
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
echo ${UNAME_MACHINE}-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 -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -856,74 +934,36 @@ EOF
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:*:*)
+ mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
- #undef mips64
- #undef mips64el
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
+ CPU=${UNAME_MACHINE}el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
+ CPU=${UNAME_MACHINE}
#else
CPU=
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
+ or1k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
+ padre:Linux:*:*)
+ echo sparc-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}
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
@@ -933,14 +973,17 @@ EOF
*) echo hppa-unknown-linux-gnu ;;
esac
exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -948,81 +991,18 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- tile:Linux:*:*)
- echo tile-unknown-linux-gnu
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-gnu
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- xtensa:Linux:*:*)
- echo xtensa-unknown-linux-gnu
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-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
@@ -1030,11 +1010,11 @@ EOF
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,
+ # 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.
+ # Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1051,7 +1031,7 @@ EOF
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
@@ -1066,7 +1046,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1094,10 +1074,13 @@ EOF
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 ;;
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1132,8 +1115,18 @@ EOF
/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; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ 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; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
@@ -1146,7 +1139,7 @@ EOF
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
@@ -1166,10 +1159,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- echo i586-unisys-sysv4
- 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
@@ -1195,11 +1188,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv${UNAME_RELEASE}
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1209,6 +1202,12 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1236,6 +1235,16 @@ EOF
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
unknown) UNAME_PROCESSOR=powerpc ;;
esac
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1251,7 +1260,10 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
@@ -1296,13 +1308,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ 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`
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1317,11 +1329,14 @@ EOF
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ 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_
@@ -1339,11 +1354,11 @@ main ()
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
- "4"
+ "4"
#else
- ""
+ ""
#endif
- ); exit (0);
+ ); exit (0);
#endif
#endif
@@ -1477,9 +1492,9 @@ 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
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
If the version you run ($0) is already up to date, please
send the following data and any information you think might be
diff --git a/erts/autoconf/config.sub b/erts/autoconf/config.sub
index f43233b104..bb6edbdb47 100755
--- a/erts/autoconf/config.sub
+++ b/erts/autoconf/config.sub
@@ -1,44 +1,40 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-04-29'
+timestamp='2013-02-12'
-# 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
+# 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 3 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.
+# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Please send patches with a ChangeLog entry to [email protected].
#
# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
# 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.
@@ -72,8 +68,7 @@ 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.
+Copyright 1992-2013 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."
@@ -120,12 +115,18 @@ esac
# 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* | \
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -148,10 +149,13 @@ case $os in
-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)
+ -apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
+ -bluegene*)
+ os=-cnk
+ ;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
@@ -166,10 +170,10 @@ case $os in
os=-chorusos
basic_machine=$1
;;
- -chorusrdb)
- os=-chorusrdb
+ -chorusrdb)
+ os=-chorusrdb
basic_machine=$1
- ;;
+ ;;
-hiux*)
os=-hiuxwe2
;;
@@ -214,6 +218,12 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
-lynx*)
os=-lynxos
;;
@@ -238,24 +248,34 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 | aarch64_be \
| 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 \
+ | arc \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | be32 | be64 \
| bfin \
| c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
+ | epiphany \
| fido | fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
- | mips64vr | mips64vrel \
+ | mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
@@ -266,31 +286,45 @@ case $basic_machine in
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
+ | moxie \
| mt \
| msp430 \
- | nios | nios2 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | or32 \
+ | open8 \
+ | or1k | or32 \
| pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
+ | rl78 | rx \
| score \
- | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | 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 \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
- | z8k)
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
basic_machine=$basic_machine-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -300,6 +334,21 @@ case $basic_machine in
basic_machine=mt-unknown
;;
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-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.
@@ -314,29 +363,37 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* | aarch64_be-* \
| 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-* \
+ | be32-* | be64-* \
| bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
| 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-* \
+ | hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
+ | mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
@@ -347,31 +404,41 @@ case $basic_machine in
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
- | nios-* | nios2-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | 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-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
| tron-* \
- | v850-* | v850e-* | vax-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
| we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
- | xstormy16-* | xtensa-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
| ymp-* \
- | z8k-*)
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -389,7 +456,7 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
- abacus)
+ abacus)
basic_machine=abacus-unknown
;;
adobe68k)
@@ -435,6 +502,10 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-bsd
;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -443,10 +514,35 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -475,8 +571,8 @@ case $basic_machine in
basic_machine=craynv-cray
os=-unicosmp
;;
- cr16c)
- basic_machine=cr16c-unknown
+ cr16 | cr16-*)
+ basic_machine=cr16-unknown
os=-elf
;;
crds | unos)
@@ -514,6 +610,10 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
@@ -629,7 +729,6 @@ case $basic_machine in
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
@@ -668,6 +767,14 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
m88k-omron*)
basic_machine=m88k-omron
;;
@@ -679,6 +786,13 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
mingw32)
basic_machine=i386-pc
os=-mingw32
@@ -715,10 +829,18 @@ case $basic_machine in
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -783,6 +905,12 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@@ -813,6 +941,14 @@ case $basic_machine in
basic_machine=i860-intel
os=-osf
;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
pbd)
basic_machine=sparc-tti
;;
@@ -857,9 +993,10 @@ case $basic_machine in
;;
power) basic_machine=power-ibm
;;
- ppc) basic_machine=powerpc-unknown
+ ppc | ppcbe) basic_machine=powerpc-unknown
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
@@ -884,7 +1021,11 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
- rdos)
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
basic_machine=i386-pc
os=-rdos
;;
@@ -953,6 +1094,9 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sun2)
basic_machine=m68000-sun
;;
@@ -1009,17 +1153,9 @@ case $basic_machine in
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
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
;;
tx39)
basic_machine=mipstx39-unknown
@@ -1027,10 +1163,6 @@ case $basic_machine in
tx39el)
basic_machine=mipstx39el-unknown
;;
- tile*)
- basic_machine=tile-tilera
- os=-linux-gnu
- ;;
toad1)
basic_machine=pdp10-xkl
os=-tops20
@@ -1092,6 +1224,9 @@ case $basic_machine in
xps | xps100)
basic_machine=xps100-honeywell
;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1100,6 +1235,10 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
none)
basic_machine=none-none
os=-none
@@ -1138,7 +1277,7 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
- sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1185,9 +1324,12 @@ esac
if [ x"$os" != x"" ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
@@ -1208,21 +1350,23 @@ case $os in
# 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* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
+ | -aos* | -aros* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
+ | -bitrig* | -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* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
@@ -1230,7 +1374,7 @@ case $os in
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1269,7 +1413,7 @@ case $os in
-opened*)
os=-openedition
;;
- -os400*)
+ -os400*)
os=-os400
;;
-wince*)
@@ -1318,7 +1462,7 @@ case $os in
-sinix*)
os=-sysv4
;;
- -tpf*)
+ -tpf*)
os=-tpf
;;
-triton*)
@@ -1354,12 +1498,14 @@ case $os in
-aros*)
os=-aros
;;
- -kaos*)
- os=-kaos
- ;;
-zvmoe)
os=-zvmoe
;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
+ ;;
-none)
;;
*)
@@ -1382,10 +1528,10 @@ else
# system, and we'll never get to this point.
case $basic_machine in
- score-*)
+ score-*)
os=-elf
;;
- spu-*)
+ spu-*)
os=-elf
;;
*-acorn)
@@ -1397,8 +1543,20 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
- c4x-* | tic4x-*)
- os=-coff
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
@@ -1418,14 +1576,11 @@ case $basic_machine in
;;
m68000-sun)
os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
;;
m68*-cisco)
os=-aout
;;
- mep-*)
+ mep-*)
os=-elf
;;
mips*-cisco)
@@ -1434,6 +1589,9 @@ case $basic_machine in
mips*-*)
os=-elf
;;
+ or1k-*)
+ os=-elf
+ ;;
or32-*)
os=-coff
;;
@@ -1452,7 +1610,7 @@ case $basic_machine in
*-ibm)
os=-aix
;;
- *-knuth)
+ *-knuth)
os=-mmixware
;;
*-wec)
@@ -1557,7 +1715,7 @@ case $basic_machine in
-sunos*)
vendor=sun
;;
- -aix*)
+ -cnk*|-aix*)
vendor=ibm
;;
-beos*)
@@ -1628,3 +1786,4 @@ exit
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
+
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
index 162fd38c2b..e2d2f415f1 100644
--- a/erts/autoconf/vxworks/sed.general
+++ b/erts/autoconf/vxworks/sed.general
@@ -31,6 +31,7 @@ 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|
+s|^#undef SIZEOF_VOID_P$|#define SIZEOF_VOID_P 4|
# General stuff.
s|@erts_rootdir@|/clearcase/otp/erts|
@@ -93,7 +94,6 @@ s|@TTPREFIX@|GCC_EXEC_PREFIX=@WIND_BASE@/host/@HOST_TYPE@/lib/gcc-lib/ @WIND_BAS
# 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|
diff --git a/erts/autoconf/vxworks/sed.vxworks_ppc32 b/erts/autoconf/vxworks/sed.vxworks_ppc32
index 5db28337c0..48ec912b4f 100644
--- a/erts/autoconf/vxworks/sed.vxworks_ppc32
+++ b/erts/autoconf/vxworks/sed.vxworks_ppc32
@@ -23,6 +23,10 @@
#
#
#
+# Install programs etc
+s|@INSTALL@|/usr/bin/install -c|
+
+# other
s|@host@|vxworks_ppc32|
s|@system_type@|vxworks_ppc32|
s|@ARCH@|ppc32|
diff --git a/erts/autoconf/vxworks/sed.vxworks_simlinux b/erts/autoconf/vxworks/sed.vxworks_simlinux
index 6eb6f8ea92..fbf6d7bc9d 100644
--- a/erts/autoconf/vxworks/sed.vxworks_simlinux
+++ b/erts/autoconf/vxworks/sed.vxworks_simlinux
@@ -23,6 +23,11 @@
#
#
#
+#
+# Install programs etc
+s|@INSTALL@|/usr/bin/install -c|
+
+# other
s|@host@|vxworks_simlinux|
s|@system_type@|vxworks_simlinux|
s|@ARCH@|simlinux|
diff --git a/erts/autoconf/vxworks/sed.vxworks_simso b/erts/autoconf/vxworks/sed.vxworks_simso
index 1d7413b484..17ec092dee 100644
--- a/erts/autoconf/vxworks/sed.vxworks_simso
+++ b/erts/autoconf/vxworks/sed.vxworks_simso
@@ -23,6 +23,10 @@
#
#
#
+# Install programs etc
+s|@INSTALL@|/usr/ucb/install -c|
+
+# other
s|@host@|vxworks_simso|
s|@system_type@|vxworks_simso|
s|@ARCH@|simso|
diff --git a/erts/configure.in b/erts/configure.in
index 6ad1951a4e..7257751068 100644
--- a/erts/configure.in
+++ b/erts/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 1997-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1997-2013. 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
@@ -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
@@ -1879,12 +1923,21 @@ fi
AC_CHECK_FUNCS([getipnodebyname getipnodebyaddr gethostbyname2])
AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlopen \
- pread pwrite writev memmove strerror strerror_r strncasecmp \
+ pread pwrite 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 time2posix \
setlocale nl_langinfo poll])
+dnl writev on OS X snow leopard is broken for files > 4GB
+case $host_os in
+ darwin10.8.0)
+ AC_MSG_CHECKING([for writev])
+ AC_MSG_RESULT(no, not stable on OS X Snow Leopard) ;;
+ *)
+ AC_CHECK_FUNCS([writev]) ;;
+esac
+
AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>])
disable_vfork=false
@@ -2000,8 +2053,12 @@ case "$erts_cv_have_in6addr_loopback" in
[Define to 1 if you have the variable in6addr_loopback declared.])
esac
-AC_CHECK_DECLS([IN6ADDR_ANY_INIT, IN6ADDR_LOOPBACK_INIT], [], [],
- [#include <netinet/in.h>])
+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.
@@ -4457,7 +4514,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 ----------------------------------------------------------------------
@@ -4520,6 +4577,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 da245d7fe8..89d7c85a86 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -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..e512c19e05 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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..61a9b0e716
--- /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>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>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> sends
+ 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 behaviour 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 f31b0cb18b..c37138e7b1 100644
--- a/erts/doc/src/driver_entry.xml
+++ b/erts/doc/src/driver_entry.xml
@@ -400,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>
@@ -427,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 f931445a3e..bb81330fec 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>1996</year><year>2012</year>
+ <year>1996</year><year>2013</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>
@@ -582,11 +582,95 @@
<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>
+ <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="+pc"/><marker id="max_processes"><c><![CDATA[+pc Range]]></c></marker></tag>
+ <item>
+ <p>Sets the range of characters that the system will consider printable in heuristic detection of strings. This typically affects the shell, debugger and io:format functions (when ~tp is used in the format string).</p>
+ <p>Currently two values for the <c>Range</c> are supported:
+ <taglist>
+ <tag><c>latin1</c></tag> <item>The default. Only characters
+ in the ISO-latin-1 range can be considered printable, which means
+ that a character with a code point &gt; 255 will never be
+ considered printable and that lists containing such
+ characters will be displayed as lists of integers rather
+ than text strings by tools.</item>
+ <tag><c>unicode</c></tag>
+ <item>All printable Unicode characters are considered when
+ determining if a list of integers is to be displayed in
+ string syntax. This may give unexpected results if for
+ example your font does not cover all Unicode
+ characters.</item>
+ </taglist>
+ </p>
+ <p>Se also <seealso marker="stdlib:io#printable_range/0">
+ io:printable_range/0</seealso>.</p>
+ </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>On Windows the default value is set to <c>8196</c> because the
+ normal OS limitations are set higher than most machines can handle.</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>
@@ -595,21 +679,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>
@@ -619,7 +696,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,
@@ -657,7 +734,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>
@@ -907,15 +1001,20 @@
<p>For more information, see
<seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p>
</item>
- <tag><marker id="+sws"><c>+sws default|legacy|proposal</c></marker></tag>
+ <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</c></marker></tag>
<item>
- <p>Set scheduler wakeup strategy. Default is <c>legacy</c> (has been
- used since OTP-R13B). The <c>proposal</c> strategy is the currently
- proposed strategy for OTP-R16. Note that the <c>proposal</c> strategy
- might change during OTP-R15.
+ <p>
+ Set scheduler wakeup strategy. Default strategy changed in erts-5.10/OTP-R16A. This strategy was previously known as <c>proposal</c> in OTP-R15. The <c>legacy</c> strategy was used as default from R13 up to and including R15.
</p>
- <p><em>NOTE:</em> This flag may be removed or changed at any time
- without prior notice.
+ <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice.
</p>
</item>
<tag><marker id="+swt"><c>+swt very_low|low|medium|high|very_high</c></marker></tag>
@@ -932,6 +1031,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.
@@ -1072,7 +1187,7 @@
</item>
</taglist>
</item>
- <tag><c><![CDATA[ERL_AFLAGS]]></c></tag>
+ <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>
@@ -1082,7 +1197,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..84f4be208d 100644
--- a/erts/doc/src/erl_dist_protocol.xml
+++ b/erts/doc/src/erl_dist_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -162,11 +162,12 @@ By default EPMD listens on port 4369.
</item>
<tag><c>Nlen</c></tag>
<item>
- The length of the <c>NodeName</c>.
+ The length (in bytes) of the <c>NodeName</c> field.
</item>
<tag><c>NodeName</c></tag>
<item>
- The NodeName as a string of length <c>Nlen</c>.
+ The NodeName as an UTF-8 encoded string of
+ <c>Nlen</c> bytes.
</item>
<tag><c>Elen</c></tag>
<item>
@@ -322,7 +323,7 @@ If Result > 0, the packet only consists of [119, Result].
NodeInfo is, as expressed in Erlang:
</p>
<code>
- io:format("name ~s at port ~p~n", [NodeName, Port]).
+ io:format("name ~ts at port ~p~n", [NodeName, Port]).
</code>
</section>
@@ -362,14 +363,14 @@ If Result > 0, the packet only consists of [119, Result].
NodeInfo is, as expressed in Erlang:
</p>
<code>
- io:format("active name ~s at port ~p, fd = ~p ~n",
+ io:format("active name ~ts at port ~p, fd = ~p ~n",
[NodeName, Port, Fd]).
</code>
<p>
or
</p>
<code>
- io:format("old/unused name ~s at port ~p, fd = ~p~n",
+ io:format("old/unused name ~ts at port ~p, fd = ~p~n",
[NodeName, Port, Fd]).
</code>
@@ -547,13 +548,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 e16fd744c0..efe0483b31 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>2012</year>
+ <year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -145,12 +145,20 @@
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
@@ -162,10 +170,13 @@
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>
+ zero timeouts. The
+ <seealso marker="#erl_drv_consume_timeslice"><c>erl_drv_consume_timeslice()</c></seealso>
+ function can be useful in order to determine when to trigger such
+ timeout callback calls. It might, however, not always be possible to
+ implement it this way, 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>
@@ -309,9 +320,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>
@@ -1529,16 +1540,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
@@ -1547,6 +1623,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>
@@ -1607,6 +1687,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>
@@ -1634,22 +1716,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
@@ -1701,17 +1793,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
@@ -1732,7 +1824,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[
@@ -1742,7 +1834,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
@@ -1762,7 +1854,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
@@ -1778,6 +1870,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>
@@ -1791,6 +1899,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>
@@ -1799,20 +1909,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>
@@ -1827,9 +1963,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
emulator thread. This enables the driver to perform
time-consuming, blocking operations without blocking the
emulator.</p>
- <p>Erlang is by default started without an async thread pool. The
- number of async threads that the runtime system should use
- is specified by the
+ <p>The async thread pool size can be set with the
<seealso marker="erl#async_thread_pool_size">+A</seealso>
command line argument of <seealso marker="erl">erl(1)</seealso>.
If no async thread pool is available, the call is made
@@ -2674,7 +2808,6 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
<p>This function is thread-safe.</p>
</desc>
</func>
-
<func>
<name><ret>int</ret><nametext>erl_drv_getenv(char *key, char *value, size_t *value_size)</nametext></name>
<fsummary>Get the value of an environment variable</fsummary>
@@ -2710,6 +2843,52 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
<p>This function is thread-safe.</p>
</desc>
</func>
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, int percent)</nametext></name>
+ <fsummary>Give the runtime system a hint about how much CPU time the
+ current driver callback call has consumed</fsummary>
+ <desc>
+ <marker id="erl_drv_consume_timeslice"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>port</c></tag>
+ <item>Port handle of the executing port.</item>
+ <tag><c>percent</c></tag>
+ <item>Approximate consumed fraction of a full
+ time-slice in percent.</item>
+ </taglist>
+ <p>Give the runtime system a hint about how much CPU time the
+ current driver callback call has consumed since last hint, or
+ since the start of the callback if no previous hint has been given.
+ The time is given as a fraction, in percent, of a full time-slice
+ that a port is allowed to execute before it should surrender the
+ CPU to other runnable ports or processes. Valid range is
+ <c>[1, 100]</c>. The scheduling time-slice is not an exact entity,
+ but can usually be approximated to about 1 millisecond.</p>
+
+ <p>Note that it is up to the runtime system to determine if and
+ how to use this information. Implementations on some platforms
+ may use other means in order to determine the consumed fraction
+ of the time-slice. Lengthy driver callbacks should regardless of
+ this frequently call the <c>erl_drv_consume_timeslice()</c>
+ function in order to determine if it is allowed to continue
+ execution or not.</p>
+
+ <p><c>erl_drv_consume_timeslice()</c> returns a non-zero value
+ if the time-slice has been exhausted, and zero if the callback is
+ allowed to continue execution. If a non-zero value is
+ returned the driver callback should return as soon as possible in
+ order for the port to be able to yield.</p>
+
+ <p>This function is provided to better support co-operative scheduling,
+ improve system responsiveness, and to make it easier to prevent
+ misbehaviors of the VM due to a port monopolizing a scheduler thread.
+ It can be used when dividing length work into a number of repeated
+ driver callback calls without the need to use threads. Also see the
+ important <seealso marker="#WARNING">warning</seealso> text at the
+ beginning of this document.</p>
+ </desc>
+ </func>
</funcs>
<section>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index fd2da2cfe3..c6849f3326 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -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 f00f7b9f46..864b91946a 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>2012</year>
+ <year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -174,9 +174,11 @@ ok
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
+ work and call the native function multiple times. Function
+ <seealso marker="#enif_consume_timeslice">enif_consume_timeslice</seealso> can be
+ used this facilitate such work division. In some cases, however, this might not
+ be possible, e.g. when calling third party libraries. Then 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>
@@ -227,8 +229,8 @@ ok
bit length have no support yet.</p>
</item>
<tag>Resource objects</tag>
- <item><p>The use of resource objects is a way to return pointers to
- native data structures from a NIF in a safe way. A resource object is
+ <item><p>The use of resource objects is a safe way to return pointers to
+ native data structures from a NIF. A resource object is
just a block of memory allocated with
<seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>.
A handle ("safe pointer") to this memory block can then be returned to Erlang by the use of
@@ -581,6 +583,31 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">erl_drv_cond_wait</seealso>.
</p></desc>
</func>
+ <func><name><ret>int</ret><nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext></name>
+ <fsummary></fsummary>
+ <desc><p>Give the runtime system a hint about how much CPU time the current NIF call has consumed
+ since last hint, or since the start of the NIF if no previous hint has been given.
+ The time is given as a <c>percent</c> of the timeslice that a process is allowed to execute Erlang
+ code until it may be suspended to give time for other runnable processes.
+ The scheduling timeslice is not an exact entity, but can usually be
+ approximated to about 1 millisecond.</p>
+ <p>Note that it is up to the runtime system to determine if and how to use this information.
+ Implementations on some platforms may use other means in order to determine consumed
+ CPU time. Lengthy NIFs should regardless of this frequently call <c>enif_consume_timeslice</c>
+ in order to determine if it is allowed to continue execution or not.</p>
+
+ <p>Returns 1 if the timeslice is exhausted, or 0 otherwise. If 1 is returned the NIF should return
+ as soon as possible in order for the process to yield.</p>
+ <p>Argument <c>percent</c> must be an integer between 1 and 100. This function
+ must only be called from a NIF-calling thread and argument <c>env</c> must be
+ the environment of the calling process.</p>
+ <p>This function is provided to better support co-operative scheduling, improve system responsiveness,
+ and make it easier to prevent misbehaviors of the VM due to a NIF monopolizing a scheduler thread.
+ It can be used to divide <seealso marker="#lengthy_work">length work</seealso> into
+ a number of repeated NIF-calls without the need to create threads.
+ See also the <seealso marker="#WARNING">warning</seealso> text at the beginning of this document.</p>
+ </desc>
+ </func>
<func><name><ret>int</ret><nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">erl_drv_equal_tids</seealso>.
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 d85dff2c0c..06fefa8efb 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.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>
@@ -65,14 +65,14 @@
<funcs>
<func>
- <name>abs(Number) -> integer() | float()</name>
+ <name name="abs" arity="1" clause_i="1"/>
+ <name name="abs" arity="1" clause_i="2"/>
+ <type variable="Float" name_i="1"/>
+ <type variable="Int" name_i="2"/>
<fsummary>Arithmetical absolute value</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
<p>Returns an integer or float which is the arithmetical
- absolute value of <c>Number</c>.</p>
+ absolute value of <c><anno>Float</anno></c> or <c><anno>Int</anno></c>.</p>
<pre>
> <input>abs(-3.33).</input>
3.33
@@ -82,26 +82,19 @@
</desc>
</func>
<func>
- <name>erlang:adler32(Data) -> integer()</name>
+ <name name="adler32" arity="1"/>
<fsummary>Compute adler32 checksum</fsummary>
- <type>
- <v>Data = iodata()</v>
- </type>
<desc>
- <p>Computes and returns the adler32 checksum for <c>Data</c>.</p>
+ <p>Computes and returns the adler32 checksum for <c><anno>Data</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:adler32(OldAdler, Data) -> integer()</name>
+ <name name="adler32" arity="2"/>
<fsummary>Compute adler32 checksum</fsummary>
- <type>
- <v>OldAdler = integer()</v>
- <v>Data = iodata()</v>
- </type>
<desc>
<p>Continue computing the adler32 checksum by combining
- the previous checksum, <c>OldAdler</c>, with the checksum of
- <c>Data</c>.</p>
+ the previous checksum, <c><anno>OldAdler</anno></c>, with the checksum of
+ <c><anno>Data</anno></c>.</p>
<p>The following code:</p>
<code>
X = erlang:adler32(Data1),
@@ -114,12 +107,8 @@
</desc>
</func>
<func>
- <name>erlang:adler32_combine(FirstAdler, SecondAdler, SecondSize) -> integer()</name>
+ <name name="adler32_combine" arity="3"/>
<fsummary>Combine two adler32 checksums</fsummary>
- <type>
- <v>FirstAdler = SecondAdler = integer()</v>
- <v>SecondSize = integer()</v>
- </type>
<desc>
<p>Combines two previously computed adler32 checksums.
This computation requires the size of the data object for
@@ -138,18 +127,14 @@
</desc>
</func>
<func>
- <name>erlang:append_element(Tuple1, Term) -> Tuple2</name>
+ <name name="append_element" arity="2"/>
<fsummary>Append an extra element to a tuple</fsummary>
- <type>
- <v>Tuple1 = Tuple2 = tuple()</v>
- <v>Term = term()</v>
- </type>
<desc>
<p>Returns a new tuple which has one element more than
- <c>Tuple1</c>, and contains the elements in <c>Tuple1</c>
- followed by <c>Term</c> as the last element. Semantically
+ <c><anno>Tuple1</anno></c>, and contains the elements in <c><anno>Tuple1</anno></c>
+ followed by <c><anno>Term</anno></c> as the last element. Semantically
equivalent to
- <c>list_to_tuple(tuple_to_list(Tuple) ++ [Term])</c>, but much
+ <c>list_to_tuple(tuple_to_list(<anno>Tuple1</anno>) ++ [<anno>Term</anno>])</c>, but much
faster.</p>
<pre>
> <input>erlang:append_element({one, two}, three).</input>
@@ -204,27 +189,24 @@
</desc>
</func>
<func>
- <name>atom_to_binary(Atom, Encoding) -> binary()</name>
+ <name name="atom_to_binary" arity="2"/>
<fsummary>Return the binary representation of an atom</fsummary>
- <type>
- <v>Atom = atom()</v>
- <v>Encoding = latin1 | utf8 | unicode</v>
- </type>
<desc>
<p>Returns a binary which corresponds to the text
- representation of <c>Atom</c>. If <c>Encoding</c>
+ representation of <c><anno>Atom</anno></c>. If <c><anno>Encoding</anno></c>
is <c>latin1</c>, there will be one byte for each character
- in the text representation. If <c>Encoding</c> is <c>utf8</c> or
+ in the text representation. If <c><anno>Encoding</anno></c> is
+ <c>utf8</c> or
<c>unicode</c>, the characters will be encoded using UTF-8
(meaning that characters from 16#80 up to 0xFF will be
encoded in two bytes).</p>
- <note><p>Currently, <c>atom_to_binary(Atom, latin1)</c> can
+ <note><p>Currently, <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> can
never fail because the text representation of an atom can only contain
characters from 0 to 16#FF. In a future release, the text representation
of atoms might be allowed to contain any Unicode character
- and <c>atom_to_binary(Atom, latin1)</c> will fail if the
- text representation for the <c>Atom</c> contains a Unicode
+ and <c>atom_to_binary(<anno>Atom</anno>, latin1)</c> will fail if the
+ text representation for the <c><anno>Atom</anno></c> contains a Unicode
character greater than 16#FF.</p></note>
<pre>
@@ -233,30 +215,21 @@
</desc>
</func>
<func>
- <name>atom_to_list(Atom) -> string()</name>
+ <name name="atom_to_list" arity="1"/>
<fsummary>Text representation of an atom</fsummary>
- <type>
- <v>Atom = atom()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Atom</c>.</p>
+ representation of <c><anno>Atom</anno></c>.</p>
<pre>
> <input>atom_to_list('Erlang').</input>
"Erlang"</pre>
</desc>
</func>
<func>
- <name>binary_part(Subject, PosLen) -> binary()</name>
+ <name name="binary_part" arity="2"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>PosLen = {Start,Length}</v>
- <v>Start = integer() >= 0</v>
- <v>Length = integer() >= 0</v>
- </type>
- <desc>
- <p>Extracts the part of the binary described by <c>PosLen</c>.</p>
+ <desc>
+ <p>Extracts the part of the binary described by <c><anno>PosLen</anno></c>.</p>
<p>Negative length can be used to extract bytes at the end of a binary:</p>
@@ -266,54 +239,47 @@
&lt;&lt;6,7,8,9,10&gt;&gt;
</code>
- <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
- <p><c>Start</c> is zero-based, i.e.:</p>
+ <p><c><anno>Start</anno></c> is zero-based, i.e.:</p>
<code>
1> Bin = &lt;&lt;1,2,3&gt;&gt;
2> binary_part(Bin,{0,2}).
&lt;&lt;1,2&gt;&gt;
</code>
- <p>See the STDLIB module <c>binary</c> for details about the <c>PosLen</c> semantics.</p>
+ <p>See the STDLIB module <c>binary</c> for details about the <c><anno>PosLen</anno></c> semantics.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>binary_part(Subject, Start, Length) -> binary()</name>
+ <name name="binary_part" arity="3"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Start = integer() >= 0</v>
- <v>Length = integer() >= 0</v>
- </type>
<desc>
- <p>The same as <c>binary_part(Subject, {Pos, Len})</c>.</p>
+ <p>The same as <c>binary_part(<anno>Subject</anno>, {<anno>Start</anno>, <anno>Length</anno>})</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>binary_to_atom(Binary, Encoding) -> atom()</name>
+ <name name="binary_to_atom" arity="2"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>Binary = binary()</v>
- <v>Encoding = latin1 | utf8 | unicode</v>
- </type>
<desc>
<p>Returns the atom whose text representation is
- <c>Binary</c>. If <c>Encoding</c> is <c>latin1</c>, no
- translation of bytes in the binary is done. If <c>Encoding</c>
+ <c><anno>Binary</anno></c>. If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
+ translation of bytes in the binary is done. If <c><anno>Encoding</anno></c>
is <c>utf8</c> or <c>unicode</c>, the binary must contain
valid UTF-8 sequences; furthermore, only Unicode characters up
to 0xFF are allowed.</p>
- <note><p><c>binary_to_atom(Binary, utf8)</c> will fail if
+ <note><p><c>binary_to_atom(<anno>Binary</anno>, utf8)</c> will fail if
the binary contains Unicode characters greater than 16#FF.
In a future release, such Unicode characters might be allowed
- and <c>binary_to_atom(Binary, utf8)</c>
- will not fail in that case.</p></note>
+ and <c>binary_to_atom(<anno>Binary</anno>, utf8)</c>
+ 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>
@@ -325,12 +291,8 @@
</desc>
</func>
<func>
- <name>binary_to_existing_atom(Binary, Encoding) -> atom()</name>
+ <name name="binary_to_existing_atom" arity="2"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>Binary = binary()</v>
- <v>Encoding = latin1 | utf8 | unicode</v>
- </type>
<desc>
<p>Works like <seealso marker="#binary_to_atom/2">binary_to_atom/2</seealso>,
but the atom must already exist.</p>
@@ -338,27 +300,59 @@
</desc>
</func>
<func>
- <name>binary_to_list(Binary) -> [char()]</name>
+ <name name="binary_to_float" arity="1"/>
+ <fsummary>Convert from text representation to a float</fsummary>
+ <desc>
+ <p>Returns the float whose text representation is <c><anno>Binary</anno></c>.</p>
+ <pre>
+> <input>binary_to_float(&lt;&lt;"2.2017764e+0">>).</input>
+2.2017764</pre>
+ <p>Failure: <c>badarg</c> if <c><anno>Binary</anno></c> contains a bad
+ representation of a float.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="binary_to_integer" arity="1"/>
+ <fsummary>Convert from text representation to an integer</fsummary>
+ <desc>
+ <p>Returns an integer whose text representation is
+ <c><anno>Binary</anno></c>.</p>
+ <pre>
+> <input>binary_to_integer(&lt;&lt;"123"&gt;&gt;).</input>
+123</pre>
+ <p>Failure: <c>badarg</c> if <c><anno>Binary</anno></c> contains a bad
+ representation of an integer.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="binary_to_integer" arity="2"/>
+ <fsummary>Convert from text representation to an integer</fsummary>
+ <desc>
+ <p>Returns an integer whose text representation in base
+ <c><anno>Base</anno></c> is <c><anno>Binary</anno></c>.</p>
+ <pre>
+> <input>binary_to_integer(&lt;&lt;"3FF"&gt;&gt;, 16).</input>
+1023</pre>
+ <p>Failure: <c>badarg</c> if <c><anno>Binary</anno></c> contains a bad
+ representation of an integer.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="binary_to_list" arity="1"/>
<fsummary>Convert a binary to a list</fsummary>
- <type>
- <v>Binary = binary()</v>
- </type>
<desc>
<p>Returns a list of integers which correspond to the bytes of
- <c>Binary</c>.</p>
+ <c><anno>Binary</anno></c>.</p>
</desc>
</func>
<func>
- <name>binary_to_list(Binary, Start, Stop) -> [char()]</name>
+ <name name="binary_to_list" arity="3"/>
<fsummary>Convert part of a binary to a list</fsummary>
- <type>
- <v>Binary = binary()</v>
- <v>Start = Stop = 1..byte_size(Binary)</v>
- </type>
+ <type_desc variable="Start">1..byte_size(<anno>Binary</anno>)</type_desc>
<desc>
<p>As <c>binary_to_list/1</c>, but returns a list of integers
- corresponding to the bytes from position <c>Start</c> to
- position <c>Stop</c> in <c>Binary</c>. Positions in the
+ corresponding to the bytes from position <c><anno>Start</anno></c> to
+ position <c><anno>Stop</anno></c> in <c><anno>Binary</anno></c>. Positions in the
binary are numbered starting from 1.</p>
<note><p>This function's indexing style of using one-based indices for
@@ -368,27 +362,21 @@
</desc>
</func>
<func>
- <name>bitstring_to_list(Bitstring) -> [char()|bitstring()]</name>
+ <name name="bitstring_to_list" arity="1"/>
<fsummary>Convert a bitstring to a list</fsummary>
- <type>
- <v>Bitstring = bitstring()</v>
- </type>
<desc>
<p>Returns a list of integers which correspond to the bytes of
- <c>Bitstring</c>. If the number of bits in the binary is not
+ <c><anno>Bitstring</anno></c>. If the number of bits in the binary is not
divisible by 8, the last element of the list will be a bitstring
containing the remaining bits (1 up to 7 bits).</p>
</desc>
</func>
<func>
- <name>binary_to_term(Binary) -> term()</name>
+ <name name="binary_to_term" arity="1"/>
<fsummary>Decode an Erlang external term format binary</fsummary>
- <type>
- <v>Binary = <seealso marker="#type-ext_binary">ext_binary()</seealso></v>
- </type>
<desc>
<p>Returns an Erlang term which is the result of decoding
- the binary object <c>Binary</c>, which must be encoded
+ the binary object <c><anno>Binary</anno></c>, which must be encoded
according to the Erlang external term format.</p>
<warning>
<p>When decoding binaries from untrusted sources, consider using
@@ -401,12 +389,8 @@
</desc>
</func>
<func>
- <name>binary_to_term(Binary, Opts) -> term()</name>
+ <name name="binary_to_term" arity="2"/>
<fsummary>Decode an Erlang external term format binary</fsummary>
- <type>
- <v>Opts = [safe]</v>
- <v>Binary = <seealso marker="#type-ext_binary">ext_binary()</seealso></v>
- </type>
<desc>
<p>As <c>binary_to_term/1</c>, but takes options that affect decoding
of the binary.</p>
@@ -436,13 +420,10 @@
</desc>
</func>
<func>
- <name>bit_size(Bitstring) -> integer() >= 0</name>
+ <name name="bit_size" arity="1"/>
<fsummary>Return the size of a bitstring</fsummary>
- <type>
- <v>Bitstring = bitstring()</v>
- </type>
<desc>
- <p>Returns an integer which is the size in bits of <c>Bitstring</c>.</p>
+ <p>Returns an integer which is the size in bits of <c><anno>Bitstring</anno></c>.</p>
<pre>
> <input>bit_size(&lt;&lt;433:16,3:3&gt;&gt;).</input>
19
@@ -452,11 +433,8 @@
</desc>
</func>
<func>
- <name>erlang:bump_reductions(Reductions) -> void()</name>
+ <name name="bump_reductions" arity="1"/>
<fsummary>Increment the reduction counter</fsummary>
- <type>
- <v>Reductions = integer() >= 0</v>
- </type>
<desc>
<p>This implementation-dependent function increments
the reduction counter for the calling process. In the Beam
@@ -472,14 +450,11 @@
</desc>
</func>
<func>
- <name>byte_size(Bitstring) -> integer() >= 0</name>
+ <name name="byte_size" arity="1"/>
<fsummary>Return the size of a bitstring (or binary)</fsummary>
- <type>
- <v>Bitstring = bitstring()</v>
- </type>
<desc>
<p>Returns an integer which is the number of bytes needed to contain
- <c>Bitstring</c>. (That is, if the number of bits in <c>Bitstring</c> is not
+ <c><anno>Bitstring</anno></c>. (That is, if the number of bits in <c><anno>Bitstring</anno></c> is not
divisible by 8, the resulting number of bytes will be rounded <em>up</em>.)</p>
<pre>
> <input>byte_size(&lt;&lt;433:16,3:3&gt;&gt;).</input>
@@ -490,21 +465,17 @@
</desc>
</func>
<func>
- <name>erlang:cancel_timer(TimerRef) -> Time | false</name>
+ <name name="cancel_timer" arity="1"/>
<fsummary>Cancel a timer</fsummary>
- <type>
- <v>TimerRef = reference()</v>
- <v>Time = integer() >= 0</v>
- </type>
<desc>
- <p>Cancels a timer, where <c>TimerRef</c> was returned by
+ <p>Cancels a timer, where <c><anno>TimerRef</anno></c> was returned by
either
<seealso marker="#send_after/3">erlang:send_after/3</seealso>
or
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>.
If the timer is there to be removed, the function returns
the time in milliseconds left until the timer would have expired,
- otherwise <c>false</c> (which means that <c>TimerRef</c> was
+ otherwise <c>false</c> (which means that <c><anno>TimerRef</anno></c> was
never a timer, that it has already been cancelled, or that it
has already delivered its message).</p>
<p>See also
@@ -518,27 +489,20 @@
</func>
<func>
- <name>check_old_code(Module) -> boolean()</name>
+ <name name="check_old_code" arity="1"/>
<fsummary>Check if a module has old code</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the <c>Module</c> has old code,
+ <p>Returns <c>true</c> if the <c><anno>Module</anno></c> has old code,
and <c>false</c> otherwise.</p>
<p>See also <seealso marker="kernel:code">code(3)</seealso>.</p>
</desc>
</func>
<func>
- <name>check_process_code(Pid, Module) -> boolean()</name>
+ <name name="check_process_code" arity="2"/>
<fsummary>Check if a process is executing old code for a module</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the process <c>Pid</c> is executing
- old code for <c>Module</c>. That is, if the current call of
+ <p>Returns <c>true</c> if the process <c><anno>Pid</anno></c> is executing
+ old code for <c><anno>Module</anno></c>. That is, if the current call of
the process executes old code for this module, or if the
process has references to old code for this module, or if the
process contains funs that references old code for this
@@ -550,26 +514,19 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:crc32(Data) -> integer() >= 0</name>
+ <name name="crc32" arity="1"/>
<fsummary>Compute crc32 (IEEE 802.3) checksum</fsummary>
- <type>
- <v>Data = iodata()</v>
- </type>
<desc>
- <p>Computes and returns the crc32 (IEEE 802.3 style) checksum for <c>Data</c>.</p>
+ <p>Computes and returns the crc32 (IEEE 802.3 style) checksum for <c><anno>Data</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:crc32(OldCrc, Data) -> integer() >= 0</name>
+ <name name="crc32" arity="2"/>
<fsummary>Compute crc32 (IEEE 802.3) checksum</fsummary>
- <type>
- <v>OldCrc = integer() >= 0</v>
- <v>Data = iodata()</v>
- </type>
<desc>
<p>Continue computing the crc32 checksum by combining
- the previous checksum, <c>OldCrc</c>, with the checksum of
- <c>Data</c>.</p>
+ the previous checksum, <c><anno>OldCrc</anno></c>, with the checksum of
+ <c><anno>Data</anno></c>.</p>
<p>The following code:</p>
<code>
X = erlang:crc32(Data1),
@@ -582,12 +539,8 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:crc32_combine(FirstCrc, SecondCrc, SecondSize) -> integer() >= 0</name>
+ <name name="crc32_combine" arity="3"/>
<fsummary>Combine two crc32 (IEEE 802.3) checksums</fsummary>
- <type>
- <v>FirstCrc = SecondCrc = integer() >= 0</v>
- <v>SecondSize = integer() >= 0</v>
- </type>
<desc>
<p>Combines two previously computed crc32 checksums.
This computation requires the size of the data object for
@@ -606,11 +559,8 @@ false</pre>
</desc>
</func>
<func>
- <name>date() -> Date</name>
+ <name name="date" arity="0"/>
<fsummary>Current date</fsummary>
- <type>
- <v>Date = <seealso marker="calendar#type-date">calendar:date()</seealso></v>
- </type>
<desc>
<p>Returns the current date as <c>{Year, Month, Day}</c>.</p>
<p>The time zone and daylight saving time correction depend on
@@ -621,47 +571,24 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:decode_packet(Type,Bin,Options) -> {ok,Packet,Rest} | {more,Length} | {error,Reason}</name>
+ <name name="decode_packet" arity="3"/>
<fsummary>Extracts a protocol packet from a binary</fsummary>
- <type>
- <v>Bin = binary()</v>
- <v>Options = [Opt]</v>
- <v>Packet = binary() | HttpPacket</v>
- <v>Rest = binary()</v>
- <v>Length = integer() > 0 | undefined</v>
- <v>Reason = term()</v>
- <v>&nbsp;Type, Opt -- see below</v>
- <v></v>
- <v>HttpPacket = HttpRequest | HttpResponse | HttpHeader | http_eoh | HttpError</v>
- <v>HttpRequest = {http_request, HttpMethod, HttpUri, HttpVersion}</v>
- <v>HttpResponse = {http_response, HttpVersion, integer(), HttpString}</v>
- <v>HttpHeader = {http_header, integer(), HttpField, Reserved=term(), Value=HttpString}</v>
- <v>HttpError = {http_error, HttpString}</v>
- <v>HttpMethod = HttpMethodAtom | HttpString</v>
- <v>HttpMethodAtom = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE'</v>
- <v>HttpUri = '*' | {absoluteURI, http|https, Host=HttpString, Port=integer()|undefined, Path=HttpString} |
- {scheme, Scheme=HttpString, HttpString} | {abs_path, HttpString} | HttpString</v>
- <v>HttpVersion = {Major=integer(), Minor=integer()}</v>
- <v>HttpString = string() | binary()</v>
- <v>HttpField = HttpFieldAtom | HttpString</v>
- <v>HttpFieldAtom = 'Cache-Control' | 'Connection' | 'Date' | 'Pragma' | 'Transfer-Encoding' | 'Upgrade' | 'Via' | 'Accept' | 'Accept-Charset' | 'Accept-Encoding' | 'Accept-Language' | 'Authorization' | 'From' | 'Host' | 'If-Modified-Since' | 'If-Match' | 'If-None-Match' | 'If-Range' | 'If-Unmodified-Since' | 'Max-Forwards' | 'Proxy-Authorization' | 'Range' | 'Referer' | 'User-Agent' | 'Age' | 'Location' | 'Proxy-Authenticate' | 'Public' | 'Retry-After' | 'Server' | 'Vary' | 'Warning' | 'Www-Authenticate' | 'Allow' | 'Content-Base' | 'Content-Encoding' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 'Content-Md5' | 'Content-Range' | 'Content-Type' | 'Etag' | 'Expires' | 'Last-Modified' | 'Accept-Ranges' | 'Set-Cookie' | 'Set-Cookie2' | 'X-Forwarded-For' | 'Cookie' | 'Keep-Alive' | 'Proxy-Connection'</v>
- <v></v>
- </type>
- <desc>
- <p>Decodes the binary <c>Bin</c> according to the packet
- protocol specified by <c>Type</c>. Very similar to the packet
- handling done by sockets with the option {packet,Type}.</p>
- <p>If an entire packet is contained in <c>Bin</c> it is
+ <desc>
+
+ <p>Decodes the binary <c><anno>Bin</anno></c> according to the packet
+ protocol specified by <c><anno>Type</anno></c>. Very similar to the packet
+ handling done by sockets with the option {packet,<anno>Type</anno>}.</p>
+ <p>If an entire packet is contained in <c><anno>Bin</anno></c> it is
returned together with the remainder of the binary as
- <c>{ok,Packet,Rest}</c>.</p>
- <p>If <c>Bin</c> does not contain the entire packet,
- <c>{more,Length}</c> is returned. <c>Length</c> is either the
+ <c>{ok,<anno>Packet</anno>,<anno>Rest</anno>}</c>.</p>
+ <p>If <c><anno>Bin</anno></c> does not contain the entire packet,
+ <c>{more,<anno>Length</anno>}</c> is returned. <c><anno>Length</anno></c> is either the
expected <em>total size</em> of the packet or <c>undefined</c>
if the expected packet size is not known. <c>decode_packet</c>
can then be called again with more data added.</p>
<p>If the packet does not conform to the protocol format
- <c>{error,Reason}</c> is returned.</p>
- <p>The following values of <c>Type</c> are valid:</p>
+ <c>{error,<anno>Reason</anno>}</c> is returned.</p>
+ <p>The following values of <c><anno>Type</anno></c> are valid:</p>
<taglist>
<tag><c>raw | 0</c></tag>
<item>
@@ -699,15 +626,17 @@ false</pre>
<item>
<p>The Hypertext Transfer Protocol. The packets
are returned with the format according to
- <c>HttpPacket</c> described above. A packet is either a
+ <c><anno>HttpPacket</anno></c> described above. A packet is either a
request, a response, a header or an end of header
- mark. Invalid lines are returned as <c>HttpError</c>.</p>
+ mark. Invalid lines are returned as <c><anno>HttpError</anno></c>.</p>
<p>Recognized request methods and header fields are returned as atoms.
- Others are returned as strings.</p>
+ Others are returned as strings. Strings of unrecognized header fields
+ are formatted with only capital letters first and after hyphen characters
+ (like <c>"Sec-Websocket-Key"</c>).</p>
<p>The protocol type <c>http</c> should only be used for
- the first line when a <c>HttpRequest</c> or a
- <c>HttpResponse</c> is expected. The following calls
- should use <c>httph</c> to get <c>HttpHeader</c>'s until
+ the first line when a <c><anno>HttpRequest</anno></c> or a
+ <c><anno>HttpResponse</anno></c> is expected. The following calls
+ should use <c>httph</c> to get <c><anno>HttpHeader</anno></c>'s until
<c>http_eoh</c> is returned that marks the end of the
headers and the beginning of any following message body.</p>
<p>The variants <c>http_bin</c> and <c>httph_bin</c> will return
@@ -716,14 +645,14 @@ false</pre>
</taglist>
<p>The following options are available:</p>
<taglist>
- <tag><c>{packet_size, integer()}</c></tag>
+ <tag><c>{packet_size, integer() >= 0}</c></tag>
<item><p>Sets the max allowed size of the packet body. If
the packet header indicates that the length of the
packet is longer than the max allowed length, the packet
is considered invalid. Default is 0 which means no
size limit.</p>
</item>
- <tag><c>{line_length, integer()}</c></tag>
+ <tag><c>{line_length, integer() >= 0}</c></tag>
<item><p>For packet type <c>line</c>, truncate lines longer
than the indicated length.</p>
<p>Option <c>line_length</c> also applies to <c>http*</c>
@@ -739,14 +668,27 @@ false</pre>
{more,6}</pre>
</desc>
</func>
+
<func>
- <name>delete_module(Module) -> true | undefined</name>
+ <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>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Makes the current code for <c>Module</c> become old code, and
+ <p>Makes the current code for <c><anno>Module</anno></c> become old code, and
deletes all references for this module from the export table.
Returns <c>undefined</c> if the module does not exist,
otherwise <c>true</c>.</p>
@@ -760,27 +702,24 @@ false</pre>
</desc>
</func>
<func>
- <name>demonitor(MonitorRef) -> true</name>
+ <name name="demonitor" arity="1"/>
<fsummary>Stop monitoring</fsummary>
- <type>
- <v>MonitorRef = reference()</v>
- </type>
<desc>
- <p>If <c>MonitorRef</c> is a reference which the calling process
+ <p>If <c><anno>MonitorRef</anno></c> is a reference which the calling process
obtained by calling
<seealso marker="#monitor/2">monitor/2</seealso>,
this monitoring is turned off. If the monitoring is already
turned off, nothing happens.</p>
- <p>Once <c>demonitor(MonitorRef)</c> has returned it is
- guaranteed that no <c>{'DOWN', MonitorRef, _, _, _}</c> message
+ <p>Once <c>demonitor(<anno>MonitorRef</anno>)</c> has returned it is
+ guaranteed that no <c>{'DOWN', <anno>MonitorRef</anno>, _, _, _}</c> message
due to the monitor will be placed in the caller's message queue
- in the future. A <c>{'DOWN', MonitorRef, _, _, _}</c> message
+ in the future. A <c>{'DOWN', <anno>MonitorRef</anno>, _, _, _}</c> message
might have been placed in the caller's message queue prior to
the call, though. Therefore, in most cases, it is advisable
to remove such a <c>'DOWN'</c> message from the message queue
after monitoring has been stopped.
- <seealso marker="#demonitor/2">demonitor(MonitorRef, [flush])</seealso> can be used instead of
- <c>demonitor(MonitorRef)</c> if this cleanup is wanted.</p>
+ <seealso marker="#demonitor/2">demonitor(<anno>MonitorRef</anno>, [flush])</seealso> can be used instead of
+ <c>demonitor(<anno>MonitorRef</anno>)</c> if this cleanup is wanted.</p>
<note>
<p>Prior to OTP release R11B (erts version 5.5) <c>demonitor/1</c>
behaved completely asynchronous, i.e., the monitor was active
@@ -792,35 +731,30 @@ false</pre>
asynchronously send a "demonitor signal" to the monitored entity
and ignore any future results of the monitor. </p>
</note>
- <p>Failure: It is an error if <c>MonitorRef</c> refers to a
+ <p>Failure: It is an error if <c><anno>MonitorRef</anno></c> refers to a
monitoring started by another process. Not all such cases are
cheap to check; if checking is cheap, the call fails with
- <c>badarg</c> (for example if <c>MonitorRef</c> is a remote
+ <c>badarg</c> (for example if <c><anno>MonitorRef</anno></c> is a remote
reference).</p>
</desc>
</func>
<func>
- <name>demonitor(MonitorRef, OptionList) -> boolean()</name>
+ <name name="demonitor" arity="2"/>
<fsummary>Stop monitoring</fsummary>
- <type>
- <v>MonitorRef = reference()</v>
- <v>OptionList = [Option]</v>
- <v>&nbsp;Option = flush | info</v>
- </type>
<desc>
<p>The returned value is <c>true</c> unless <c>info</c> is part
- of <c>OptionList</c>.
+ of <c><anno>OptionList</anno></c>.
</p>
- <p><c>demonitor(MonitorRef, [])</c> is equivalent to
- <seealso marker="#demonitor/1">demonitor(MonitorRef)</seealso>.</p>
- <p>Currently the following <c>Option</c>s are valid:</p>
+ <p><c>demonitor(<anno>MonitorRef</anno>, [])</c> is equivalent to
+ <seealso marker="#demonitor/1">demonitor(<anno>MonitorRef</anno>)</seealso>.</p>
+ <p>Currently the following <c><anno>Option</anno></c>s are valid:</p>
<taglist>
<tag><c>flush</c></tag>
<item>
- <p>Remove (one) <c>{_, MonitorRef, _, _, _}</c> message,
+ <p>Remove (one) <c>{_, <anno>MonitorRef</anno>, _, _, _}</c> message,
if there is one, from the caller's message queue after
monitoring has been stopped.</p>
- <p>Calling <c>demonitor(MonitorRef, [flush])</c>
+ <p>Calling <c>demonitor(<anno>MonitorRef</anno>, [flush])</c>
is equivalent to the following, but more efficient:</p>
<code type="none">
@@ -860,8 +794,8 @@ false</pre>
<note>
<p>More options may be added in the future.</p>
</note>
- <p>Failure: <c>badarg</c> if <c>OptionList</c> is not a list, or
- if <c>Option</c> is not a valid option, or the same failure as for
+ <p>Failure: <c>badarg</c> if <c><anno>OptionList</anno></c> is not a list, or
+ if <c><anno>Option</anno></c> is not a valid option, or the same failure as for
<seealso marker="#demonitor/1">demonitor/1</seealso></p>
</desc>
</func>
@@ -878,13 +812,10 @@ false</pre>
</desc>
</func>
<func>
- <name>erlang:display(Term) -> true</name>
+ <name name="display" arity="1"/>
<fsummary>Print a term on standard output</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Prints a text representation of <c>Term</c> on the standard
+ <p>Prints a text representation of <c><anno>Term</anno></c> on the standard
output.</p>
<warning>
<p>This BIF is intended for debugging only.</p>
@@ -892,15 +823,12 @@ false</pre>
</desc>
</func>
<func>
- <name>element(N, Tuple) -> term()</name>
+ <name name="element" arity="2"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Get Nth element of a tuple</fsummary>
- <type>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>Tuple = tuple()</v>
- </type>
<desc>
- <p>Returns the <c>N</c>th element (numbering from 1) of
- <c>Tuple</c>.</p>
+ <p>Returns the <c><anno>N</anno></c>th element (numbering from 1) of
+ <c><anno>Tuple</anno></c>.</p>
<pre>
> <input>element(2, {a, b, c}).</input>
b</pre>
@@ -908,11 +836,8 @@ b</pre>
</desc>
</func>
<func>
- <name>erase() -> [{Key, Val}]</name>
+ <name name="erase" arity="0"/>
<fsummary>Return and delete the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
<p>Returns the process dictionary and deletes it.</p>
<pre>
@@ -923,15 +848,12 @@ b</pre>
</desc>
</func>
<func>
- <name>erase(Key) -> Val | undefined</name>
+ <name name="erase" arity="1"/>
<fsummary>Return and delete a value from the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
- <p>Returns the value <c>Val</c> associated with <c>Key</c> and
+ <p>Returns the value <c><anno>Val</anno></c> associated with <c><anno>Key</anno></c> and
deletes it from the process dictionary. Returns
- <c>undefined</c> if no value is associated with <c>Key</c>.</p>
+ <c>undefined</c> if no value is associated with <c><anno>Key</anno></c>.</p>
<pre>
> <input>put(key1, {merry, lambs, are, playing}),</input>
<input>X = erase(key1),</input>
@@ -940,15 +862,12 @@ b</pre>
</desc>
</func>
<func>
- <name>error(Reason)</name>
+ <name name="error" arity="1"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Stops the execution of the calling process with the reason
- <c>Reason</c>, where <c>Reason</c> is any term. The actual
- exit reason will be <c>{Reason, Where}</c>, where <c>Where</c>
+ <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. The actual
+ exit reason will be <c>{<anno>Reason</anno>, Where}</c>, where <c>Where</c>
is a list of the functions most recently called (the current
function first). Since evaluating this function causes
the process to terminate, it has no return value.</p>
@@ -962,18 +881,14 @@ b</pre>
</desc>
</func>
<func>
- <name>error(Reason, Args)</name>
+ <name name="error" arity="2"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Stops the execution of the calling process with the reason
- <c>Reason</c>, where <c>Reason</c> is any term. The actual
- exit reason will be <c>{Reason, Where}</c>, where <c>Where</c>
+ <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. The actual
+ exit reason will be <c>{<anno>Reason</anno>, Where}</c>, where <c>Where</c>
is a list of the functions most recently called (the current
- function first). <c>Args</c> is expected to be the list of
+ function first). <c><anno>Args</anno></c> is expected to be the list of
arguments for the current function; in Beam it will be used
to provide the actual arguments for the current function in
the <c>Where</c> term. Since evaluating this function causes
@@ -981,14 +896,11 @@ b</pre>
</desc>
</func>
<func>
- <name>exit(Reason)</name>
+ <name name="exit" arity="1"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Stops the execution of the calling process with the exit
- reason <c>Reason</c>, where <c>Reason</c> is any term. Since
+ reason <c><anno>Reason</anno></c>, where <c><anno>Reason</anno></c> is any term. Since
evaluating this function causes the process to terminate, it
has no return value.</p>
<pre>
@@ -999,78 +911,67 @@ b</pre>
</desc>
</func>
<func>
- <name>exit(Pid, Reason) -> true</name>
- <fsummary>Send an exit signal to a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = term()</v>
- </type>
+ <name name="exit" arity="2"/>
+ <fsummary>Send an exit signal to a process or a port</fsummary>
<desc>
- <p>Sends an exit signal with exit reason <c>Reason</c> to
- the process <c>Pid</c>.</p>
- <p>The following behavior apply if <c>Reason</c> is any term
+ <p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to
+ 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>Pid</c> is not trapping exits, <c>Pid</c> itself will
- exit with exit reason <c>Reason</c>. If <c>Pid</c> is trapping
+ <p>If <c><anno>Pid</anno></c> is not trapping exits, <c><anno>Pid</anno></c> itself will
+ exit with exit reason <c><anno>Reason</anno></c>. If <c><anno>Pid</anno></c> is trapping
exits, the exit signal is transformed into a message
- <c>{'EXIT', From, Reason}</c> and delivered to the message
- queue of <c>Pid</c>. <c>From</c> is the pid of the process
+ <c>{'EXIT', From, <anno>Reason</anno>}</c> and delivered to the message
+ queue of <c><anno>Pid</anno></c>. <c>From</c> is the pid of the process
which sent the exit signal. See also
<seealso marker="#process_flag/2">process_flag/2</seealso>.</p>
- <p>If <c>Reason</c> is the atom <c>normal</c>, <c>Pid</c> will
+ <p>If <c><anno>Reason</anno></c> is the atom <c>normal</c>, <c><anno>Pid</anno></c> will
not exit. If it is trapping exits, the exit signal is
transformed into a message <c>{'EXIT', From, normal}</c>
and delivered to its message queue.</p>
- <p>If <c>Reason</c> is the atom <c>kill</c>, that is if
- <c>exit(Pid, kill)</c> is called, an untrappable exit signal
- is sent to <c>Pid</c> which will unconditionally exit with
+ <p>If <c><anno>Reason</anno></c> is the atom <c>kill</c>, that is if
+ <c>exit(<anno>Pid</anno>, kill)</c> is called, an untrappable exit signal
+ is sent to <c><anno>Pid</anno></c> which will unconditionally exit with
exit reason <c>killed</c>.</p>
</desc>
</func>
<func>
- <name>erlang:external_size(Term) -> integer() >= 0</name>
+ <name name="external_size" arity="1"/>
<fsummary>Calculate the maximum size for a term encoded in the Erlang
external term format</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
condition applies always:</p>
<p>
<pre>
-> <input>Size1 = byte_size(term_to_binary(Term)),</input>
-> <input>Size2 = erlang:external_size(Term),</input>
+> <input>Size1 = byte_size(term_to_binary(<anno>Term</anno>)),</input>
+> <input>Size2 = erlang:external_size(<anno>Term</anno>),</input>
> <input>true = Size1 =&lt; Size2.</input>
true
</pre>
</p>
- <p>This is equivalent to a call to: <code>erlang:external_size(Term, [])
+ <p>This is equivalent to a call to: <code>erlang:external_size(<anno>Term</anno>, [])
</code></p>
</desc>
</func>
<func>
- <name>erlang:external_size(Term, [Option]) -> integer() >= 0</name>
+ <name name="external_size" arity="2"/>
<fsummary>Calculate the maximum size for a term encoded in the Erlang
external term format</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Option = {minor_version, Version}</v>
- </type>
<desc>
<p>Calculates, without doing the encoding, the maximum byte size for
a term encoded in the Erlang external term format. The following
condition applies always:</p>
<p>
<pre>
-> <input>Size1 = byte_size(term_to_binary(Term, Options)),</input>
-> <input>Size2 = erlang:external_size(Term, Options),</input>
+> <input>Size1 = byte_size(term_to_binary(<anno>Term</anno>, <anno>Options</anno>)),</input>
+> <input>Size2 = erlang:external_size(<anno>Term</anno>, <anno>Options</anno>),</input>
> <input>true = Size1 =&lt; Size2.</input>
true
</pre>
</p>
- <p>The option <c>{minor_version, Version}</c> specifies how floats
+ <p>The option <c>{minor_version, <anno>Version</anno>}</c> specifies how floats
are encoded. See
<seealso marker="#term_to_binary/2">term_to_binary/2</seealso> for
a more detailed description.
@@ -1078,13 +979,10 @@ true
</desc>
</func>
<func>
- <name>float(Number) -> float()</name>
+ <name name="float" arity="1"/>
<fsummary>Convert a number to a float</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
- <p>Returns a float by converting <c>Number</c> to a float.</p>
+ <p>Returns a float by converting <c><anno>Number</anno></c> to a float.</p>
<pre>
> <input>float(55).</input>
55.0</pre>
@@ -1101,17 +999,58 @@ true
</desc>
</func>
<func>
- <name>float_to_list(Float) -> string()</name>
+ <name name="float_to_binary" arity="1"/>
+ <fsummary>Text representation of a float</fsummary>
+ <desc>
+ <p>The same as <c>float_to_binary(<anno>Float</anno>,[{scientific,20}])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="float_to_binary" arity="2"/>
+ <fsummary>Text representation of a float formatted using given options</fsummary>
+ <desc>
+ <p>Returns a binary which corresponds to the text
+ representation of <c><anno>Float</anno></c> using fixed decimal
+ point formatting. The <c><anno>Options</anno></c> behave in the same
+ way as <seealso marker="#float_to_list/2">float_to_list/2</seealso>.
+ </p>
+ <pre>
+> <input>float_to_binary(7.12, [{decimals, 4}]).</input>
+&lt;&lt;"7.1200">>
+> <input>float_to_binary(7.12, [{decimals, 4}, compact]).</input>
+&lt;&lt;"7.12">></pre>
+ </desc>
+ </func>
+ <func>
+ <name name="float_to_list" arity="1"/>
<fsummary>Text representation of a float</fsummary>
- <type>
- <v>Float = float()</v>
- </type>
+ <desc>
+ <p>The same as <c>float_to_list(<anno>Float</anno>,[{scientific,20}])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="float_to_list" arity="2"/>
+ <fsummary>Text representation of a float formatted using given options</fsummary>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Float</c>.</p>
+ 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><seealso marker="#float_to_list/1">float_to_list/1</seealso></c>.
+ </p>
<pre>
-> <input>float_to_list(7.0).</input>
-"7.00000000000000000000e+00"</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>
@@ -1213,18 +1152,15 @@ true
</desc>
</func>
<func>
- <name>erlang:fun_info(Fun, Item) -> {Item, Info}</name>
+ <name name="fun_info" arity="2"/>
+ <type name="fun_info_item"/>
<fsummary>Information about a fun</fsummary>
- <type>
- <v>Fun = fun()</v>
- <v>Item, Info -- see below</v>
- </type>
- <desc>
- <p>Returns information about <c>Fun</c> as specified by
- <c>Item</c>, in the form <c>{Item,Info}</c>.</p>
- <p>For any fun, <c>Item</c> can be any of the atoms
+ <desc>
+ <p>Returns information about <c><anno>Fun</anno></c> as specified by
+ <c><anno>Item</anno></c>, in the form <c>{<anno>Item</anno>,<anno>Info</anno>}</c>.</p>
+ <p>For any fun, <c><anno>Item</anno></c> can be any of the atoms
<c>module</c>, <c>name</c>, <c>arity</c>, <c>env</c>, or <c>type</c>.</p>
- <p>For a local fun, <c>Item</c> can also be any of the atoms
+ <p>For a local fun, <c><anno>Item</anno></c> can also be any of the atoms
<c>index</c>, <c>new_index</c>, <c>new_uniq</c>,
<c>uniq</c>, and <c>pid</c>. For an external fun, the value
of any of these items is always the atom <c>undefined</c>.</p>
@@ -1233,33 +1169,26 @@ true
</desc>
</func>
<func>
- <name>erlang:fun_to_list(Fun) -> string()</name>
+ <name name="fun_to_list" arity="1"/>
<fsummary>Text representation of a fun</fsummary>
- <type>
- <v>Fun = fun()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Fun</c>.</p>
+ representation of <c><anno>Fun</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:function_exported(Module, Function, Arity) -> boolean()</name>
+ <name name="function_exported" arity="3"/>
<fsummary>Check if a function is exported and loaded</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Arity = arity()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the module <c>Module</c> is loaded
- and contains an exported function <c>Function/Arity</c>;
+ <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded
+ and contains an exported function <c><anno>Function</anno>/<anno>Arity</anno></c>;
otherwise <c>false</c>.</p>
<p>Returns <c>false</c> for any BIF (functions implemented in C
rather than in Erlang).</p>
</desc>
</func>
<func>
- <name>garbage_collect() -> true</name>
+ <name name="garbage_collect" arity="0"/>
<fsummary>Force an immediate garbage collection of the calling process</fsummary>
<desc>
<p>Forces an immediate garbage collection of the currently
@@ -1276,26 +1205,20 @@ true
</desc>
</func>
<func>
- <name>garbage_collect(Pid) -> boolean()</name>
+ <name name="garbage_collect" arity="1"/>
<fsummary>Force an immediate garbage collection of a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>Works like <c>erlang:garbage_collect()</c> but on any
process. The same caveats apply. Returns <c>false</c> if
- <c>Pid</c> refers to a dead process; <c>true</c> otherwise.</p>
+ <c><anno>Pid</anno></c> refers to a dead process; <c>true</c> otherwise.</p>
</desc>
</func>
<func>
- <name>get() -> [{Key, Val}]</name>
+ <name name="get" arity="0"/>
<fsummary>Return the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
<p>Returns the process dictionary as a list of
- <c>{Key, Val}</c> tuples.</p>
+ <c>{<anno>Key</anno>, <anno>Val</anno>}</c> tuples.</p>
<pre>
> <input>put(key1, merry),</input>
<input>put(key2, lambs),</input>
@@ -1305,14 +1228,11 @@ true
</desc>
</func>
<func>
- <name>get(Key) -> Val | undefined</name>
+ <name name="get" arity="1"/>
<fsummary>Return a value from the process dictionary</fsummary>
- <type>
- <v>Key = Val = term()</v>
- </type>
<desc>
- <p>Returns the value <c>Val</c>associated with <c>Key</c> in
- the process dictionary, or <c>undefined</c> if <c>Key</c>
+ <p>Returns the value <c><anno>Val</anno></c>associated with <c><anno>Key</anno></c> in
+ the process dictionary, or <c>undefined</c> if <c><anno>Key</anno></c>
does not exist.</p>
<pre>
> <input>put(key1, merry),</input>
@@ -1331,14 +1251,11 @@ true
</desc>
</func>
<func>
- <name>get_keys(Val) -> [Key]</name>
+ <name name="get_keys" arity="1"/>
<fsummary>Return a list of keys from the process dictionary</fsummary>
- <type>
- <v>Val = Key = term()</v>
- </type>
<desc>
<p>Returns a list of keys which are associated with the value
- <c>Val</c> in the process dictionary.</p>
+ <c><anno>Val</anno></c> in the process dictionary.</p>
<pre>
> <input>put(mary, {1, 2}),</input>
<input>put(had, {1, 2}),</input>
@@ -1351,28 +1268,23 @@ true
</desc>
</func>
<func>
- <name>erlang:get_stacktrace() -> [{Module, Function, Arity | Args, Location}]</name>
+ <name name="get_stacktrace" arity="0"/>
<fsummary>Get the call stack back-trace of the last exception</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Arity = arity()</v>
- <v>Args = [term()]</v>
- <v>Location = [{atom(),term()}]</v>
- </type>
+ <type name="stack_item"/>
<desc>
<p>Get the call stack back-trace (<em>stacktrace</em>) of the last
exception in the calling process as a list of
- <c>{Module,Function,Arity,Location}</c> tuples.
- The <c>Arity</c> field in the first tuple may be the argument
+ <c>{<anno>Module</anno>,<anno>Function</anno>,<anno>Arity</anno>,<anno>Location</anno>}</c> tuples.
+ The <c><anno>Arity</anno></c> field in the first tuple may be the argument
list of that function call instead of an arity integer,
depending on the exception.</p>
<p>If there has not been any exceptions in a process, the
- stacktrace is []. After a code change for the process,
+ stacktrace is <c>[]</c>. After a code change for the process,
the stacktrace may also be reset to [].</p>
<p>The stacktrace is the same data as the <c>catch</c> operator
returns, for example:</p>
<p><c>{'EXIT',{badarg,Stacktrace}} = catch abs(x)</c></p>
- <p><c>Location</c> is a (possibly empty) list of two-tuples that
+ <p><c><anno>Location</anno></c> is a (possibly empty) list of two-tuples that
may indicate the location in the source code of the function.
The first element is an atom that describes the type of
information in the second element. Currently the following
@@ -1397,11 +1309,8 @@ true
</desc>
</func>
<func>
- <name>group_leader() -> GroupLeader</name>
+ <name name="group_leader" arity="0"/>
<fsummary>Get the group leader for the calling process</fsummary>
- <type>
- <v>GroupLeader = pid()</v>
- </type>
<desc>
<p>Returns the pid of the group leader for the process which
evaluates the function.</p>
@@ -1414,13 +1323,10 @@ true
</desc>
</func>
<func>
- <name>group_leader(GroupLeader, Pid) -> true</name>
+ <name name="group_leader" arity="2"/>
<fsummary>Set the group leader for a process</fsummary>
- <type>
- <v>GroupLeader = Pid = pid()</v>
- </type>
<desc>
- <p>Sets the group leader of <c>Pid</c> to <c>GroupLeader</c>.
+ <p>Sets the group leader of <c><anno>Pid</anno></c> to <c><anno>GroupLeader</anno></c>.
Typically, this is used when a processes started from a
certain shell should have another group leader than
<c>init</c>.</p>
@@ -1429,7 +1335,7 @@ true
</desc>
</func>
<func>
- <name>halt()</name>
+ <name name="halt" arity="0"/>
<fsummary>Halt the Erlang runtime system and indicate normal exit to the calling environment</fsummary>
<desc>
<p>The same as
@@ -1440,14 +1346,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>halt(Status)</name>
+ <name name="halt" arity="1"/>
<fsummary>Halt the Erlang runtime system</fsummary>
- <type>
- <v>Status = integer() >= 0 | string() | abort</v>
- </type>
<desc>
<p>The same as
- <seealso marker="#halt/2"><c>halt(Status, [])</c></seealso>.</p>
+ <seealso marker="#halt/2"><c>halt(<anno>Status</anno>, [])</c></seealso>.</p>
<pre>
> <input>halt(17).</input>
os_prompt% <input>echo $?</input>
@@ -1456,26 +1359,21 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>halt(Status, Options)</name>
+ <name name="halt" arity="2"/>
<fsummary>Halt the Erlang runtime system</fsummary>
- <type>
- <v>Status = integer() >= 0 | string() | abort</v>
- <v>Options = [Option]</v>
- <v>Option = {flush,boolean()} | term()</v>
- </type>
<desc>
- <p><c>Status</c> must be a non-negative integer, a string,
+ <p><c><anno>Status</anno></c> must be a non-negative integer, a string,
or the atom <c>abort</c>.
Halts the Erlang runtime system. Has no return value.
- Depending on <c>Status</c>:
+ Depending on <c><anno>Status</anno></c>:
</p>
<taglist>
<tag>integer()</tag>
- <item>The runtime system exits with the integer value <c>Status</c>
+ <item>The runtime system exits with the integer value <c><anno>Status</anno></c>
as status code to the calling environment (operating system).
</item>
<tag>string()</tag>
- <item>An erlang crash dump is produced with <c>Status</c> as slogan,
+ <item>An erlang crash dump is produced with <c><anno>Status</anno></c> as slogan,
and then the runtime system exits with status code <c>1</c>.
</item>
<tag><c>abort</c></tag>
@@ -1487,10 +1385,10 @@ os_prompt% </pre>
<p>Note that on many platforms, only the status codes 0-255 are
supported by the operating system.
</p>
- <p>For integer <c>Status</c> the Erlang runtime system closes all ports
+ <p>For integer <c><anno>Status</anno></c> the Erlang runtime system closes all ports
and allows async threads to finish their operations before exiting.
To exit without such flushing use
- <c>Option</c> as <c>{flush,false}</c>.
+ <c><anno>Option</anno></c> as <c>{flush,false}</c>.
</p>
<p>For statuses <c>string()</c> and <c>abort</c> the <c>flush</c>
option is ignored and flushing is <em>not</em> done.
@@ -1498,11 +1396,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:hash(Term, Range) -> Hash</name>
+ <name name="hash" arity="2"/>
<fsummary>Hash function (deprecated)</fsummary>
<desc>
- <p>Returns a hash value for <c>Term</c> within the range
- <c>1..Range</c>. The allowed range is 1..2^27-1.</p>
+ <p>Returns a hash value for <c><anno>Term</anno></c> within the range
+ <c>1..<anno>Range</anno></c>. The allowed range is 1..2^27-1.</p>
<warning>
<p>This BIF is deprecated as the hash value may differ on
different architectures. Also the hash values for integer
@@ -1515,35 +1413,28 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>hd(List) -> term()</name>
+ <name name="hd" arity="1"/>
<fsummary>Head of a list</fsummary>
- <type>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns the head of <c>List</c>, that is, the first element.</p>
+ <p>Returns the head of <c><anno>List</anno></c>, that is, the first element.</p>
<pre>
> <input>hd([1,2,3,4,5]).</input>
1</pre>
<p>Allowed in guard tests.</p>
- <p>Failure: <c>badarg</c> if <c>List</c> is the empty list [].</p>
+ <p>Failure: <c>badarg</c> if <c><anno>List</anno></c> is the empty list [].</p>
</desc>
</func>
<func>
- <name>erlang:hibernate(Module, Function, Args)</name>
+ <name name="hibernate" arity="3"/>
<fsummary>Hibernate a process until a message is sent to it</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Puts the calling process into a wait state where its memory
allocation has been reduced as much as possible, which is
useful if the process does not expect to receive any messages
in the near future.</p>
<p>The process will be awaken when a message is sent to it, and
- control will resume in <c>Module:Function</c> with
- the arguments given by <c>Args</c> with the call stack
+ control will resume in <c><anno>Module</anno>:<anno>Function</anno></c> with
+ the arguments given by <c><anno>Args</anno></c> with the call stack
emptied, meaning that the process will terminate when that
function returns. Thus <c>erlang:hibernate/3</c> will never
return to its caller.</p>
@@ -1572,15 +1463,51 @@ 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_binary" arity="1"/>
+ <fsummary>Text representation of an integer</fsummary>
+ <desc>
+ <p>Returns a binary which corresponds to the text
+ representation of <c><anno>Integer</anno></c>.</p>
+ <pre>
+> <input>integer_to_binary(77).</input>
+&lt;&lt;"77">></pre>
+ </desc>
+ </func>
+ <func>
+ <name name="integer_to_binary" arity="2"/>
+ <fsummary>Text representation of an integer</fsummary>
+ <desc>
+ <p>Returns a binary which corresponds to the text
+ representation of <c><anno>Integer</anno></c> in base <c><anno>Base</anno></c>.</p>
+ <pre>
+> <input>integer_to_binary(1023, 16).</input>
+&lt;&lt;"3FF">></pre>
+ </desc>
+ </func>
<func>
- <name>integer_to_list(Integer) -> string()</name>
+ <name name="integer_to_list" arity="1"/>
<fsummary>Text representation of an integer</fsummary>
- <type>
- <v>Integer = integer()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Integer</c>.</p>
+ representation of <c><anno>Integer</anno></c>.</p>
<pre>
> <input>integer_to_list(77).</input>
"77"</pre>
@@ -1598,14 +1525,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>iolist_to_binary(IoListOrBinary) -> binary()</name>
+ <name name="iolist_to_binary" arity="1"/>
<fsummary>Convert an iolist to a binary</fsummary>
- <type>
- <v>IoListOrBinary = iolist() | binary()</v>
- </type>
<desc>
<p>Returns a binary which is made from the integers and
- binaries in <c>IoListOrBinary</c>.</p>
+ binaries in <c><anno>IoListOrBinary</anno></c>.</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
&lt;&lt;1,2,3&gt;&gt;
@@ -1618,22 +1542,19 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>iolist_size(Item) -> integer() >= 0</name>
+ <name name="iolist_size" arity="1"/>
<fsummary>Size of an iolist</fsummary>
- <type>
- <v>Item = iolist() | binary()</v>
- </type>
<desc>
<p>Returns an integer which is the size in bytes
of the binary that would be the result of
- <c>iolist_to_binary(Item)</c>.</p>
+ <c>iolist_to_binary(<anno>Item</anno>)</c>.</p>
<pre>
> <input>iolist_size([1,2|&lt;&lt;3,4>>]).</input>
4</pre>
</desc>
</func>
<func>
- <name>is_alive() -> boolean()</name>
+ <name name="is_alive" arity="0"/>
<fsummary>Check whether the local node is alive</fsummary>
<desc>
<p>Returns <c>true</c> if the local node is alive; that is, if
@@ -1642,25 +1563,19 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_atom(Term) -> boolean()</name>
+ <name name="is_atom" arity="1"/>
<fsummary>Check whether a term is an atom</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is an atom;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_binary(Term) -> boolean()</name>
+ <name name="is_binary" arity="1"/>
<fsummary>Check whether a term is a binary</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a binary;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary;
otherwise returns <c>false</c>.</p>
<p>A binary always contains a complete number of bytes.</p>
@@ -1669,158 +1584,113 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_bitstring(Term) -> boolean()</name>
+ <name name="is_bitstring" arity="1"/>
<fsummary>Check whether a term is a bitstring</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a bitstring (including a binary);
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a bitstring (including a binary);
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_boolean(Term) -> boolean()</name>
+ <name name="is_boolean" arity="1"/>
<fsummary>Check whether a term is a boolean</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is
either the atom <c>true</c> or the atom <c>false</c>
(i.e. a boolean); otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>erlang:is_builtin(Module, Function, Arity) -> boolean()</name>
+ <name name="is_builtin" arity="3"/>
<fsummary>Check if a function is a BIF implemented in C</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Arity = arity()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Module:Function/Arity</c> is
+ <p>Returns <c>true</c> if <c><anno>Module</anno>:<anno>Function</anno>/<anno>Arity</anno></c> is
a BIF implemented in C; otherwise returns <c>false</c>.
This BIF is useful for builders of cross reference tools.</p>
</desc>
</func>
<func>
- <name>is_float(Term) -> boolean()</name>
+ <name name="is_float" arity="1"/>
<fsummary>Check whether a term is a float</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a floating point
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point
number; otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_function(Term) -> boolean()</name>
+ <name name="is_function" arity="1"/>
<fsummary>Check whether a term is a fun</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a fun; otherwise
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun; otherwise
returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_function(Term, Arity) -> boolean()</name>
+ <name name="is_function" arity="2"/>
<fsummary>Check whether a term is a fun with a given arity</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Arity = arity()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a fun that can be
- applied with <c>Arity</c> number of arguments; otherwise
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun that can be
+ 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>
- <name>is_integer(Term) -> boolean()</name>
+ <name name="is_integer" arity="1"/>
<fsummary>Check whether a term is an integer</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is an integer;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_list(Term) -> boolean()</name>
+ <name name="is_list" arity="1"/>
<fsummary>Check whether a term is a list</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a list with
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with
zero or more elements; otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_number(Term) -> boolean()</name>
+ <name name="is_number" arity="1"/>
<fsummary>Check whether a term is a number</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is either an integer or a
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is either an integer or a
floating point number; otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_pid(Term) -> boolean()</name>
+ <name name="is_pid" arity="1"/>
<fsummary>Check whether a term is a pid</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a pid (process
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a pid (process
identifier); otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_port(Term) -> boolean()</name>
+ <name name="is_port" arity="1"/>
<fsummary>Check whether a term is a port</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a port identifier;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_process_alive(Pid) -> boolean()</name>
+ <name name="is_process_alive" arity="1"/>
<fsummary>Check whether a process is alive</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>
- <c>Pid</c> must refer to a process at the local node.
+ <c><anno>Pid</anno></c> must refer to a process at the local node.
Returns <c>true</c> if the process exists and is alive, that
is, is not exiting and has not exited. Otherwise, returns
<c>false</c>.
@@ -1828,41 +1698,32 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_record(Term, RecordTag) -> boolean()</name>
+ <name name="is_record" arity="2"/>
<fsummary>Check whether a term appears to be a record</fsummary>
- <type>
- <v>Term = term()</v>
- <v>RecordTag = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a tuple and its first
- element is <c>RecordTag</c>. Otherwise, returns <c>false</c>.</p>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple and its first
+ element is <c><anno>RecordTag</anno></c>. Otherwise, returns <c>false</c>.</p>
<note>
<p>Normally the compiler treats calls to <c>is_record/2</c>
- specially. It emits code to verify that <c>Term</c> is a
- tuple, that its first element is <c>RecordTag</c>, and that
- the size is correct. However, if the <c>RecordTag</c> is
+ specially. It emits code to verify that <c><anno>Term</anno></c> is a
+ tuple, that its first element is <c><anno>RecordTag</anno></c>, and that
+ the size is correct. However, if the <c><anno>RecordTag</anno></c> is
not a literal atom, the <c>is_record/2</c> BIF will be
called instead and the size of the tuple will not be
verified.</p>
</note>
- <p>Allowed in guard tests, if <c>RecordTag</c> is a literal
+ <p>Allowed in guard tests, if <c><anno>RecordTag</anno></c> is a literal
atom.</p>
</desc>
</func>
<func>
- <name>is_record(Term, RecordTag, Size) -> boolean()</name>
+ <name name="is_record" arity="3"/>
<fsummary>Check whether a term appears to be a record</fsummary>
- <type>
- <v>Term = term()</v>
- <v>RecordTag = atom()</v>
- <v>Size = integer()</v>
- </type>
- <desc>
- <p><c>RecordTag</c> must be an atom. Returns <c>true</c> if
- <c>Term</c> is a tuple, its first element is <c>RecordTag</c>,
- and its size is <c>Size</c>. Otherwise, returns <c>false</c>.</p>
- <p>Allowed in guard tests, provided that <c>RecordTag</c> is
+ <desc>
+ <p><c><anno>RecordTag</anno></c> must be an atom. Returns <c>true</c> if
+ <c><anno>Term</anno></c> is a tuple, its first element is <c><anno>RecordTag</anno></c>,
+ and its size is <c><anno>Size</anno></c>. Otherwise, returns <c>false</c>.</p>
+ <p>Allowed in guard tests, provided that <c><anno>RecordTag</anno></c> is
a literal atom and <c>Size</c> is a literal integer.</p>
<note>
<p>This BIF is documented for completeness. In most cases
@@ -1871,37 +1732,28 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>is_reference(Term) -> boolean()</name>
+ <name name="is_reference" arity="1"/>
<fsummary>Check whether a term is a reference</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a reference;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>is_tuple(Term) -> boolean()</name>
+ <name name="is_tuple" arity="1"/>
<fsummary>Check whether a term is a tuple</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Term</c> is a tuple;
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple;
otherwise returns <c>false</c>.</p>
<p>Allowed in guard tests.</p>
</desc>
</func>
<func>
- <name>length(List) -> integer() >= 0</name>
+ <name name="length" arity="1"/>
<fsummary>Length of a list</fsummary>
- <type>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns the length of <c>List</c>.</p>
+ <p>Returns the length of <c><anno>List</anno></c>.</p>
<pre>
> <input>length([1,2,3,4,5,6,7,8,9]).</input>
9</pre>
@@ -1909,52 +1761,49 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>link(Pid) -> true</name>
+ <name name="link" arity="1"/>
<fsummary>Create a link to another process (or port)</fsummary>
- <type>
- <v>Pid = pid() | port()</v>
- </type>
<desc>
<p>Creates a link between the calling process and another
- process (or port) <c>Pid</c>, if there is not such a link
+ process (or port) <c><anno>PidOrPort</anno></c>, if there is not such a link
already. If a process attempts to create a link to itself,
nothing is done. Returns <c>true</c>.</p>
- <p>If <c>Pid</c> does not exist, the behavior of the BIF depends
+ <p>If <c><anno>PidOrPort</anno></c> does not exist, the behavior of the BIF depends
on if the calling process is trapping exits or not (see
<seealso marker="#process_flag/2">process_flag/2</seealso>):</p>
<list type="bulleted">
<item>If the calling process is not trapping exits, and
- checking <c>Pid</c> is cheap -- that is, if <c>Pid</c> is
+ checking <c><anno>PidOrPort</anno></c> is cheap -- that is, if <c><anno>PidOrPort</anno></c> is
local -- <c>link/1</c> fails with reason <c>noproc</c>.</item>
<item>Otherwise, if the calling process is trapping exits,
- and/or <c>Pid</c> is remote, <c>link/1</c> returns
+ and/or <c><anno>PidOrPort</anno></c> is remote, <c>link/1</c> returns
<c>true</c>, but an exit signal with reason <c>noproc</c>
is sent to the calling process.</item>
</list>
</desc>
</func>
<func>
- <name>list_to_atom(String) -> atom()</name>
+ <name name="list_to_atom" arity="1"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns the atom whose text representation is <c>String</c>.</p>
+ <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
+ characters (i.e. numbers below 256) as the current
+ implementation does not allow unicode characters >= 256 in
+ 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>
</desc>
</func>
<func>
- <name>list_to_binary(IoList) -> binary()</name>
+ <name name="list_to_binary" arity="1"/>
<fsummary>Convert a list to a binary</fsummary>
- <type>
- <v>IoList = iolist()</v>
- </type>
<desc>
<p>Returns a binary which is made from the integers and
- binaries in <c>IoList</c>.</p>
+ binaries in <c><anno>IoList</anno></c>.</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
&lt;&lt;1,2,3&gt;&gt;
@@ -1967,14 +1816,12 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>list_to_bitstring(BitstringList) -> bitstring()</name>
+ <name name="list_to_bitstring" arity="1"/>
+ <type name="bitstring_list"/>
<fsummary>Convert a list to a bitstring</fsummary>
- <type>
- <v>BitstringList = [BitstringList | bitstring() | char()]</v>
- </type>
<desc>
<p>Returns a bitstring which is made from the integers and
- bitstrings in <c>BitstringList</c>. (The last tail in <c>BitstringList</c>
+ bitstrings in <c><anno>BitstringList</anno></c>. (The last tail in <c><anno>BitstringList</anno></c>
is allowed to be a bitstring.)</p>
<pre>
> <input>Bin1 = &lt;&lt;1,2,3&gt;&gt;.</input>
@@ -1983,51 +1830,42 @@ os_prompt% </pre>
&lt;&lt;4,5&gt;&gt;
> <input>Bin3 = &lt;&lt;6,7:4,&gt;&gt;.</input>
&lt;&lt;6&gt;&gt;
-> <input>list_to_binary([Bin1,1,[2,3,Bin2],4|Bin3]).</input>
+> <input>list_to_bitstring([Bin1,1,[2,3,Bin2],4|Bin3]).</input>
&lt;&lt;1,2,3,1,2,3,4,5,4,6,7:46&gt;&gt;</pre>
</desc>
</func>
<func>
- <name>list_to_existing_atom(String) -> atom()</name>
+ <name name="list_to_existing_atom" arity="1"/>
<fsummary>Convert from text representation to an atom</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns the atom whose text representation is <c>String</c>,
+ <p>Returns the atom whose text representation is <c><anno>String</anno></c>,
but only if there already exists such atom.</p>
<p>Failure: <c>badarg</c> if there does not already exist an atom
- whose text representation is <c>String</c>.</p>
+ whose text representation is <c><anno>String</anno></c>.</p>
</desc>
</func>
<func>
- <name>list_to_float(String) -> float()</name>
+ <name name="list_to_float" arity="1"/>
<fsummary>Convert from text representation to a float</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns the float whose text representation is <c>String</c>.</p>
+ <p>Returns the float whose text representation is <c><anno>String</anno></c>.</p>
<pre>
> <input>list_to_float("2.2017764e+0").</input>
2.2017764</pre>
- <p>Failure: <c>badarg</c> if <c>String</c> contains a bad
+ <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad
representation of a float.</p>
</desc>
</func>
<func>
- <name>list_to_integer(String) -> integer()</name>
+ <name name="list_to_integer" arity="1"/>
<fsummary>Convert from text representation to an integer</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
<p>Returns an integer whose text representation is
- <c>String</c>.</p>
+ <c><anno>String</anno></c>.</p>
<pre>
> <input>list_to_integer("123").</input>
123</pre>
- <p>Failure: <c>badarg</c> if <c>String</c> contains a bad
+ <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad
representation of an integer.</p>
</desc>
</func>
@@ -2045,13 +1883,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>list_to_pid(String) -> pid()</name>
+ <name name="list_to_pid" arity="1"/>
<fsummary>Convert from text representation to a pid</fsummary>
- <type>
- <v>String = string()</v>
- </type>
<desc>
- <p>Returns a pid whose text representation is <c>String</c>.</p>
+ <p>Returns a pid whose text representation is <c><anno>String</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -2060,18 +1895,15 @@ os_prompt% </pre>
<pre>
> <input>list_to_pid("&lt;0.4.1>").</input>
&lt;0.4.1></pre>
- <p>Failure: <c>badarg</c> if <c>String</c> contains a bad
+ <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad
representation of a pid.</p>
</desc>
</func>
<func>
- <name>list_to_tuple(List) -> tuple()</name>
+ <name name="list_to_tuple" arity="1"/>
<fsummary>Convert a list to a tuple</fsummary>
- <type>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns a tuple which corresponds to <c>List</c>. <c>List</c>
+ <p>Returns a tuple which corresponds to <c><anno>List</anno></c>. <c><anno>List</anno></c>
can contain any Erlang terms.</p>
<pre>
> <input>list_to_tuple([share, ['Ericsson_B', 163]]).</input>
@@ -2079,38 +1911,30 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>load_module(Module, Binary) -> {module, Module} | {error, Reason}</name>
+ <name name="load_module" arity="2"/>
<fsummary>Load object code for a module</fsummary>
- <type>
- <v>Module = atom()</v>
- <v>Binary = binary()</v>
- <v>Reason = badfile | not_purged | badfile</v>
- </type>
- <desc>
- <p>If <c>Binary</c> contains the object code for the module
- <c>Module</c>, this BIF loads that object code. Also, if
- the code for the module <c>Module</c> already exists, all
+ <desc>
+ <p>If <c><anno>Binary</anno></c> contains the object code for the module
+ <c><anno>Module</anno></c>, this BIF loads that object code. Also, if
+ the code for the module <c><anno>Module</anno></c> already exists, all
export references are replaced so they point to the newly
loaded code. The previously loaded code is kept in the system
as old code, as there may still be processes which are
executing that code. It returns either
- <c>{module, Module}</c>, or <c>{error, Reason}</c> if loading
- fails. <c>Reason</c> is one of the following:</p>
+ <c>{module, <anno>Module</anno>}</c>, or <c>{error, <anno>Reason</anno>}</c> if loading
+ fails. <c><anno>Reason</anno></c> is one of the following:</p>
<taglist>
<tag><c>badfile</c></tag>
<item>
- <p>The object code in <c>Binary</c> has an incorrect format.</p>
+ <p>The object code in <c><anno>Binary</anno></c> has an
+ incorrect format <em>or</em> the object code contains code
+ for another module than <c><anno>Module</anno></c>.</p>
</item>
<tag><c>not_purged</c></tag>
<item>
- <p><c>Binary</c> contains a module which cannot be loaded
+ <p><c><anno>Binary</anno></c> contains a module which cannot be loaded
because old code for this module already exists.</p>
</item>
- <tag><c>badfile</c></tag>
- <item>
- <p>The object code contains code for another module than
- <c>Module</c></p>
- </item>
</taglist>
<warning>
<p>This BIF is intended for the code server (see
@@ -2120,15 +1944,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:load_nif(Path, LoadInfo) -> ok | {error, {Reason, Text}}</name>
+ <name name="load_nif" arity="2"/>
<fsummary>Load NIF library</fsummary>
- <type>
- <v>Path = string()</v>
- <v>LoadInfo = term()</v>
- <v>Reason = load_failed | bad_lib | load | reload |
- upgrade | old_code</v>
- <v>Text = string()</v>
- </type>
<desc>
<note>
<p>In releases older than OTP R14B, NIFs were an
@@ -2139,21 +1956,21 @@ os_prompt% </pre>
<c>{error,Reason,Text}</c>.</p>
</note>
<p>Loads and links a dynamic library containing native
- implemented functions (NIFs) for a module. <c>Path</c> is a
+ implemented functions (NIFs) for a module. <c><anno>Path</anno></c> is a
file path to the sharable object/dynamic library file minus
the OS-dependent file extension (.so for Unix and .dll for
Windows). See <seealso marker="erl_nif">erl_nif</seealso>
on how to implement a NIF library.</p>
- <p><c>LoadInfo</c> can be any term. It will be passed on to
+ <p><c><anno>LoadInfo</anno></c> can be any term. It will be passed on to
the library as part of the initialization. A good practice is
to include a module version number to support future code
upgrade scenarios.</p>
<p>The call to <c>load_nif/2</c> must be made
<em>directly</em> from the Erlang code of the module that the
NIF library belongs to.</p>
- <p>It returns either <c>ok</c>, or <c>{error,{Reason,Text}}</c>
- if loading fails. <c>Reason</c> is one of the atoms below,
- while <c>Text</c> is a human readable string that may give
+ <p>It returns either <c>ok</c>, or <c>{error,{<anno>Reason</anno>,Text}}</c>
+ if loading fails. <c><anno>Reason</anno></c> is one of the atoms below,
+ while <c><anno>Text</anno></c> is a human readable string that may give
some more information about the failure.</p>
<taglist>
<tag><c>load_failed</c></tag>
@@ -2179,11 +1996,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:loaded() -> [Module]</name>
+ <name name="loaded" arity="0"/>
<fsummary>List of all loaded modules</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
<p>Returns a list of all loaded Erlang modules (current and/or
old code), including preloaded modules.</p>
@@ -2191,11 +2005,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:localtime() -> DateTime</name>
+ <name name="localtime" arity="0"/>
<fsummary>Current local date and time</fsummary>
- <type>
- <v>DateTime = <seealso marker="calendar#type-datetime">calendar:datetime()</seealso></v>
- </type>
<desc>
<p>Returns the current local date and time
<c>{{Year, Month, Day}, {Hour, Minute, Second}}</c>.</p>
@@ -2212,32 +2023,27 @@ os_prompt% </pre>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC), if this is supported by the underlying OS. Otherwise,
- no conversion is done and <c>{<anno>Date1</anno>, <anno>Time1</anno>}</c> is returned.</p>
+ no conversion is done and <c><anno>Localtime</anno></c> is returned.</p>
<pre>
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).</input>
{{1996,11,6},{13,45,17}}</pre>
- <p>Failure: <c>badarg</c> if <c>Date1</c> or <c>Time1</c> do
- not denote a valid date or time.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Localtime</anno></c> does not denote
+ a valid date and time.</p>
</desc>
</func>
<func>
- <name>erlang:localtime_to_universaltime({Date1, Time1}, IsDst) -> {Date2, Time2}</name>
+ <name name="localtime_to_universaltime" arity="2"/>
<fsummary>Convert from local to Universal Time Coordinated (UTC) date and time</fsummary>
- <type>
- <v>Date1 = Date2 = <seealso marker="calendar#type-date">calendar:date()</seealso></v>
- <v>Time1 = Time2 = <seealso marker="calendar#type-time">calendar:time()</seealso></v>
- <v>IsDst = true | false | undefined</v>
- </type>
<desc>
<p>Converts local date and time to Universal Time Coordinated
(UTC) just like <c>erlang:localtime_to_universaltime/1</c>,
but the caller decides if daylight saving time is active or
not.</p>
- <p>If <c>IsDst == true</c> the <c>{Date1, Time1}</c> is during
- daylight saving time, if <c>IsDst == false</c> it is not,
- and if <c>IsDst == undefined</c> the underlying OS may
+ <p>If <c><anno>IsDst</anno> == true</c> the <c><anno>Localtime</anno></c> is during
+ daylight saving time, if <c><anno>IsDst</anno> == false</c> it is not,
+ and if <c><anno>IsDst</anno> == undefined</c> the underlying OS may
guess, which is the same as calling
- <c>erlang:localtime_to_universaltime({Date1, Time1})</c>.</p>
+ <c>erlang:localtime_to_universaltime(<anno>Localtime</anno>)</c>.</p>
<pre>
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}, true).</input>
{{1996,11,6},{12,45,17}}
@@ -2245,12 +2051,12 @@ os_prompt% </pre>
{{1996,11,6},{13,45,17}}
> <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}, undefined).</input>
{{1996,11,6},{13,45,17}}</pre>
- <p>Failure: <c>badarg</c> if <c>Date1</c> or <c>Time1</c> do
- not denote a valid date or time.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Localtime</anno></c> does not denote
+ a valid date and time.</p>
</desc>
</func>
<func>
- <name>make_ref() -> reference()</name>
+ <name name="make_ref" arity="0"/>
<fsummary>Return an almost unique reference</fsummary>
<desc>
<p>Returns an almost unique reference.</p>
@@ -2262,33 +2068,23 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:make_tuple(Arity, InitialValue) -> tuple()</name>
+ <name name="make_tuple" arity="2"/>
<fsummary>Create a new tuple of a given arity</fsummary>
- <type>
- <v>Arity = arity()</v>
- <v>InitialValue = term()</v>
- </type>
<desc>
- <p>Returns a new tuple of the given <c>Arity</c>, where all
- elements are <c>InitialValue</c>.</p>
+ <p>Returns a new tuple of the given <c><anno>Arity</anno></c>, where all
+ elements are <c><anno>InitialValue</anno></c>.</p>
<pre>
> <input>erlang:make_tuple(4, []).</input>
{[],[],[],[]}</pre>
</desc>
</func>
<func>
- <name>erlang:make_tuple(Arity, Default, InitList) -> tuple()</name>
+ <name name="make_tuple" arity="3"/>
<fsummary>Create a new tuple with given arity and contents</fsummary>
- <type>
- <v>Arity = arity()</v>
- <v>Default = term()</v>
- <v>InitList = [{Position,term()}]</v>
- <v>Position = integer()</v>
- </type>
- <desc>
- <p><c>erlang:make_tuple</c> first creates a tuple of size <c>Arity</c>
- where each element has the value <c>Default</c>. It then fills
- in values from <c>InitList</c>. Each list element in <c>InitList</c>
+ <desc>
+ <p><c>erlang:make_tuple</c> first creates a tuple of size <c><anno>Arity</anno></c>
+ where each element has the value <c><anno>DefaultValue</anno></c>. It then fills
+ in values from <c><anno>InitList</anno></c>. Each list element in <c><anno>InitList</anno></c>
must be a two-tuple where the first element is a position in the
newly created tuple and the second element is any term. If a position
occurs more than once in the list, the term corresponding to
@@ -2307,15 +2103,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:md5(Data) -> Digest</name>
+ <name name="md5" arity="1"/>
<fsummary>Compute an MD5 message digest</fsummary>
- <type>
- <v>Data = iodata()</v>
- <v>Digest = binary()</v>
- </type>
<desc>
- <p>Computes an <c>MD5</c> message digest from <c>Data</c>, where
- the length of the digest is 128 bits (16 bytes). <c>Data</c>
+ <p>Computes an <c>MD5</c> message digest from <c><anno>Data</anno></c>, where
+ the length of the digest is 128 bits (16 bytes). <c><anno>Data</anno></c>
is a binary or a list of small integers and binaries.</p>
<p>See The MD5 Message Digest Algorithm (RFC 1321) for more
information about MD5.</p>
@@ -2324,51 +2116,39 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:md5_final(Context) -> Digest</name>
+ <name name="md5_final" arity="1"/>
<fsummary>Finish the update of an MD5 context and return the computed MD5 message digest</fsummary>
- <type>
- <v>Context = Digest = binary()</v>
- </type>
<desc>
- <p>Finishes the update of an MD5 <c>Context</c> and returns
+ <p>Finishes the update of an MD5 <c><anno>Context</anno></c> and returns
the computed <c>MD5</c> message digest.</p>
</desc>
</func>
<func>
- <name>erlang:md5_init() -> Context</name>
+ <name name="md5_init" arity="0"/>
<fsummary>Create an MD5 context</fsummary>
- <type>
- <v>Context = binary()</v>
- </type>
<desc>
<p>Creates an MD5 context, to be used in subsequent calls to
<c>md5_update/2</c>.</p>
</desc>
</func>
<func>
- <name>erlang:md5_update(Context, Data) -> NewContext</name>
+ <name name="md5_update" arity="2"/>
<fsummary>Update an MD5 context with data, and return a new context</fsummary>
- <type>
- <v>Data = iodata()</v>
- <v>Context = NewContext = binary()</v>
- </type>
<desc>
- <p>Updates an MD5 <c>Context</c> with <c>Data</c>, and returns
- a <c>NewContext</c>.</p>
+ <p>Updates an MD5 <c><anno>Context</anno></c> with <c><anno>Data</anno></c>, and returns
+ a <c><anno>NewContext</anno></c>.</p>
</desc>
</func>
<func>
- <name>erlang:memory() -> [{Type, Size}]</name>
+ <name name="memory" arity="0"/>
+ <type name="memory_type"/>
<fsummary>Information about dynamically allocated memory</fsummary>
- <type>
- <v>Type, Size -- see below</v>
- </type>
<desc>
<p>Returns a list containing information about memory
dynamically allocated by the Erlang emulator. Each element of
the list is a tuple <c>{Type, Size}</c>. The first element
- <c>Type</c>is an atom describing memory type. The second
- element <c>Size</c>is memory size in bytes. A description of
+ <c><anno>Type</anno></c>is an atom describing memory type. The second
+ element <c><anno>Size</anno></c>is memory size in bytes. A description of
each memory type follows:</p>
<taglist>
<tag><c>total</c></tag>
@@ -2430,6 +2210,14 @@ os_prompt% </pre>
<p>This memory is part of the memory presented as
<c>system</c> memory.</p>
</item>
+ <tag><c>low</c></tag>
+ <item>
+ <p>Only on 64-bit halfword emulator.</p>
+ <p>The total amount of memory allocated in low memory areas
+ that are restricted to less than 4 Gb even though
+ the system may have more physical memory.</p>
+ <p>May be removed in future releases of halfword emulator.</p>
+ </item>
<tag><c>maximum</c></tag>
<item>
<p>The maximum total amount of memory allocated since
@@ -2441,14 +2229,6 @@ os_prompt% </pre>
<seealso marker="tools:instrument">instrument(3)</seealso>
and/or <seealso marker="erts:erl">erl(1)</seealso>.</p>
</item>
- <tag><c>low</c></tag>
- <item>
- <p>Only on 64-bit halfword emulator.</p>
- <p>The total amount of memory allocated in low memory areas
- that are restricted to less than 4 Gb even though
- the system may have more physical memory.</p>
- <p>May be removed in future releases of halfword emulator.</p>
- </item>
</taglist>
<note>
<p>The <c>system</c> value is not complete. Some allocated
@@ -2512,16 +2292,15 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:memory(Type | [Type]) -> Size | [{Type, Size}]</name>
+ <name name="memory" arity="1" clause_i="1"/>
+ <name name="memory" arity="1" clause_i="2"/>
+ <type name="memory_type"/>
<fsummary>Information about dynamically allocated memory</fsummary>
- <type>
- <v>Type, Size -- see below</v>
- </type>
<desc>
<p>Returns the memory size in bytes allocated for memory of
- type <c>Type</c>. The argument can also be given as a list
- of <c>Type</c> atoms, in which case a corresponding list of
- <c>{Type, Size}</c> tuples is returned.</p>
+ type <c><anno>Type</anno></c>. The argument can also be given as a list
+ of <c>memory_type()</c> atoms, in which case a corresponding list of
+ <c>{memory_type(), Size :: integer >= 0}</c> tuples is returned.</p>
<note>
<p>
Since erts version 5.6.4 <c>erlang:memory/1</c> requires that
@@ -2533,13 +2312,13 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Type</c> is not one of the memory types listed in the
+ If <c><anno>Type</anno></c> is not one of the memory types listed in the
documentation of
<seealso marker="#memory/0">erlang:memory/0</seealso>.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>maximum</c> is passed as <c>Type</c> and the emulator
+ If <c>maximum</c> is passed as <c><anno>Type</anno></c> and the emulator
is not run in instrumented mode.
</item>
<tag><c>notsup</c></tag>
@@ -2561,13 +2340,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>module_loaded(Module) -> boolean()</name>
+ <name name="module_loaded" arity="1"/>
<fsummary>Check if a module is loaded</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Returns <c>true</c> if the module <c>Module</c> is loaded,
+ <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is loaded,
otherwise returns <c>false</c>. It does not attempt to load
the module.</p>
<warning>
@@ -2578,22 +2354,15 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>monitor(Type, Item) -> MonitorRef</name>
+ <name name="monitor" arity="2"/>
<fsummary>Start monitoring</fsummary>
- <type>
- <v>Type = process</v>
- <v>Item = pid() | {RegName, Node} | RegName</v>
- <v>&nbsp;RegName = atom()</v>
- <v>&nbsp;Node = node()</v>
- <v>MonitorRef = reference()</v>
- </type>
- <desc>
- <p>The calling process starts monitoring <c>Item</c> which is
- an object of type <c>Type</c>.</p>
+ <desc>
+ <p>The calling process starts monitoring <c><anno>Item</anno></c> which is
+ an object of type <c><anno>Type</anno></c>.</p>
<p>Currently only processes can be monitored, i.e. the only
- allowed <c>Type</c> is <c>process</c>, but other types may be
+ allowed <c><anno>Type</anno></c> is <c>process</c>, but other types may be
allowed in the future.</p>
- <p><c>Item</c> can be:</p>
+ <p><c><anno>Item</anno></c> can be:</p>
<taglist>
<tag><c>pid()</c></tag>
<item>
@@ -2619,8 +2388,8 @@ os_prompt% </pre>
unregistered.</p>
</note>
<p>A <c>'DOWN'</c> message will be sent to the monitoring
- process if <c>Item</c> dies, if <c>Item</c> does not exist,
- or if the connection is lost to the node which <c>Item</c>
+ process if <c><anno>Item</anno></c> dies, if <c><anno>Item</anno></c> does not exist,
+ or if the connection is lost to the node which <c><anno>Item</anno></c>
resides on. A <c>'DOWN'</c> message has the following pattern:</p>
<code type="none">
{'DOWN', MonitorRef, Type, Object, Info}</code>
@@ -2631,11 +2400,11 @@ os_prompt% </pre>
<item>
<p>A reference to the monitored object:</p>
<list type="bulleted">
- <item>the pid of the monitored process, if <c>Item</c> was
+ <item>the pid of the monitored process, if <c><anno>Item</anno></c> was
specified as a pid.</item>
- <item><c>{RegName, Node}</c>, if <c>Item</c> was specified as
+ <item><c>{RegName, Node}</c>, if <c><anno>Item</anno></c> was specified as
<c>{RegName, Node}</c>.</item>
- <item><c>{RegName, Node}</c>, if <c>Item</c> was specified as
+ <item><c>{RegName, Node}</c>, if <c><anno>Item</anno></c> was specified as
<c>RegName</c>. <c>Node</c> will in this case be the
name of the local node (<c>node()</c>).</item>
</list>
@@ -2644,7 +2413,7 @@ os_prompt% </pre>
<item>
<p>Either the exit reason of the process, <c>noproc</c>
(non-existing process), or <c>noconnection</c> (no
- connection to <c>Node</c>).</p>
+ connection to <c><anno>Node</anno></c>).</p>
</item>
</taglist>
<note>
@@ -2662,7 +2431,7 @@ os_prompt% </pre>
where remote process monitoring by registered name is not
implemented), the call fails with <c>badarg</c>.</p>
<p>Making several calls to <c>monitor/2</c> for the same
- <c>Item</c> is not an error; it results in as many, completely
+ <c><anno>Item</anno></c> is not an error; it results in as many, completely
independent, monitorings.</p>
<note>
<p>The format of the <c>'DOWN'</c> message changed in the 5.2
@@ -2680,25 +2449,21 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>monitor_node(Node, Flag) -> true</name>
+ <name name="monitor_node" arity="2"/>
<fsummary>Monitor the status of a node</fsummary>
- <type>
- <v>Node = node()</v>
- <v>Flag = boolean()</v>
- </type>
<desc>
- <p>Monitors the status of the node <c>Node</c>. If <c>Flag</c>
- is <c>true</c>, monitoring is turned on; if <c>Flag</c> is
+ <p>Monitors the status of the node <c><anno>Node</anno></c>. If <c><anno>Flag</anno></c>
+ is <c>true</c>, monitoring is turned on; if <c><anno>Flag</anno></c> is
<c>false</c>, monitoring is turned off.</p>
<p>Making several calls to <c>monitor_node(Node, true)</c> for
- the same <c>Node</c> is not an error; it results in as many,
+ the same <c><anno>Node</anno></c> is not an error; it results in as many,
completely independent, monitorings.</p>
- <p>If <c>Node</c> fails or does not exist, the message
+ <p>If <c><anno>Node</anno></c> fails or does not exist, the message
<c>{nodedown, Node}</c> is delivered to the process. If a
process has made two calls to <c>monitor_node(Node, true)</c>
- and <c>Node</c> terminates, two <c>nodedown</c> messages are
+ and <c><anno>Node</anno></c> terminates, two <c>nodedown</c> messages are
delivered to the process. If there is no connection to
- <c>Node</c>, there will be an attempt to create one. If this
+ <c><anno>Node</anno></c>, there will be an attempt to create one. If this
fails, a <c>nodedown</c> message is delivered.</p>
<p>Nodes connected through hidden connections can be monitored
as any other node.</p>
@@ -2706,14 +2471,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:monitor_node(Node, Flag, Options) -> true</name>
+ <name name="monitor_node" arity="3"/>
<fsummary>Monitor the status of a node</fsummary>
- <type>
- <v>Node = node()</v>
- <v>Flag = boolean()</v>
- <v>Options = [Option]</v>
- <v>Option = allow_passive_connect</v>
- </type>
<desc>
<p>Behaves as <c>monitor_node/2</c> except that it allows an
extra option to be given, namely <c>allow_passive_connect</c>.
@@ -2736,11 +2495,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:nif_error(Reason)</name>
+ <name name="nif_error" arity="1"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Works exactly like
<seealso marker="#error/1">erlang:error/1</seealso>,
@@ -2751,12 +2507,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:nif_error(Reason, Args)</name>
+ <name name="nif_error" arity="2"/>
<fsummary>Stop execution with a given reason</fsummary>
- <type>
- <v>Reason = term()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Works exactly like
<seealso marker="#error/2">erlang:error/2</seealso>,
@@ -2767,11 +2519,8 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>node() -> Node</name>
+ <name name="node" arity="0"/>
<fsummary>Name of the local node</fsummary>
- <type>
- <v>Node = node()</v>
- </type>
<desc>
<p>Returns the name of the local node. If the node is not alive,
<c>nonode@nohost</c> is returned instead.</p>
@@ -2779,14 +2528,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>node(Arg) -> Node</name>
+ <name name="node" arity="1"/>
<fsummary>At which node is a pid, port or reference located</fsummary>
- <type>
- <v>Arg = pid() | port() | reference()</v>
- <v>Node = node()</v>
- </type>
<desc>
- <p>Returns the node where <c>Arg</c> is located. <c>Arg</c> can
+ <p>Returns the node where <c><anno>Arg</anno></c> is located. <c><anno>Arg</anno></c> can
be a pid, a reference, or a port. If the local node is not
alive, <c>nonode@nohost</c> is returned.</p>
<p>Allowed in guard tests.</p>
@@ -2801,17 +2546,13 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>nodes(Arg | [Arg]) -> Nodes</name>
+ <name name="nodes" arity="1"/>
<fsummary>All nodes of a certain type in the system</fsummary>
- <type>
- <v>Arg = visible | hidden | connected | this | known</v>
- <v>Nodes = [node()]</v>
- </type>
<desc>
<p>Returns a list of nodes according to argument given.
The result returned when the argument is a list, is the list
of nodes satisfying the disjunction(s) of the list elements.</p>
- <p><c>Arg</c> can be any of the following:</p>
+ <p><c><anno>NodeType</anno></c> can be any of the following:</p>
<taglist>
<tag><c>visible</c></tag>
<item>
@@ -2840,15 +2581,12 @@ os_prompt% </pre>
<c>nodes() = nodes(visible)</c>.</p>
<p>If the local node is not alive,
<c>nodes(this) == nodes(known) == [nonode@nohost]</c>, for
- any other <c>Arg</c> the empty list [] is returned.</p>
+ any other <c><anno>Arg</anno></c> the empty list [] is returned.</p>
</desc>
</func>
<func>
- <name>now() -> timestamp()</name>
- <type>
- <v>timestamp() = {MegaSecs, Secs, MicroSecs}</v>
- <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v>
- </type>
+ <name name="now" arity="0"/>
+ <type name="timestamp"/>
<fsummary>Elapsed time since 00:00 GMT</fsummary>
<desc>
<p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c> which is
@@ -2870,35 +2608,19 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>open_port(PortName, PortSettings) -> port()</name>
+ <name name="open_port" arity="2"/>
<fsummary>Open a port</fsummary>
- <type>
- <v>PortName = {spawn, Command} | {spawn_driver, Command} | {spawn_executable, FileName} | {fd, In, Out}</v>
- <v>&nbsp;Command = string()</v>
- <v>&nbsp;FileName = [ FileNameChar ] | binary()</v>
- <v>&nbsp;FileNameChar = integer() (1..255 or any Unicode codepoint, see description)</v>
- <v>&nbsp;In = Out = integer()</v>
- <v>PortSettings = [Opt]</v>
- <v>&nbsp;Opt = {packet, N} | stream | {line, L} | {cd, Dir} | {env, Env} | {args, [ ArgString ]} | {arg0, ArgString} | exit_status | use_stdio | nouse_stdio | stderr_to_stdout | in | out | binary | eof</v>
- <v>&nbsp;&nbsp;N = 1 | 2 | 4</v>
- <v>&nbsp;&nbsp;L = integer()</v>
- <v>&nbsp;&nbsp;Dir = string()</v>
- <v>&nbsp;&nbsp;ArgString = [ FileNameChar ] | binary()</v>
- <v>&nbsp;&nbsp;Env = [{Name, Val}]</v>
- <v>&nbsp;&nbsp;&nbsp;Name = string()</v>
- <v>&nbsp;&nbsp;&nbsp;Val = string() | false</v>
- </type>
<desc>
<p>Returns a port identifier as the result of opening a
new Erlang port. A port can be seen as an external Erlang
- process. <c>PortName</c> is one of the following:</p>
+ process. <c><anno>PortName</anno></c> is one of the following:</p>
<taglist>
- <tag><c>{spawn, Command}</c></tag>
+ <tag><c>{spawn, <anno>Command</anno>}</c></tag>
<item>
- <p>Starts an external program. <c>Command</c> is the name
- of the external program which will be run. <c>Command</c>
+ <p>Starts an external program. <c><anno>Command</anno></c> is the name
+ of the external program which will be run. <c><anno>Command</anno></c>
runs outside the Erlang work space unless an Erlang
- driver with the name <c>Command</c> is found. If found,
+ driver with the name <c><anno>Command</anno></c> is found. If found,
that driver will be started. A driver runs in the Erlang
workspace, which means that it is linked with the Erlang
runtime system.</p>
@@ -2918,24 +2640,24 @@ os_prompt% </pre>
name of the executable (or driver). This (among other
things) makes this option unsuitable for running
programs having spaces in file or directory names. Use
- {spawn_executable, Command} instead if spaces in executable
+ {spawn_executable, <anno>Command</anno>} instead if spaces in executable
file names is desired.</p>
</item>
- <tag><c>{spawn_driver, Command}</c></tag>
+ <tag><c>{spawn_driver, <anno>Command</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, Command}</c>, but demands the
+ <p>Works like <c>{spawn, <anno>Command</anno>}</c>, but demands the
first (space separated) token of the command to be the name of a
loaded driver. If no driver with that name is loaded, a
<c>badarg</c> error is raised.</p>
</item>
- <tag><c>{spawn_executable, Command}</c></tag>
+ <tag><c>{spawn_executable, <anno>FileName</anno>}</c></tag>
<item>
- <p>Works like <c>{spawn, Command}</c>, but only runs
- external executables. The <c>Command</c> in its whole
+ <p>Works like <c>{spawn, <anno>FileName</anno>}</c>, but only runs
+ external executables. The <c><anno>FileName</anno></c> in its whole
is used as the name of the executable, including any
spaces. If arguments are to be passed, the
- <c>args</c> and <c>arg0</c> <c>PortSettings</c> can be used.</p>
+ <c>args</c> and <c>arg0</c> <c><anno>PortSettings</anno></c> can be used.</p>
<p>The shell is not usually invoked to start the
program, it's executed directly. Neither is the
@@ -2966,7 +2688,7 @@ os_prompt% </pre>
of the executable is limited to the ISO-latin-1
character set.</p></note>
- <p>If the <c>Command</c> cannot be run, an error
+ <p>If the <c><anno>FileName</anno></c> cannot be run, an error
exception, with the posix error code as the reason, is
raised. The error reason may differ between operating
systems. Typically the error <c>enoent</c> is raised
@@ -2974,23 +2696,23 @@ os_prompt% </pre>
<c>eaccess</c> is raised when the given file is not
executable.</p>
</item>
- <tag><c>{fd, In, Out}</c></tag>
+ <tag><c>{fd, <anno>In</anno>, <anno>Out</anno>}</c></tag>
<item>
<p>Allows an Erlang process to access any currently opened
file descriptors used by Erlang. The file descriptor
- <c>In</c> can be used for standard input, and the file
- descriptor <c>Out</c> for standard output. It is only
+ <c><anno>In</anno></c> can be used for standard input, and the file
+ descriptor <c><anno>Out</anno></c> for standard output. It is only
used for various servers in the Erlang operating system
(<c>shell</c> and <c>user</c>). Hence, its use is very
limited.</p>
</item>
</taglist>
- <p><c>PortSettings</c> is a list of settings for the port.
+ <p><c><anno>PortSettings</anno></c> is a list of settings for the port.
Valid settings are:</p>
<taglist>
- <tag><c>{packet, N}</c></tag>
+ <tag><c>{packet, <anno>N</anno>}</c></tag>
<item>
- <p>Messages are preceded by their length, sent in <c>N</c>
+ <p>Messages are preceded by their length, sent in <c><anno>N</anno></c>
bytes, with the most significant byte first. Valid values
for <c>N</c> are 1, 2, or 4.</p>
</item>
@@ -3000,7 +2722,7 @@ os_prompt% </pre>
user-defined protocol must be used between the Erlang
process and the external object.</p>
</item>
- <tag><c>{line, L}</c></tag>
+ <tag><c>{line, <anno>L</anno>}</c></tag>
<item>
<p>Messages are delivered on a per line basis. Each line
(delimited by the OS-dependent newline sequence) is
@@ -3008,7 +2730,7 @@ os_prompt% </pre>
is <c>{Flag, Line}</c>, where <c>Flag</c> is either
<c>eol</c> or <c>noeol</c> and <c>Line</c> is the actual
data delivered (without the newline sequence).</p>
- <p><c>L</c> specifies the maximum line length in bytes.
+ <p><c><anno>L</anno></c> specifies the maximum line length in bytes.
Lines longer than this will be delivered in more than one
message, with the <c>Flag</c> set to <c>noeol</c> for all
but the last message. If end of file is encountered
@@ -3016,39 +2738,40 @@ os_prompt% </pre>
sequence, the last line will also be delivered with
the <c>Flag</c> set to <c>noeol</c>. In all other cases,
lines are delivered with <c>Flag</c> set to <c>eol</c>.</p>
- <p>The <c>{packet, N}</c> and <c>{line, L}</c> settings are
+ <p>The <c>{packet, <anno>N</anno>}</c> and <c>{line, <anno>L</anno>}</c> settings are
mutually exclusive.</p>
</item>
- <tag><c>{cd, Dir}</c></tag>
+ <tag><c>{cd, <anno>Dir</anno>}</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> and
- <c>{spawn_executable, Command}</c>.
- The external program starts using <c>Dir</c> as its
- working directory. <c>Dir</c> must be a string. Not
- available on VxWorks.</p>
+ <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.
+ </p>
</item>
- <tag><c>{env, Env}</c></tag>
+ <tag><c>{env, <anno>Env</anno>}</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> and
- <c>{spawn_executable, Command}</c>.
+ <p>This is only valid for <c>{spawn, <anno>Command</anno>}</c> and
+ <c>{spawn_executable, <anno>FileName</anno>}</c>.
The environment of the started process is extended using
- the environment specifications in <c>Env</c>.</p>
- <p><c>Env</c> should be a list of tuples <c>{Name, Val}</c>,
- where <c>Name</c> is the name of an environment variable,
- and <c>Val</c> is the value it is to have in the spawned
- port process. Both <c>Name</c> and <c>Val</c> must be
- strings. The one exception is <c>Val</c> being the atom
+ the environment specifications in <c><anno>Env</anno></c>.</p>
+ <p><c><anno>Env</anno></c> should be a list of tuples <c>{<anno>Name</anno>, <anno>Val</anno>}</c>,
+ where <c><anno>Name</anno></c> is the name of an environment variable,
+ and <c><anno>Val</anno></c> is the value it is to have in the spawned
+ 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.</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() ]}</c></tag>
+ <tag><c>{args, [ string() | binary() ]}</c></tag>
<item>
- <p>This option is only valid for <c>{spawn_executable, Command}</c>
+ <p>This option is only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
and specifies arguments to the executable. Each argument
is given as a separate string and (on Unix) eventually
ends up as one element each in the argument vector. On
@@ -3091,10 +2814,10 @@ os_prompt% </pre>
option can be used.</p>
</item>
- <tag><c>{arg0, string()}</c></tag>
+ <tag><c>{arg0, string() | binary()}</c></tag>
<item>
- <p>This option is only valid for <c>{spawn_executable, Command}</c>
+ <p>This option is only valid for <c>{spawn_executable, <anno>FileName</anno>}</c>
and explicitly specifies the program name argument when
running an executable. This might in some circumstances,
on some operating systems, be desirable. How the program
@@ -3108,9 +2831,9 @@ os_prompt% </pre>
<tag><c>exit_status</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> where
- <c>Command</c> refers to an external program, and for
- <c>{spawn_executable, Command}</c>.</p>
+ <p>This is only valid for <c>{spawn, <anno>Command</anno>}</c> where
+ <c><anno>Command</anno></c> refers to an external program, and for
+ <c>{spawn_executable, <anno>FileName</anno>}</c>.</p>
<p>When the external process connected to the port exits, a
message of the form <c>{Port,{exit_status,Status}}</c> is
sent to the connected process, where <c>Status</c> is the
@@ -3125,8 +2848,8 @@ os_prompt% </pre>
</item>
<tag><c>use_stdio</c></tag>
<item>
- <p>This is only valid for <c>{spawn, Command}</c> and
- <c>{spawn_executable, Command}</c>. It
+ <p>This is only valid for <c>{spawn, <anno>Command</anno>}</c> and
+ <c>{spawn_executable, <anno>FileName</anno>}</c>. It
allows the standard input and output (file descriptors 0
and 1) of the spawned (UNIX) process for communication
with Erlang.</p>
@@ -3180,6 +2903,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>
@@ -3223,7 +2958,7 @@ os_prompt% </pre>
</item>
<tag><c>enoent</c></tag>
<item>
- <p>The <c>Command</c> given in <c>{spawn_executable, Command}</c> does not point out an existing file.</p>
+ <p>The <c><anno>FileName</anno></c> given in <c>{spawn_executable, <anno>FileName</anno>}</c> does not point out an existing file.</p>
</item>
</taglist>
<p>During use of a port opened using <c>{spawn, Name}</c>,
@@ -3232,63 +2967,55 @@ 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>
- <name>erlang:phash(Term, Range) -> Hash</name>
+ <name name="phash" arity="2"/>
+ <type_desc variable="Range">Range = 1..2^32, Hash = 1..Range</type_desc>
<fsummary>Portable hash function</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Range = 1..2^32</v>
- <v>Hash = 1..Range</v>
- </type>
<desc>
<p>Portable hash function that will give the same hash for
the same Erlang term regardless of machine architecture and
ERTS version (the BIF was introduced in ERTS 4.9.1.1). Range
can be between 1 and 2^32, the function returns a hash value
- for <c>Term</c> within the range <c>1..Range</c>.</p>
+ for <c><anno>Term</anno></c> within the range <c>1..<anno>Range</anno></c>.</p>
<p>This BIF could be used instead of the old deprecated
<c>erlang:hash/2</c> BIF, as it calculates better hashes for
all data-types, but consider using <c>phash2/1,2</c> instead.</p>
</desc>
</func>
<func>
- <name>erlang:phash2(Term [, Range]) -> Hash</name>
+ <name name="phash2" arity="1"/>
+ <name name="phash2" arity="2"/>
+ <type_desc variable="Range">1..2^32</type_desc>
+ <type_desc variable="Hash">0..Range-1</type_desc>
<fsummary>Portable hash function</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Range = 1..2^32</v>
- <v>Hash = 0..Range-1</v>
- </type>
<desc>
<p>Portable hash function that will give the same hash for
the same Erlang term regardless of machine architecture and
ERTS version (the BIF was introduced in ERTS 5.2). Range can
be between 1 and 2^32, the function returns a hash value for
- <c>Term</c> within the range <c>0..Range-1</c>. When called
- without the <c>Range</c> argument, a value in the range
+ <c><anno>Term</anno></c> within the range <c>0..<anno>Range</anno>-1</c>. When called
+ without the <c><anno>Range</anno></c> argument, a value in the range
<c>0..2^27-1</c> is returned.</p>
<p>This BIF should always be used for hashing terms. It
distributes small integers better than <c>phash/2</c>, and
it is faster for bignums and binaries.</p>
- <p>Note that the range <c>0..Range-1</c> is different from
- the range of <c>phash/2</c> (<c>1..Range</c>).</p>
+ <p>Note that the range <c>0..<anno>Range</anno>-1</c> is different from
+ the range of <c>phash/2</c> (<c>1..<anno>Range</anno></c>).</p>
</desc>
</func>
<func>
- <name>pid_to_list(Pid) -> string()</name>
+ <name name="pid_to_list" arity="1"/>
<fsummary>Text representation of a pid</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Pid</c>.</p>
+ representation of <c><anno>Pid</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -3297,88 +3024,81 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>port_close(Port) -> true</name>
+ <name name="port_close" arity="1"/>
<fsummary>Close an open port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- </type>
<desc>
<p>Closes an open port. Roughly the same as
- <c>Port ! {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>
- <p>For comparison: <c>Port ! {self(), close}</c> fails with
- <c>badarg</c> if <c>Port</c> cannot be sent to (i.e.,
- <c>Port</c> refers neither to a port nor to a process). If
- <c>Port</c> is a closed port nothing happens. If <c>Port</c>
+ <c><anno>Port</anno> ! {self(), close}</c> except for the error behaviour
+ (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
+ <c><anno>Port</anno></c> is a closed port nothing happens. If <c><anno>Port</anno></c>
is an open port and the calling process is the port owner,
the port replies with <c>{Port, closed}</c> when all buffers
have been flushed and the port really closes, but if
the calling process is not the port owner the <em>port owner</em> fails with <c>badsig</c>.</p>
<p>Note that any process can close a port using
- <c>Port ! {PortOwner, close}</c> just as if it itself was
+ <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>Port ! {self(), close}</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port or
+ <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>
</func>
<func>
- <name>port_command(Port, Data) -> true</name>
+ <name name="port_command" arity="2"/>
<fsummary>Send data to a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Data = iodata()</v>
- </type>
<desc>
<p>Sends data to a port. Same as
- <c>Port ! {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>Port ! {self(), {command, Data}}</c>
- fails with <c>badarg</c> if <c>Port</c> cannot be sent to
- (i.e., <c>Port</c> refers neither to a port nor to a process).
- If <c>Port</c> is a closed port the data message disappears
- without a sound. If <c>Port</c> is open and the calling
+ <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
+ without a sound. If <c><anno>Port</anno></c> is open and the calling
process is not the port owner, the <em>port owner</em> fails
with <c>badsig</c>. The port owner fails with <c>badsig</c>
- also if <c>Data</c> is not a valid IO list.</p>
+ also if <c><anno>Data</anno></c> is not a valid IO list.</p>
<p>Note that any process can send to a port using
- <c>Port ! {PortOwner, {command, Data}}</c> just as if it
+ <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(Port, Data)</c> has a cleaner and
- more logical behaviour than
- <c>Port ! {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>
<item>
- If <c>Port</c> is not an open port or the registered name
+ If <c><anno>Port</anno></c> is not an open port or the registered name
of an open port.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>Data</c> is not a valid io list.
+ If <c><anno>Data</anno></c> is not a valid io list.
</item>
</taglist>
</desc>
</func>
<func>
- <name>port_command(Port, Data, OptionList) -> boolean()</name>
+ <name name="port_command" arity="3"/>
<fsummary>Send data to a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Data = iodata()</v>
- <v>OptionList = [Option]</v>
- <v>Option = force</v>
- <v>Option = nosuspend</v>
- </type>
<desc>
<p>Sends data to a port. <c>port_command(Port, Data, [])</c>
equals <c>port_command(Port, Data)</c>.</p>
@@ -3386,7 +3106,7 @@ os_prompt% </pre>
otherwise, <c>true</c> is returned.</p>
<p>If the port is busy, the calling process will be suspended
until the port is not busy anymore.</p>
- <p>Currently the following <c>Option</c>s are valid:</p>
+ <p>Currently the following <c><anno>Option</anno></c>s are valid:</p>
<taglist>
<tag><c>force</c></tag>
<item>The calling process will not be suspended if the port is
@@ -3410,16 +3130,16 @@ os_prompt% </pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Port</c> is not an open port or the registered name
+ If <c><anno>Port</anno></c> is not an open port or the registered name
of an open port.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>Data</c> is not a valid io list.
+ If <c><anno>Data</anno></c> is not a valid io list.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>OptionList</c> is not a valid option list.
+ If <c><anno>OptionList</anno></c> is not a valid option list.
</item>
<tag><c>notsup</c></tag>
<item>
@@ -3431,15 +3151,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>port_connect(Port, Pid) -> true</name>
+ <name name="port_connect" arity="2"/>
<fsummary>Set the owner of a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Pid = pid()</v>
- </type>
<desc>
- <p>Sets the port owner (the connected port) to <c>Pid</c>.
- Roughly the same as <c>Port ! {self(), {connect, Pid}}</c>
+ <p>Sets the port owner (the connected port) to <c><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>
@@ -3450,6 +3166,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>
@@ -3457,164 +3176,265 @@ os_prompt% </pre>
<c>unlink(Port)</c> if this is not desired. Any process may
set the port owner to be any process with
<c>port_connect/2</c>.</p>
- <p>For comparison: <c>Port ! {self(), {connect, Pid}}</c> fails
- with <c>badarg</c> if <c>Port</c> cannot be sent to (i.e.,
- <c>Port</c> refers neither to a port nor to a process). If
- <c>Port</c> is a closed port nothing happens. If <c>Port</c>
+ <p>For comparison: <c><anno>Port</anno> ! {self(), {connect, <anno>Pid</anno>}}</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 nothing happens. If <c><anno>Port</anno></c>
is an open port and the calling process is the port owner,
the port replies with <c>{Port, connected}</c> to the old
port owner. Note that the old port owner is still linked to
- the port, and that the new is not. If <c>Port</c> is an open
+ the port, and that the new is not. If <c><anno>Port</anno></c> is an open
port and the calling process is not the port owner,
the <em>port owner</em> fails with <c>badsig</c>. The port
- owner fails with <c>badsig</c> also if <c>Pid</c> is not an
+ owner fails with <c>badsig</c> also if <c><anno>Pid</anno></c> is not an
existing local pid.</p>
<p>Note that any process can set the port owner using
- <c>Port ! {PortOwner, {connect, Pid}}</c> just as if it
+ <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(Port, Pid)</c> has a cleaner and
- more logical behaviour than
- <c>Port ! {self(),{connect,Pid}}</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port
+ <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>Pid</c> is
not an existing local pid.</p>
</desc>
</func>
<func>
- <name>port_control(Port, Operation, Data) -> Res</name>
+ <name name="port_control" arity="3"/>
<fsummary>Perform a synchronous control operation on a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Operation = integer()</v>
- <v>Data = Res = iodata()</v>
- </type>
<desc>
<p>Performs a synchronous control operation on a port.
- The meaning of <c>Operation</c> and <c>Data</c> depends on
+ The meaning of <c><anno>Operation</anno></c> and <c><anno>Data</anno></c> depends on
the port, i.e., on the port driver. Not all port drivers
support this control feature.</p>
<p>Returns: a list of integers in the range 0 through 255, or a
binary, depending on the port driver. The meaning of
the returned data also depends on the port driver.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port or
- the registered name of an open port, if <c>Operation</c>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port or
+ the registered name of an open port, if <c><anno>Operation</anno></c>
cannot fit in a 32-bit integer, if the port driver does not
support synchronous control operations, or if the port driver
so decides for any reason (probably something wrong with
- <c>Operation</c> or <c>Data</c>).</p>
+ <c><anno>Operation</anno></c> or <c><anno>Data</anno></c>).</p>
</desc>
</func>
<func>
- <name>erlang:port_call(Port, Operation, Data) -> term()</name>
+ <name name="port_call" arity="3"/>
<fsummary>Synchronous call to a port with term data</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Operation = integer()</v>
- <v>Data = term()</v>
- </type>
<desc>
<p>Performs a synchronous call to a port. The meaning of
- <c>Operation</c> and <c>Data</c> depends on the port, i.e.,
+ <c><anno>Operation</anno></c> and <c><anno>Data</anno></c> depends on the port, i.e.,
on the port driver. Not all port drivers support this feature.</p>
- <p><c>Port</c> is a port identifier, referring to a driver.</p>
- <p><c>Operation</c> is an integer, which is passed on to
+ <p><c><anno>Port</anno></c> is a port identifier, referring to a driver.</p>
+ <p><c><anno>Operation</anno></c> is an integer, which is passed on to
the driver.</p>
- <p><c>Data</c> is any Erlang term. This data is converted to
+ <p><c><anno>Data</anno></c> is any Erlang term. This data is converted to
binary term format and sent to the port.</p>
<p>Returns: a term from the driver. The meaning of the returned
data also depends on the port driver.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not an open port or
- the registered name of an open port, if <c>Operation</c>
+ <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not an open port or
+ the registered name of an open port, if <c><anno>Operation</anno></c>
cannot fit in a 32-bit integer, if the port driver does not
support synchronous control operations, or if the port driver
so decides for any reason (probably something wrong with
- <c>Operation</c> or <c>Data</c>).</p>
+ <c><anno>Operation</anno></c> or <c><anno>Data</anno></c>).</p>
</desc>
</func>
<func>
- <name>erlang:port_info(Port) -> [{Item, Info}] | undefined</name>
+ <name name="port_info" arity="1"/>
<fsummary>Information about a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Item, Info -- see below</v>
- </type>
<desc>
<p>Returns a list containing tuples with information about
- the <c>Port</c>, or <c>undefined</c> if the port is not open.
+ 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, RegName}</c></tag>
- <item>
- <p><c>RegName</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, Index}</c></tag>
- <item>
- <p><c>Index</c> (an integer) is the internal index of the
- port. This index may be used to separate ports.</p>
- </item>
- <tag><c>{connected, Pid}</c></tag>
- <item>
- <p><c>Pid</c> is the process connected to the port.</p>
- </item>
- <tag><c>{links, Pids}</c></tag>
- <item>
- <p><c>Pids</c> is a list of pids to which processes the
- port is linked.</p>
- </item>
- <tag><c>{name, String}</c></tag>
- <item>
- <p><c>String</c> is the command name set by
- <c>open_port</c>.</p>
- </item>
- <tag><c>{input, Bytes}</c></tag>
- <item>
- <p><c>Bytes</c> is the total number of bytes read from
- the port.</p>
- </item>
- <tag><c>{output, Bytes}</c></tag>
- <item>
- <p><c>Bytes</c> is the total number of bytes written to
- the port.</p>
- </item>
- <tag><c>{os_pid, Integer | undefined}</c></tag>
- <item>
- <p><c>Integer</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>Port</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>erlang:port_info(Port, Item) -> {Item, Info} | undefined | []</name>
- <fsummary>Information about a port</fsummary>
- <type>
- <v>Port = port() | atom()</v>
- <v>Item, Info -- see below</v>
- </type>
+ <name name="port_info" arity="2" clause_i="1"/>
+ <fsummary>Information about the connected process of a port</fsummary>
+ <desc>
+ <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>Returns information about <c>Port</c> as specified
- by <c>Item</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, [] is returned.</p>
- <p>For valid values of <c>Item</c>, and corresponding
- values of <c>Info</c>, see
- <seealso marker="#port_info/1">erlang:port_info/1</seealso>.</p>
- <p>Failure: <c>badarg</c> if <c>Port</c> is not a local port.</p>
+ <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>erlang:port_to_list(Port) -> string()</name>
+ <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>
+ <name name="port_to_list" arity="1"/>
<fsummary>Text representation of a port identifier</fsummary>
- <type>
- <v>Port = port()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of the port identifier <c>Port</c>.</p>
+ representation of the port identifier <c><anno>Port</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -3623,18 +3443,18 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:ports() -> [port()]</name>
+ <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>
- <name>pre_loaded() -> [Module]</name>
+ <name name="pre_loaded" arity="0"/>
<fsummary>List of all pre-loaded modules</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
<p>Returns a list of Erlang modules which are pre-loaded in
the system. As all loading of code is done through the file
@@ -3643,220 +3463,222 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:process_display(Pid, Type) -> void()</name>
+ <name name="process_display" arity="2"/>
<fsummary>Write information about a local process on standard error</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Type = backtrace</v>
- </type>
<desc>
- <p>Writes information about the local process <c>Pid</c> on
+ <p>Writes information about the local process <c><anno>Pid</anno></c> on
standard error. The currently allowed value for the atom
- <c>Type</c> is <c>backtrace</c>, which shows the contents of
+ <c><anno>Type</anno></c> is <c>backtrace</c>, which shows the contents of
the call stack, including information about the call chain, with
the current function printed first. The format of the output
is not further defined.</p>
</desc>
</func>
<func>
- <name>process_flag(Flag, Value) -> OldValue</name>
- <fsummary>Set process flags for the calling process</fsummary>
- <type>
- <v>Flag, Value, OldValue -- see below</v>
- </type>
+ <name name="process_flag" arity="2" clause_i="1"/>
+ <fsummary>Set process flag trap_exit for the calling process</fsummary>
<desc>
- <p>Sets certain flags for the process which calls this
- function. Returns the old value of the flag.</p>
- <taglist>
- <tag><c>process_flag(trap_exit, Boolean)</c></tag>
- <item>
- <p>When <c>trap_exit</c> is set to <c>true</c>, exit signals
- arriving to a process are converted to <c>{'EXIT', From, Reason}</c> messages, which can be received as ordinary
- messages. If <c>trap_exit</c> is set to <c>false</c>, the
- process exits if it receives an exit signal other than
- <c>normal</c> and the exit signal is propagated to its
- linked processes. Application processes should normally
- not trap exits.</p>
- <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p>
- </item>
- <tag><c>process_flag(error_handler, Module)</c></tag>
- <item>
- <p>This is used by a process to redefine the error handler
- for undefined function calls and undefined registered
- processes. Inexperienced users should not use this flag
- since code auto-loading is dependent on the correct
- operation of the error handling module.</p>
- </item>
- <tag><c>process_flag(min_heap_size, MinHeapSize)</c></tag>
- <item>
- <p>This changes the minimum heap size for the calling
- process.</p>
- </item>
- <tag><c>process_flag(min_bin_vheap_size, MinBinVHeapSize)</c></tag>
- <item>
- <p>This changes the minimum binary virtual heap size for the calling
- process.</p>
- </item>
- <tag><marker id="process_flag_priority"><c>process_flag(priority, Level)</c></marker></tag>
- <item>
- <p>This sets the process priority. <c>Level</c> is an atom.
- There are currently four priority levels: <c>low</c>,
- <c>normal</c>, <c>high</c>, and <c>max</c>. The default
- priority level is <c>normal</c>. <em>NOTE</em>: The
- <c>max</c> priority level is reserved for internal use in
- the Erlang runtime system, and should <em>not</em> be used
- by others.
- </p>
- <p>Internally in each priority level processes are scheduled
- in a round robin fashion.
- </p>
- <p>Execution of processes on priority <c>normal</c> and
- priority <c>low</c> will be interleaved. Processes on
- priority <c>low</c> will be selected for execution less
- frequently than processes on priority <c>normal</c>.
- </p>
- <p>When there are runnable processes on priority <c>high</c>
- no processes on priority <c>low</c>, or <c>normal</c> will
- be selected for execution. Note, however, that this does
- <em>not</em> mean that no processes on priority <c>low</c>,
- or <c>normal</c> will be able to run when there are
- processes on priority <c>high</c> running. On the runtime
- system with SMP support there might be more processes running
- in parallel than processes on priority <c>high</c>, i.e.,
- a <c>low</c>, and a <c>high</c> priority process might
- execute at the same time.
- </p>
- <p>When there are runnable processes on priority <c>max</c>
- no processes on priority <c>low</c>, <c>normal</c>, or
- <c>high</c> will be selected for execution. As with the
- <c>high</c> priority, processes on lower priorities might
- execute in parallel with processes on priority <c>max</c>.
- </p>
- <p>Scheduling is preemptive. Regardless of priority, a process
- is preempted when it has consumed more than a certain amount
- of reductions since the last time it was selected for
- execution.
- </p>
- <p><em>NOTE</em>: You should not depend on the scheduling
- to remain exactly as it is today. Scheduling, at least on
- the runtime system with SMP support, is very likely to be
- modified in the future in order to better utilize available
- processor cores.
- </p>
- <p>There is currently <em>no</em> automatic mechanism for
- avoiding priority inversion, such as priority inheritance,
- or priority ceilings. When using priorities you have
- to take this into account and handle such scenarios by
- yourself.
- </p>
- <p>Making calls from a <c>high</c> priority process into code
- that you don't have control over may cause the <c>high</c>
- priority process to wait for a processes with lower
- priority, i.e., effectively decreasing the priority of the
- <c>high</c> priority process during the call. Even if this
- isn't the case with one version of the code that you don't
- have under your control, it might be the case in a future
- version of it. This might, for example, happen if a
- <c>high</c> priority process triggers code loading, since
- the code server runs on priority <c>normal</c>.
- </p>
- <p>Other priorities than <c>normal</c> are normally not needed.
- When other priorities are used, they need to be used
- with care, especially the <c>high</c> priority <em>must</em>
- be used with care. A process on <c>high</c> priority should
- only perform work for short periods of time. Busy looping for
- long periods of time in a <c>high</c> priority process will
- most likely cause problems, since there are important servers
- in OTP running on priority <c>normal</c>.
- </p>
- </item>
-
- <tag><c>process_flag(save_calls, N)</c></tag>
- <item>
- <p><c>N</c> must be an integer in the interval 0..10000.
- If <c>N</c> &gt; 0, call saving is made active for the
- process, which means that information about the <c>N</c>
- most recent global function calls, BIF calls, sends and
- receives made by the process are saved in a list, which
- can be retrieved with
- <c>process_info(Pid, last_calls)</c>. A global function
- call is one in which the module of the function is
- explicitly mentioned. Only a fixed amount of information
- is saved: a tuple <c>{Module, Function, Arity}</c> for
- function calls, and the mere atoms <c>send</c>,
- <c>'receive'</c> and <c>timeout</c> for sends and receives
- (<c>'receive'</c> when a message is received and
- <c>timeout</c> when a receive times out). If <c>N</c> = 0,
- call saving is disabled for the process, which is the
- default. Whenever the size of the call saving list is set,
- its contents are reset.</p>
- </item>
- <tag><c>process_flag(sensitive, Boolean)</c></tag>
- <item>
- <p>Set or clear the <c>sensitive</c> flag for the current process.
- When a process has been marked as sensitive by calling
- <c>process_flag(sensitive, true)</c>, features in the run-time
- system that can be used for examining the data and/or inner working
- of the process are silently disabled.</p>
- <p>Features that are disabled include (but are not limited to)
- the following:</p>
- <p>Tracing: Trace flags can still be set for the process, but no
- trace messages of any kind will be generated.
- (If the <c>sensitive</c> flag is turned off, trace messages will
- again be generated if there are any trace flags set.)</p>
- <p>Sequential tracing: The sequential trace token will be propagated
- as usual, but no sequential trace messages will be generated.</p>
- <p><c>process_info/1,2</c> cannot be used to read out the message
- queue or the process dictionary (both will be returned as empty lists).</p>
- <p>Stack back-traces cannot be displayed for the process.</p>
- <p>In crash dumps, the stack, messages, and the process dictionary
- will be omitted.</p>
- <p>If <c>{save_calls,N}</c> has been set for the process, no
- function calls will be saved to the call saving list.
- (The call saving list will not be cleared; furthermore, send, receive,
- and timeout events will still be added to the list.)</p>
- </item>
- </taglist>
+ <p>When <c>trap_exit</c> is set to <c>true</c>, exit signals
+ arriving to a process are converted to <c>{'EXIT', From, Reason}</c> messages, which can be received as ordinary
+ messages. If <c>trap_exit</c> is set to <c>false</c>, the
+ process exits if it receives an exit signal other than
+ <c>normal</c> and the exit signal is propagated to its
+ linked processes. Application processes should normally
+ not trap exits.</p>
+ <p>Returns the old value of the flag.</p>
+ <p>See also <seealso marker="#exit/2">exit/2</seealso>.</p>
</desc>
</func>
<func>
- <name>process_flag(Pid, Flag, Value) -> OldValue</name>
+ <name name="process_flag" arity="2" clause_i="2"/>
+ <fsummary>Set process flag error_handler for the calling process</fsummary>
+ <desc>
+ <p>This is used by a process to redefine the error handler
+ for undefined function calls and undefined registered
+ processes. Inexperienced users should not use this flag
+ since code auto-loading is dependent on the correct
+ operation of the error handling module.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="3"/>
+ <fsummary>Set process flag min_heap_size for the calling process</fsummary>
+ <desc>
+ <p>This changes the minimum heap size for the calling
+ process.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="4"/>
+ <fsummary>Set process flag min_bin_vheap_size for the calling process</fsummary>
+ <desc>
+ <p>This changes the minimum binary virtual heap size for the calling
+ process.</p>
+ <p>Returns the old value of the flag.</p> </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="5"/>
+ <type name="priority_level"/>
+ <fsummary>Set process flag priority for the calling process</fsummary>
+ <desc>
+ <p><marker id="process_flag_priority"></marker>
+ This sets the process priority. <c><anno>Level</anno></c> is an atom.
+ There are currently four priority levels: <c>low</c>,
+ <c>normal</c>, <c>high</c>, and <c>max</c>. The default
+ priority level is <c>normal</c>. <em>NOTE</em>: The
+ <c>max</c> priority level is reserved for internal use in
+ the Erlang runtime system, and should <em>not</em> be used
+ by others.
+ </p>
+ <p>Internally in each priority level processes are scheduled
+ in a round robin fashion.
+ </p>
+ <p>Execution of processes on priority <c>normal</c> and
+ priority <c>low</c> will be interleaved. Processes on
+ priority <c>low</c> will be selected for execution less
+ frequently than processes on priority <c>normal</c>.
+ </p>
+ <p>When there are runnable processes on priority <c>high</c>
+ no processes on priority <c>low</c>, or <c>normal</c> will
+ be selected for execution. Note, however, that this does
+ <em>not</em> mean that no processes on priority <c>low</c>,
+ or <c>normal</c> will be able to run when there are
+ processes on priority <c>high</c> running. On the runtime
+ system with SMP support there might be more processes running
+ in parallel than processes on priority <c>high</c>, i.e.,
+ a <c>low</c>, and a <c>high</c> priority process might
+ execute at the same time.
+ </p>
+ <p>When there are runnable processes on priority <c>max</c>
+ no processes on priority <c>low</c>, <c>normal</c>, or
+ <c>high</c> will be selected for execution. As with the
+ <c>high</c> priority, processes on lower priorities might
+ execute in parallel with processes on priority <c>max</c>.
+ </p>
+ <p>Scheduling is preemptive. Regardless of priority, a process
+ is preempted when it has consumed more than a certain amount
+ of reductions since the last time it was selected for
+ execution.
+ </p>
+ <p><em>NOTE</em>: You should not depend on the scheduling
+ to remain exactly as it is today. Scheduling, at least on
+ the runtime system with SMP support, is very likely to be
+ modified in the future in order to better utilize available
+ processor cores.
+ </p>
+ <p>There is currently <em>no</em> automatic mechanism for
+ avoiding priority inversion, such as priority inheritance,
+ or priority ceilings. When using priorities you have
+ to take this into account and handle such scenarios by
+ yourself.
+ </p>
+ <p>Making calls from a <c>high</c> priority process into code
+ that you don't have control over may cause the <c>high</c>
+ priority process to wait for a processes with lower
+ priority, i.e., effectively decreasing the priority of the
+ <c>high</c> priority process during the call. Even if this
+ isn't the case with one version of the code that you don't
+ have under your control, it might be the case in a future
+ version of it. This might, for example, happen if a
+ <c>high</c> priority process triggers code loading, since
+ the code server runs on priority <c>normal</c>.
+ </p>
+ <p>Other priorities than <c>normal</c> are normally not needed.
+ When other priorities are used, they need to be used
+ with care, especially the <c>high</c> priority <em>must</em>
+ be used with care. A process on <c>high</c> priority should
+ only perform work for short periods of time. Busy looping for
+ long periods of time in a <c>high</c> priority process will
+ most likely cause problems, since there are important servers
+ in OTP running on priority <c>normal</c>.
+ </p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="6"/>
+ <fsummary>Set process flag save_calls for the calling process</fsummary>
+ <desc>
+ <p><c><anno>N</anno></c> must be an integer in the interval 0..10000.
+ If <c><anno>N</anno></c> &gt; 0, call saving is made active for the
+ process, which means that information about the <c><anno>N</anno></c>
+ most recent global function calls, BIF calls, sends and
+ receives made by the process are saved in a list, which
+ can be retrieved with
+ <c>process_info(Pid, last_calls)</c>. A global function
+ call is one in which the module of the function is
+ explicitly mentioned. Only a fixed amount of information
+ is saved: a tuple <c>{Module, Function, Arity}</c> for
+ function calls, and the mere atoms <c>send</c>,
+ <c>'receive'</c> and <c>timeout</c> for sends and receives
+ (<c>'receive'</c> when a message is received and
+ <c>timeout</c> when a receive times out). If <c>N</c> = 0,
+ call saving is disabled for the process, which is the
+ default. Whenever the size of the call saving list is set,
+ its contents are reset.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="2" clause_i="7"/>
+ <fsummary>Set process flag sensitive for the calling process</fsummary>
+ <desc>
+ <p>Set or clear the <c>sensitive</c> flag for the current process.
+ When a process has been marked as sensitive by calling
+ <c>process_flag(sensitive, true)</c>, features in the run-time
+ system that can be used for examining the data and/or inner working
+ of the process are silently disabled.</p>
+ <p>Features that are disabled include (but are not limited to)
+ the following:</p>
+ <p>Tracing: Trace flags can still be set for the process, but no
+ trace messages of any kind will be generated.
+ (If the <c>sensitive</c> flag is turned off, trace messages will
+ again be generated if there are any trace flags set.)</p>
+ <p>Sequential tracing: The sequential trace token will be propagated
+ as usual, but no sequential trace messages will be generated.</p>
+ <p><c>process_info/1,2</c> cannot be used to read out the message
+ queue or the process dictionary (both will be returned as empty lists).</p>
+ <p>Stack back-traces cannot be displayed for the process.</p>
+ <p>In crash dumps, the stack, messages, and the process dictionary
+ will be omitted.</p>
+ <p>If <c>{save_calls,N}</c> has been set for the process, no
+ function calls will be saved to the call saving list.
+ (The call saving list will not be cleared; furthermore, send, receive,
+ and timeout events will still be added to the list.)</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="process_flag" arity="3"/>
<fsummary>Set process flags for a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Flag, Value, OldValue -- see below</v>
- </type>
<desc>
- <p>Sets certain flags for the process <c>Pid</c>, in the same
+ <p>Sets certain flags for the process <c><anno>Pid</anno></c>, in the same
manner as
<seealso marker="#process_flag/2">process_flag/2</seealso>.
Returns the old value of the flag. The allowed values for
- <c>Flag</c> are only a subset of those allowed in
+ <c><anno>Flag</anno></c> are only a subset of those allowed in
<c>process_flag/2</c>, namely: <c>save_calls</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Pid</c> is not a local process.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a local process.</p>
</desc>
</func>
<func>
- <name>process_info(Pid) -> InfoResult</name>
+ <name name="process_info" arity="1"/>
+ <type name="process_info_result_item"/>
+ <type name="priority_level"/>
+ <type name="stack_item"/>
<fsummary>Information about a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Item = atom()</v>
- <v>Info = term()</v>
- <v>InfoTuple = {Item, Info}</v>
- <v>InfoTupleList = [InfoTuple]</v>
- <v>InfoResult = InfoTupleList | undefined</v>
- </type>
- <desc>
- <p>Returns a list containing <c>InfoTuple</c>s with
+ <desc>
+ <p>Returns a list containing <c><anno>InfoTuple</anno></c>s with
miscellaneous information about the process identified by
<c>Pid</c>, or <c>undefined</c> if the process is not alive.
</p>
<p>
- The order of the <c>InfoTuple</c>s is not defined, nor
- are all the <c>InfoTuple</c>s mandatory. The <c>InfoTuple</c>s
+ The order of the <c><anno>InfoTuple</anno></c>s is not defined, nor
+ are all the <c><anno>InfoTuple</anno></c>s mandatory. The <c><anno>InfoTuple</anno></c>s
part of the result may be changed without prior notice.
- Currently <c>InfoTuple</c>s with the following <c>Item</c>s
+ Currently <c><anno>InfoTuple</anno></c>s with the following items
are part of the result:
<c>current_function</c>, <c>initial_call</c>, <c>status</c>,
<c>message_queue_len</c>, <c>messages</c>, <c>links</c>,
@@ -3864,12 +3686,12 @@ os_prompt% </pre>
<c>priority</c>, <c>group_leader</c>, <c>total_heap_size</c>,
<c>heap_size</c>, <c>stack_size</c>, <c>reductions</c>, and
<c>garbage_collection</c>.
- If the process identified by <c>Pid</c> has a registered name
- also an <c>InfoTuple</c> with <c>Item == registered_name</c>
+ If the process identified by <c><anno>Pid</anno></c> has a registered name
+ also an <c><anno>InfoTuple</anno></c> with the item <c>registered_name</c>
will appear.
</p>
<p>See <seealso marker="#process_info/2">process_info/2</seealso>
- for information about specific <c>InfoTuple</c>s.</p>
+ for information about specific <c><anno>InfoTuple</anno></c>s.</p>
<warning>
<p>This BIF is intended for <em>debugging only</em>, use
<seealso marker="#process_info/2">process_info/2</seealso>
@@ -3880,113 +3702,108 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>process_info(Pid, ItemSpec) -> InfoResult</name>
+ <name name="process_info" arity="2" clause_i="1"/>
+ <name name="process_info" arity="2" clause_i="2"/>
+ <type name="process_info_item"/>
+ <type name="process_info_result_item"/>
+ <type name="stack_item"/>
+ <type name="priority_level"/>
<fsummary>Information about a process</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Item = atom()</v>
- <v>Info = term()</v>
- <v>ItemList = [Item]</v>
- <v>ItemSpec = Item | ItemList</v>
- <v>InfoTuple = {Item, Info}</v>
- <v>InfoTupleList = [InfoTuple]</v>
- <v>InfoResult = InfoTuple | InfoTupleList | undefined | []</v>
- </type>
- <desc>
- <p>Returns information about the process identified by <c>Pid</c>
- as specified by the <c>ItemSpec</c>, or <c>undefined</c> if the
+ <desc>
+ <p>Returns information about the process identified by <c><anno>Pid</anno></c>
+ as specified by the <c><anno>Item</anno></c> or the <c><anno>ItemList</anno></c>, or <c>undefined</c> if the
process is not alive.
</p>
- <p>If the process is alive and <c>ItemSpec</c> is a single
- <c>Item</c>, the returned value is the corresponding
- <c>InfoTuple</c> unless <c>ItemSpec == registered_name</c>
+ <p>If the process is alive and a single <c><anno>Item</anno></c> is given,
+ the returned value is the corresponding
+ <c><anno>InfoTuple</anno></c> unless <c>Item =:= registered_name</c>
and the process has no registered name. In this case
<c>[]</c> is returned. This strange behavior is due to
historical reasons, and is kept for backward compatibility.
</p>
- <p>If <c>ItemSpec</c> is an <c>ItemList</c>, the result is an
- <c>InfoTupleList</c>. The <c>InfoTuple</c>s in the
- <c>InfoTupleList</c> will appear with the corresponding
- <c>Item</c>s in the same order as the <c>Item</c>s appeared
- in the <c>ItemList</c>. Valid <c>Item</c>s may appear multiple
- times in the <c>ItemList</c>.
+ <p>If an <c>ItemList</c> is given, the result is an
+ <c><anno>InfoTupleList</anno></c>. The <c><anno>InfoTuple</anno></c>s in the
+ <c><anno>InfoTupleList</anno></c> will appear with the corresponding
+ <c><anno>Item</anno></c>s in the same order as the <c><anno>Item</anno></c>s appeared
+ in the <c><anno>ItemList</anno></c>. Valid <c><anno>Item</anno></c>s may appear multiple
+ times in the <c><anno>ItemList</anno></c>.
</p>
- <note><p>If <c>registered_name</c> is part of an <c>ItemList</c>
+ <note><p>If <c>registered_name</c> is part of an <c><anno>ItemList</anno></c>
and the process has no name registered a
- <c>{registered_name, []}</c> <c>InfoTuple</c> <em>will</em>
- appear in the resulting <c>InfoTupleList</c>. This
- behavior is different than when
- <c>ItemSpec == registered_name</c>, and than when
+ <c>{registered_name, []}</c> <c><anno>InfoTuple</anno></c> <em>will</em>
+ appear in the resulting <c><anno>InfoTupleList</anno></c>. This
+ behavior is different than when a single
+ <c>Item =:= registered_name</c> is given, and than when
<c>process_info/1</c> is used.
</p></note>
- <p>Currently the following <c>InfoTuple</c>s with corresponding
- <c>Item</c>s are valid:</p>
+ <p>Currently the following <c><anno>InfoTuple</anno></c>s with corresponding
+ <c><anno>Item</anno></c>s are valid:</p>
<taglist>
- <tag><c>{backtrace, Bin}</c></tag>
+ <tag><c>{backtrace, <anno>Bin</anno>}</c></tag>
<item>
- <p>The binary <c>Bin</c> contains the same information as
+ <p>The binary <c><anno>Bin</anno></c> contains the same information as
the output from
- <c>erlang:process_display(Pid, backtrace)</c>. Use
+ <c>erlang:process_display(<anno>Pid</anno>, backtrace)</c>. Use
<c>binary_to_list/1</c> to obtain the string of characters
from the binary.</p>
</item>
- <tag><c>{binary, BinInfo}</c></tag>
+ <tag><c>{binary, <anno>BinInfo</anno>}</c></tag>
<item>
- <p><c>BinInfo</c> is a list containing miscellaneous information
+ <p><c><anno>BinInfo</anno></c> is a list containing miscellaneous information
about binaries currently being referred to by this process.
- This <c>InfoTuple</c> may be changed or removed without prior
+ This <c><anno>InfoTuple</anno></c> may be changed or removed without prior
notice.</p>
</item>
- <tag><c>{catchlevel, CatchLevel}</c></tag>
+ <tag><c>{catchlevel, <anno>CatchLevel</anno>}</c></tag>
<item>
- <p><c>CatchLevel</c> is the number of currently active
- catches in this process. This <c>InfoTuple</c> may be
+ <p><c><anno>CatchLevel</anno></c> is the number of currently active
+ catches in this process. This <c><anno>InfoTuple</anno></c> may be
changed or removed without prior notice.</p>
</item>
- <tag><c>{current_function, {Module, Function, Arity}}</c></tag>
+ <tag><c>{current_function, {<anno>Module</anno>, <anno>Function</anno>, <anno>Arity</anno>}}</c></tag>
<item>
- <p><c>Module</c>, <c>Function</c>, <c>Arity</c> is
+ <p><c><anno>Module</anno></c>, <c><anno>Function</anno></c>, <c><anno>Arity</anno></c> is
the current function call of the process.</p>
</item>
- <tag><c>{current_location, {Module, Function, Arity, Location}}</c></tag>
+ <tag><c>{current_location, {<anno>Module</anno>, <anno>Function</anno>, <anno>Arity</anno>, <anno>Location</anno>}}</c></tag>
<item>
- <p><c>Module</c>, <c>Function</c>, <c>Arity</c> is
+ <p><c><anno>Module</anno></c>, <c><anno>Function</anno></c>, <c><anno>Arity</anno></c> is
the current function call of the process.
- <c>Location</c> is a list of two-tuples that describes the
+ <c><anno>Location</anno></c> is a list of two-tuples that describes the
location in the source code.
</p>
</item>
- <tag><c>{current_stacktrace, Stack}</c></tag>
+ <tag><c>{current_stacktrace, <anno>Stack</anno>}</c></tag>
<item>
<p>Return the current call stack back-trace (<em>stacktrace</em>)
of the process. The stack has the same format as returned by
<seealso marker="#get_stacktrace/0">erlang:get_stacktrace/0</seealso>.
</p>
</item>
- <tag><c>{dictionary, Dictionary}</c></tag>
+ <tag><c>{dictionary, <anno>Dictionary</anno>}</c></tag>
<item>
- <p><c>Dictionary</c> is the dictionary of the process.</p>
+ <p><c><anno>Dictionary</anno></c> is the dictionary of the process.</p>
</item>
- <tag><c>{error_handler, Module}</c></tag>
+ <tag><c>{error_handler, <anno>Module</anno>}</c></tag>
<item>
- <p><c>Module</c> is the error handler module used by
+ <p><c><anno>Module</anno></c> is the error handler module used by
the process (for undefined function calls, for example).</p>
</item>
- <tag><c>{garbage_collection, GCInfo}</c></tag>
+ <tag><c>{garbage_collection, <anno>GCInfo</anno>}</c></tag>
<item>
- <p><c>GCInfo</c> is a list which contains miscellaneous
+ <p><c><anno>GCInfo</anno></c> is a list which contains miscellaneous
information about garbage collection for this process.
- The content of <c>GCInfo</c> may be changed without
+ The content of <c><anno>GCInfo</anno></c> may be changed without
prior notice.</p>
</item>
- <tag><c>{group_leader, GroupLeader}</c></tag>
+ <tag><c>{group_leader, <anno>GroupLeader</anno>}</c></tag>
<item>
- <p><c>GroupLeader</c> is group leader for the IO of
+ <p><c><anno>GroupLeader</anno></c> is group leader for the IO of
the process.</p>
</item>
- <tag><c>{heap_size, Size}</c></tag>
+ <tag><c>{heap_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the size in words of youngest heap generation
+ <p><c><anno>Size</anno></c> is the size in words of youngest heap generation
of the process. This generation currently include the stack
of the process. This information is highly implementation
dependent, and may change if the implementation change.
@@ -3998,10 +3815,11 @@ os_prompt% </pre>
the initial function call with which the process was
spawned.</p>
</item>
- <tag><c>{links, PidsAndPorts}</c></tag>
+ <tag><c>{links, <anno>PidsAndPorts</anno>}</c></tag>
<item>
- <p><c>PidsAndPorts</c> is a list of pids and port identifiers, with
- processes or ports 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>
@@ -4011,131 +3829,131 @@ os_prompt% </pre>
If call saving is active, a list is returned, in which
the last element is the most recent called.</p>
</item>
- <tag><c>{memory, Size}</c></tag>
+ <tag><c>{memory, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the size in bytes of the process. This
+ <p><c><anno>Size</anno></c> is the size in bytes of the process. This
includes call stack, heap and internal structures.</p>
</item>
- <tag><c>{message_queue_len, MessageQueueLen}</c></tag>
+ <tag><c>{message_queue_len, <anno>MessageQueueLen</anno>}</c></tag>
<item>
- <p><c>MessageQueueLen</c> is the number of messages
+ <p><c><anno>MessageQueueLen</anno></c> is the number of messages
currently in the message queue of the process. This is
- the length of the list <c>MessageQueue</c> returned as
+ the length of the list <c><anno>MessageQueue</anno></c> returned as
the info item <c>messages</c> (see below).</p>
</item>
- <tag><c>{messages, MessageQueue}</c></tag>
+ <tag><c>{messages, <anno>MessageQueue</anno>}</c></tag>
<item>
- <p><c>MessageQueue</c> is a list of the messages to
+ <p><c><anno>MessageQueue</anno></c> is a list of the messages to
the process, which have not yet been processed.</p>
</item>
- <tag><c>{min_heap_size, MinHeapSize}</c></tag>
+ <tag><c>{min_heap_size, <anno>MinHeapSize</anno>}</c></tag>
<item>
- <p><c>MinHeapSize</c> is the minimum heap size for the process.</p>
+ <p><c><anno>MinHeapSize</anno></c> is the minimum heap size for the process.</p>
</item>
- <tag><c>{min_bin_vheap_size, MinBinVHeapSize}</c></tag>
+ <tag><c>{min_bin_vheap_size, <anno>MinBinVHeapSize</anno>}</c></tag>
<item>
- <p><c>MinBinVHeapSize</c> is the minimum binary virtual heap size for the process.</p>
+ <p><c><anno>MinBinVHeapSize</anno></c> is the minimum binary virtual heap size for the process.</p>
</item>
- <tag><c>{monitored_by, Pids}</c></tag>
+ <tag><c>{monitored_by, <anno>Pids</anno>}</c></tag>
<item>
<p>A list of pids that are monitoring the process (with
<c>monitor/2</c>).</p>
</item>
- <tag><c>{monitors, Monitors}</c></tag>
+ <tag><c>{monitors, <anno>Monitors</anno>}</c></tag>
<item>
<p>A list of monitors (started by <c>monitor/2</c>)
that are active for the process. For a local process
monitor or a remote process monitor by pid, the list item
- is <c>{process, Pid}</c>, and for a remote process
+ is <c>{process, <anno>Pid</anno>}</c>, and for a remote process
monitor by name, the list item is
- <c>{process, {RegName, Node}}</c>.</p>
+ <c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c>.</p>
</item>
<tag><c>{priority, Level}</c></tag>
<item>
- <p><c>Level</c> is the current priority level for
+ <p><c><anno>Level</anno></c> is the current priority level for
the process. For more information on priorities see
<seealso marker="#process_flag_priority">process_flag(priority, Level)</seealso>.</p>
</item>
- <tag><c>{reductions, Number}</c></tag>
+ <tag><c>{reductions, <anno>Number</anno>}</c></tag>
<item>
- <p><c>Number</c> is the number of reductions executed by
+ <p><c><anno>Number</anno></c> is the number of reductions executed by
the process.</p>
</item>
- <tag><c>{registered_name, Atom}</c></tag>
+ <tag><c>{registered_name, <anno>Atom</anno>}</c></tag>
<item>
- <p><c>Atom</c> is the registered name of the process. If
+ <p><c><anno>Atom</anno></c> is the registered name of the process. If
the process has no registered name, this tuple is not
present in the list.</p>
</item>
- <tag><c>{sequential_trace_token, [] | SequentialTraceToken}</c></tag>
+ <tag><c>{sequential_trace_token, [] | <anno>SequentialTraceToken</anno>}</c></tag>
<item>
- <p><c>SequentialTraceToken</c> the sequential trace token for
- the process. This <c>InfoTuple</c> may be changed or removed
+ <p><c><anno>SequentialTraceToken</anno></c> the sequential trace token for
+ the process. This <c><anno>InfoTuple</anno></c> may be changed or removed
without prior notice.</p>
</item>
- <tag><c>{stack_size, Size}</c></tag>
+ <tag><c>{stack_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the stack size of the process in words.</p>
+ <p><c><anno>Size</anno></c> is the stack size of the process in words.</p>
</item>
- <tag><c>{status, Status}</c></tag>
+ <tag><c>{status, <anno>Status</anno>}</c></tag>
<item>
- <p><c>Status</c> is the status of the process. <c>Status</c>
+ <p><c><anno>Status</anno></c> is the status of the process. <c><anno>Status</anno></c>
is <c>exiting</c>, <c>garbage_collecting</c>,
<c>waiting</c> (for a message), <c>running</c>,
<c>runnable</c> (ready to run, but another process is
running), or <c>suspended</c> (suspended on a "busy" port
or by the <c>erlang:suspend_process/[1,2]</c> BIF).</p>
</item>
- <tag><c>{suspending, SuspendeeList}</c></tag>
+ <tag><c>{suspending, <anno>SuspendeeList</anno>}</c></tag>
<item>
- <p><c>SuspendeeList</c> is a list of <c>{Suspendee,
- ActiveSuspendCount, OutstandingSuspendCount}</c> tuples.
- <c>Suspendee</c> is the pid of a process that have been or is to
- be suspended by the process identified by <c>Pid</c> via the
+ <p><c><anno>SuspendeeList</anno></c> is a list of <c>{<anno>Suspendee</anno>,
+ <anno>ActiveSuspendCount</anno>, <anno>OutstandingSuspendCount</anno>}</c> tuples.
+ <c><anno>Suspendee</anno></c> is the pid of a process that have been or is to
+ be suspended by the process identified by <c><anno>Pid</anno></c> via the
<seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>
BIF, or the
<seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
- BIF. <c>ActiveSuspendCount</c> is the number of times the
- <c>Suspendee</c> has been suspended by <c>Pid</c>.
- <c>OutstandingSuspendCount</c> is the number of not yet
- completed suspend requests sent by <c>Pid</c>. That is,
- if <c>ActiveSuspendCount /= 0</c>, <c>Suspendee</c> is
+ BIF. <c><anno>ActiveSuspendCount</anno></c> is the number of times the
+ <c><anno>Suspendee</anno></c> has been suspended by <c><anno>Pid</anno></c>.
+ <c><anno>OutstandingSuspendCount</anno></c> is the number of not yet
+ completed suspend requests sent by <c><anno>Pid</anno></c>. That is,
+ if <c><anno>ActiveSuspendCount</anno> =/= 0</c>, <c><anno>Suspendee</anno></c> is
currently in the suspended state, and if
- <c>OutstandingSuspendCount /= 0</c> the <c>asynchronous</c>
+ <c><anno>OutstandingSuspendCount</anno> =/= 0</c> the <c>asynchronous</c>
option of <c>erlang:suspend_process/2</c> has been used and
- the suspendee has not yet been suspended by <c>Pid</c>.
- Note that the <c>ActiveSuspendCount</c> and
- <c>OutstandingSuspendCount</c> are not the total suspend count
- on <c>Suspendee</c>, only the parts contributed by <c>Pid</c>.
+ the suspendee has not yet been suspended by <c><anno>Pid</anno></c>.
+ Note that the <c><anno>ActiveSuspendCount</anno></c> and
+ <c><anno>OutstandingSuspendCount</anno></c> are not the total suspend count
+ on <c><anno>Suspendee</anno></c>, only the parts contributed by <c>Pid</c>.
</p>
</item>
- <tag><c>{total_heap_size, Size}</c></tag>
+ <tag><c>{total_heap_size, <anno>Size</anno>}</c></tag>
<item>
- <p><c>Size</c> is the total size in words of all heap
+ <p><c><anno>Size</anno></c> is the total size in words of all heap
fragments of the process. This currently include the stack
of the process.
</p>
</item>
- <tag><c>{trace, InternalTraceFlags}</c></tag>
+ <tag><c>{trace, <anno>InternalTraceFlags</anno>}</c></tag>
<item>
- <p><c>InternalTraceFlags</c> is an integer representing
- internal trace flag for this process. This <c>InfoTuple</c>
+ <p><c><anno>InternalTraceFlags</anno></c> is an integer representing
+ internal trace flag for this process. This <c><anno>InfoTuple</anno></c>
may be changed or removed without prior notice.</p>
</item>
- <tag><c>{trap_exit, Boolean}</c></tag>
+ <tag><c>{trap_exit, <anno>Boolean</anno>}</c></tag>
<item>
- <p><c>Boolean</c> is <c>true</c> if the process is trapping
+ <p><c><anno>Boolean</anno></c> is <c>true</c> if the process is trapping
exits, otherwise it is <c>false</c>.</p>
</item>
</taglist>
<p>Note however, that not all implementations support every one
- of the above <c>Items</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Pid</c> is not a local process,
- or if <c>Item</c> is not a valid <c>Item</c>.</p>
+ of the above <c><anno>Item</anno></c>s.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>Pid</anno></c> is not a local process,
+ or if <c><anno>Item</anno></c> is not a valid <c><anno>Item</anno></c>.</p>
</desc>
</func>
<func>
- <name>processes() -> [pid()]</name>
+ <name name="processes" arity="0"/>
<fsummary>All processes</fsummary>
<desc>
<p>Returns a list of process identifiers corresponding to
@@ -4152,13 +3970,10 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>purge_module(Module) -> void()</name>
+ <name name="purge_module" arity="1"/>
<fsummary>Remove old code for a module</fsummary>
- <type>
- <v>Module = atom()</v>
- </type>
<desc>
- <p>Removes old code for <c>Module</c>. Before this BIF is used,
+ <p>Removes old code for <c><anno>Module</anno></c>. Before this BIF is used,
<c>erlang:check_process_code/2</c> should be called to check
that no processes are executing old code in the module.</p>
<warning>
@@ -4167,20 +3982,17 @@ os_prompt% </pre>
used elsewhere.</p>
</warning>
<p>Failure: <c>badarg</c> if there is no old code for
- <c>Module</c>.</p>
+ <c><anno>Module</anno></c>.</p>
</desc>
</func>
<func>
- <name>put(Key, Val) -> OldVal | undefined</name>
+ <name name="put" arity="2"/>
<fsummary>Add a new value to the process dictionary</fsummary>
- <type>
- <v>Key = Val = OldVal = term()</v>
- </type>
- <desc>
- <p>Adds a new <c>Key</c> to the process dictionary, associated
- with the value <c>Val</c>, and returns <c>undefined</c>. If
- <c>Key</c> already exists, the old value is deleted and
- replaced by <c>Val</c> and the function returns the old value.</p>
+ <desc>
+ <p>Adds a new <c><anno>Key</anno></c> to the process dictionary, associated
+ with the value <c><anno>Val</anno></c>, and returns <c>undefined</c>. If
+ <c><anno>Key</anno></c> already exists, the old value is deleted and
+ replaced by <c><anno>Val</anno></c> and the function returns the old value.</p>
<note>
<p>The values stored when <c>put</c> is evaluated within
the scope of a <c>catch</c> will not be retracted if a
@@ -4194,17 +4006,9 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:raise(Class, Reason, Stacktrace)</name>
+ <name name="raise" arity="3"/>
+ <type name="raise_stacktrace"/>
<fsummary>Stop execution with an exception of given class, reason and call stack backtrace</fsummary>
- <type>
- <v>Class = error | exit | throw</v>
- <v>Reason = term()</v>
- <v>Stacktrace = [{Module, Function, Arity | Args} | {Fun, Args}]</v>
- <v>&nbsp;Module = Function = atom()</v>
- <v>&nbsp;Arity = arity()</v>
- <v>&nbsp;Args = [term()]</v>
- <v>&nbsp;Fun = [fun()]</v>
- </type>
<desc>
<p>Stops the execution of the calling process with an
exception of given class, reason and call stack backtrace
@@ -4215,11 +4019,11 @@ os_prompt% </pre>
be avoided in applications, unless you know
very well what you are doing.</p>
</warning>
- <p><c>Class</c> is one of <c>error</c>, <c>exit</c> or
+ <p><c><anno>Class</anno></c> is one of <c>error</c>, <c>exit</c> or
<c>throw</c>, so if it were not for the stacktrace
- <c>erlang:raise(Class, Reason, Stacktrace)</c> is
- equivalent to <c>erlang:Class(Reason)</c>.
- <c>Reason</c> is any term and <c>Stacktrace</c> is a list as
+ <c>erlang:raise(<anno>Class</anno>, <anno>Reason</anno>, <anno>Stacktrace</anno>)</c> is
+ equivalent to <c>erlang:<anno>Class</anno>(<anno>Reason</anno>)</c>.
+ <c><anno>Reason</anno></c> is any term and <c><anno>Stacktrace</anno></c> is a list as
returned from <c>get_stacktrace()</c>, that is a list of
4-tuples <c>{Module, Function, Arity | Args,
Location}</c> where <c>Module</c> and <c>Function</c>
@@ -4236,24 +4040,21 @@ os_prompt% </pre>
terminate, it has no return value - unless the arguments are
invalid, in which case the function <em>returns the error reason</em>, that is <c>badarg</c>. If you want to be
really sure not to return you can call
- <c>error(erlang:raise(Class, Reason, Stacktrace))</c>
+ <c>error(erlang:raise(<anno>Class</anno>, <anno>Reason</anno>, <anno>Stacktrace</anno>))</c>
and hope to distinguish exceptions later.</p>
</desc>
</func>
<func>
- <name>erlang:read_timer(TimerRef) -> integer() >= 0 | false</name>
+ <name name="read_timer" arity="1"/>
<fsummary>Number of milliseconds remaining for a timer</fsummary>
- <type>
- <v>TimerRef = reference()</v>
- </type>
<desc>
- <p><c>TimerRef</c> is a timer reference returned by
+ <p><c><anno>TimerRef</anno></c> is a timer reference returned by
<seealso marker="#send_after/3">erlang:send_after/3</seealso>
or
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>.
If the timer is active, the function returns the time in
milliseconds left until the timer will expire, otherwise
- <c>false</c> (which means that <c>TimerRef</c> was never a
+ <c>false</c> (which means that <c><anno>TimerRef</anno></c> was never a
timer, that it has been cancelled, or that it has already
delivered its message).</p>
<p>See also
@@ -4264,14 +4065,11 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>erlang:ref_to_list(Ref) -> string()</name>
+ <name name="ref_to_list" arity="1"/>
<fsummary>Text representation of a reference</fsummary>
- <type>
- <v>Ref = reference()</v>
- </type>
<desc>
<p>Returns a string which corresponds to the text
- representation of <c>Ref</c>.</p>
+ representation of <c><anno>Ref</anno></c>.</p>
<warning>
<p>This BIF is intended for debugging and for use in
the Erlang operating system. It should not be used in
@@ -4280,33 +4078,25 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name>register(RegName, Pid | Port) -> true</name>
+ <name name="register" arity="2"/>
<fsummary>Register a name for a pid (or port)</fsummary>
- <type>
- <v>RegName = atom()</v>
- <v>Pid = pid()</v>
- <v>Port = port()</v>
- </type>
- <desc>
- <p>Associates the name <c>RegName</c> with a pid or a port
- identifier. <c>RegName</c>, which must be an atom, can be used
+ <desc>
+ <p>Associates the name <c><anno>RegName</anno></c> with a pid or a port
+ identifier. <c><anno>RegName</anno></c>, which must be an atom, can be used
instead of the pid / port identifier in the send operator
- (<c>RegName ! Message</c>).</p>
+ (<c><anno>RegName</anno> ! Message</c>).</p>
<pre>
> <input>register(db, Pid).</input>
true</pre>
- <p>Failure: <c>badarg</c> if <c>Pid</c> is not an existing,
- local process or port, if <c>RegName</c> is already in use,
+ <p>Failure: <c>badarg</c> if <c><anno>PidOrPort</anno></c> is not an existing,
+ local process or port, if <c><anno>RegName</anno></c> is already in use,
if the process or port is already registered (already has a
- name), or if <c>RegName</c> is the atom <c>undefined</c>.</p>
+ name), or if <c><anno>RegName</anno></c> is the atom <c>undefined</c>.</p>
</desc>
</func>
<func>
- <name>registered() -> [RegName]</name>
+ <name name="registered" arity="0"/>
<fsummary>All registered names</fsummary>
- <type>
- <v>RegName = atom()</v>
- </type>
<desc>
<p>Returns a list of names which have been registered using
<seealso marker="#register/2">register/2</seealso>.</p>
@@ -4316,22 +4106,19 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:resume_process(Suspendee) -> true</name>
+ <name name="resume_process" arity="1"/>
<fsummary>Resume a suspended process</fsummary>
- <type>
- <v>Suspendee = pid()</v>
- </type>
<desc>
<p>Decreases the suspend count on the process identified by
- <c>Suspendee</c>. <c>Suspendee</c> should previously have been
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> should previously have been
suspended via
<seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>,
or
<seealso marker="#suspend_process/1">erlang:suspend_process/1</seealso>
- by the process calling <c>erlang:resume_process(Suspendee)</c>. When
- the suspend count on <c>Suspendee</c> reach zero, <c>Suspendee</c>
+ by the process calling <c>erlang:resume_process(<anno>Suspendee</anno>)</c>. When
+ the suspend count on <c><anno>Suspendee</anno></c> reach zero, <c><anno>Suspendee</anno></c>
will be resumed, i.e., the state of the <c>Suspendee</c> is changed
- from suspended into the state <c>Suspendee</c> was in before it was
+ from suspended into the state <c><anno>Suspendee</anno></c> was in before it was
suspended.
</p>
<warning>
@@ -4341,29 +4128,26 @@ true</pre>
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Suspendee</c> isn't a process identifier.
+ If <c><anno>Suspendee</anno></c> isn't a process identifier.
</item>
<tag><c>badarg</c></tag>
<item>
If the process calling <c>erlang:resume_process/1</c> had
not previously increased the suspend count on the process
- identified by <c>Suspendee</c>.
+ identified by <c><anno>Suspendee</anno></c>.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> is not alive.
+ If the process identified by <c><anno>Suspendee</anno></c> is not alive.
</item>
</taglist>
</desc>
</func>
<func>
- <name>round(Number) -> integer()</name>
+ <name name="round" arity="1"/>
<fsummary>Return an integer by rounding a number</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
- <p>Returns an integer by rounding <c>Number</c>.</p>
+ <p>Returns an integer by rounding <c><anno>Number</anno></c>.</p>
<pre>
> <input>round(5.5).</input>
6</pre>
@@ -4371,7 +4155,7 @@ true</pre>
</desc>
</func>
<func>
- <name>self() -> pid()</name>
+ <name name="self" arity="0"/>
<fsummary>Pid of the calling process</fsummary>
<desc>
<p>Returns the pid (process identifier) of the calling process.</p>
@@ -4382,33 +4166,21 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:send(Dest, Msg) -> Msg</name>
+ <name name="send" arity="2"/>
<fsummary>Send a message</fsummary>
- <type>
- <v>Dest = pid() | port() | RegName | {RegName, Node}</v>
- <v>Msg = term()</v>
- <v>&nbsp;RegName = atom()</v>
- <v>&nbsp;Node = node()</v>
- </type>
- <desc>
- <p>Sends a message and returns <c>Msg</c>. This is the same as
- <c>Dest ! Msg</c>.</p>
- <p><c>Dest</c> may be a remote or local pid, a (local) port, a
- locally registered name, or a tuple <c>{RegName, Node}</c>
+ <type name="dst"/>
+ <desc>
+ <p>Sends a message and returns <c><anno>Msg</anno></c>. This is the same as
+ <c><anno>Dest</anno> ! <anno>Msg</anno></c>.</p>
+ <p><c><anno>Dest</anno></c> may be a remote or local pid, a (local) port, a
+ locally registered name, or a tuple <c>{<anno>RegName</anno>, <anno>Node</anno>}</c>
for a registered name at another node.</p>
</desc>
</func>
<func>
- <name>erlang:send(Dest, Msg, [Option]) -> Res</name>
+ <name name="send" arity="3"/>
+ <type name="dst"/>
<fsummary>Send a message conditionally</fsummary>
- <type>
- <v>Dest = pid() | port() | RegName | {RegName, Node}</v>
- <v>&nbsp;RegName = atom()</v>
- <v>&nbsp;Node = node()</v>
- <v>Msg = term()</v>
- <v>Option = nosuspend | noconnect</v>
- <v>Res = ok | nosuspend | noconnect</v>
- </type>
<desc>
<p>Sends a message and returns <c>ok</c>, or does not send
the message but returns something else (see below). Otherwise
@@ -4438,28 +4210,24 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:send_after(Time, Dest, Msg) -> TimerRef</name>
+ <name name="send_after" arity="3"/>
+ <type_desc variable="Time">0 &lt;= Time &lt;= 4294967295</type_desc>
<fsummary>Start a timer</fsummary>
- <type>
- <v>Time = integer() >= 0</v>
- <v>&nbsp;0 &lt;= Time &lt;= 4294967295</v>
- <v>Dest = pid() | RegName </v>
- <v>&nbsp;LocalPid = pid() (of a process, alive or dead, on the local node)</v>
- <v>Msg = term()</v>
- <v>TimerRef = reference()</v>
- </type>
<desc>
<p>Starts a timer which will send the message <c>Msg</c>
- to <c>Dest</c> after <c>Time</c> milliseconds.</p>
- <p>If <c>Dest</c> is an atom, it is supposed to be the name of
+ to <c><anno>Dest</anno></c> after <c><anno>Time</anno></c> milliseconds.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to be a <c>pid()</c> of a local process, dead or alive.</p>
+ <p>The <c><anno>Time</anno></c> value can, in the current implementation, not be greater than 4294967295.</p>
+ <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of
a registered process. The process referred to by the name is
looked up at the time of delivery. No error is given if
the name does not refer to a process.</p>
- <p>If <c>Dest</c> is a pid, the timer will be automatically
- canceled if the process referred to by the pid is not alive,
+
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically
+ canceled if the process referred to by the <c>pid()</c> is not alive,
or when the process exits. This feature was introduced in
erts version 5.4.11. Note that timers will not be
- automatically canceled when <c>Dest</c> is an atom.</p>
+ automatically canceled when <c><anno>Dest</anno></c> is an <c>atom</c>.</p>
<p>See also
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>,
<seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>,
@@ -4559,32 +4327,25 @@ true</pre>
</desc>
</func>
<func>
- <name>setelement(Index, Tuple1, Value) -> Tuple2</name>
+ <name name="setelement" arity="3"/>
+ <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc>
<fsummary>Set Nth element of a tuple</fsummary>
- <type>
- <v>Index = 1..tuple_size(Tuple1)</v>
- <v>Tuple1 = Tuple2 = tuple()</v>
- <v>Value = term()</v>
- </type>
- <desc>
- <p>Returns a tuple which is a copy of the argument <c>Tuple1</c>
- with the element given by the integer argument <c>Index</c>
+ <desc>
+ <p>Returns a tuple which is a copy of the argument <c><anno>Tuple1</anno></c>
+ with the element given by the integer argument <c><anno>Index</anno></c>
(the first element is the element with index 1) replaced by
- the argument <c>Value</c>.</p>
+ the argument <c><anno>Value</anno></c>.</p>
<pre>
> <input>setelement(2, {10, green, bottles}, red).</input>
{10,red,bottles}</pre>
</desc>
</func>
<func>
- <name>size(Item) -> integer() >= 0</name>
+ <name name="size" arity="1"/>
<fsummary>Size of a tuple or binary</fsummary>
- <type>
- <v>Item = tuple() | binary()</v>
- </type>
<desc>
<p>Returns an integer which is the size of the argument
- <c>Item</c>, which must be either a tuple or a binary.</p>
+ <c><anno>Item</anno></c>, which must be either a tuple or a binary.</p>
<pre>
> <input>size({morni, mulle, bwange}).</input>
3</pre>
@@ -4612,20 +4373,16 @@ true</pre>
</desc>
</func>
<func>
- <name>spawn(Module, Function, Args) -> pid()</name>
+ <name name="spawn" arity="3"/>
<fsummary>Create a new process with a function as entry point</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Returns the pid of a new process started by the application
- of <c>Module:Function</c> to <c>Args</c>. The new process
+ of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c>. The new process
created will be placed in the system scheduler queue and be
run some time later.</p>
- <p><c>error_handler:undefined_function(Module, Function, Args)</c> is evaluated by the new process if
- <c>Module:Function/Arity</c> does not exist (where
- <c>Arity</c> is the length of <c>Args</c>). The error handler
+ <p><c>error_handler:undefined_function(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> is evaluated by the new process if
+ <c><anno>Module</anno>:<anno>Function</anno>/Arity</c> does not exist (where
+ <c>Arity</c> is the length of <c><anno>Args</anno></c>). The error handler
can be redefined (see
<seealso marker="#process_flag/2">process_flag/2</seealso>).
If <c>error_handler</c> is undefined, or the user has
@@ -4672,15 +4429,11 @@ true</pre>
</desc>
</func>
<func>
- <name>spawn_link(Module, Function, Args) -> pid()</name>
+ <name name="spawn_link" arity="3"/>
<fsummary>Create and link to a new process with a function as entry point</fsummary>
- <type>
- <v>Module = Function = atom()</v>
- <v>Args = [term()]</v>
- </type>
<desc>
<p>Returns the pid of a new process started by the application
- of <c>Module:Function</c> to <c>Args</c>. A link is created
+ of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c>. A link is created
between the calling process and the new process, atomically.
Otherwise works like
<seealso marker="#spawn/3">spawn/3</seealso>.</p>
@@ -4859,15 +4612,12 @@ true</pre>
</desc>
</func>
<func>
- <name>split_binary(Bin, Pos) -> {Bin1, Bin2}</name>
+ <name name="split_binary" arity="2"/>
+ <type_desc variable="Pos">0..byte_size(Bin)</type_desc>
<fsummary>Split a binary into two</fsummary>
- <type>
- <v>Bin = Bin1 = Bin2 = binary()</v>
- <v>Pos = 0..byte_size(Bin)</v>
- </type>
<desc>
<p>Returns a tuple containing the binaries which are the result
- of splitting <c>Bin</c> into two parts at position <c>Pos</c>.
+ of splitting <c><anno>Bin</anno></c> into two parts at position <c><anno>Pos</anno></c>.
This is not a destructive operation. After the operation,
there will be three binaries altogether.</p>
<pre>
@@ -4884,30 +4634,24 @@ true</pre>
</desc>
</func>
<func>
- <name>erlang:start_timer(Time, Dest, Msg) -> TimerRef</name>
+ <name name="start_timer" arity="3"/>
+ <type_desc variable="Time">0 &lt;= Time &lt;= 4294967295</type_desc>
<fsummary>Start a timer</fsummary>
- <type>
- <v>Time = integer() >= 0</v>
- <v>&nbsp;0 &lt;= Time &lt;= 4294967295</v>
- <v>Dest = LocalPid | RegName </v>
- <v>&nbsp;LocalPid = pid() (of a process, alive or dead, on the local node)</v>
- <v>&nbsp;RegName = atom()</v>
- <v>Msg = term()</v>
- <v>TimerRef = reference()</v>
- </type>
<desc>
<p>Starts a timer which will send the message
- <c>{timeout, TimerRef, Msg}</c> to <c>Dest</c>
- after <c>Time</c> milliseconds.</p>
- <p>If <c>Dest</c> is an atom, it is supposed to be the name of
+ <c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c> to <c><anno>Dest</anno></c>
+ after <c><anno>Time</anno></c> milliseconds.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to be a <c>pid()</c> of a local process, dead or alive.</p>
+ <p>The <c><anno>Time</anno></c> value can, in the current implementation, not be greater than 4294967295.</p>
+ <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of
a registered process. The process referred to by the name is
looked up at the time of delivery. No error is given if
the name does not refer to a process.</p>
- <p>If <c>Dest</c> is a pid, the timer will be automatically
- canceled if the process referred to by the pid is not alive,
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically
+ canceled if the process referred to by the <c>pid()</c> is not alive,
or when the process exits. This feature was introduced in
erts version 5.4.11. Note that timers will not be
- automatically canceled when <c>Dest</c> is an atom.</p>
+ automatically canceled when <c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
<p>See also
<seealso marker="#send_after/3">erlang:send_after/3</seealso>,
<seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>,
@@ -4918,54 +4662,52 @@ true</pre>
</desc>
</func>
<func>
- <name>statistics(Type) -> Res</name>
- <fsummary>Information about the system</fsummary>
- <type>
- <v>Type, Res -- see below</v>
- </type>
+ <name name="statistics" arity="1" clause_i="1"/>
+ <fsummary>Information about context switches</fsummary>
<desc>
- <p>All times are in milliseconds unless otherwise specified.</p>
- <p>Returns information about the system as specified by
- <c>Type</c>:</p>
- <taglist>
- <tag><c>context_switches</c></tag>
- <item>
- <p>Returns <c>{ContextSwitches, 0}</c>, where
- <c>ContextSwitches</c> is the total number of context
- switches since the system started.</p>
- </item>
- <tag><marker id="statistics_exact_reductions"><c>exact_reductions</c></marker></tag>
- <item>
- <p>Returns
- <c>{Total_Exact_Reductions, Exact_Reductions_Since_Last_Call}</c>.</p>
- <note><p><c>statistics(exact_reductions)</c> is
- a more expensive operation than
- <seealso marker="#statistics_reductions">statistics(reductions)</seealso>
- especially on an Erlang machine with SMP support.</p>
- </note>
- </item>
- <tag><c>garbage_collection</c></tag>
- <item>
- <p>Returns <c>{Number_of_GCs, Words_Reclaimed, 0}</c>. This
- information may not be valid for all implementations.</p>
- <pre>
+ <p><c><anno>ContextSwitches</anno></c> is the total number of context
+ switches since the system started.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="2"/>
+ <fsummary>Information about exact reductions</fsummary>
+ <desc>
+ <marker id="statistics_exact_reductions"></marker>
+ <note><p><c>statistics(exact_reductions)</c> is
+ a more expensive operation than
+ <seealso marker="#statistics_reductions">statistics(reductions)</seealso>
+ especially on an Erlang machine with SMP support.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="3"/>
+ <fsummary>Information about garbage collection</fsummary>
+ <desc>
+ <p>This information may not be valid for all implementations.</p>
+ <pre>
> <input>statistics(garbage_collection).</input>
{85,23961,0}
</pre>
- </item>
- <tag><c>io</c></tag>
- <item>
- <p>Returns <c>{{input, Input}, {output, Output}}</c>,
- where <c>Input</c> is the total number of bytes received
- through ports, and <c>Output</c> is the total number of
- bytes output to ports.</p>
- </item>
- <tag><marker id="statistics_reductions"><c>reductions</c></marker></tag>
- <item>
- <p>Returns
- <c>{Total_Reductions, Reductions_Since_Last_Call}</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="4"/>
+ <fsummary>Information about io</fsummary>
+ <desc>
+ <p><c><anno>Input</anno></c> is the total number of bytes received
+ through ports, and <c><anno>Output</anno></c> is the total number of
+ bytes output to ports.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="5"/>
+ <fsummary>Information about reductions</fsummary>
+ <desc>
+ <marker id="statistics_reductions"></marker>
<note>
- <p>From erts version 5.5 (OTP release R11B)
+ <p>Since erts-5.5 (OTP release R11B)
this value does not include reductions performed in current
time slices of currently scheduled processes. If an
exact value is wanted, use
@@ -4975,53 +4717,65 @@ true</pre>
> <input>statistics(reductions).</input>
{2046,11}
</pre>
- </item>
- <tag><c>run_queue</c></tag>
- <item>
- <p>Returns the length of the run queue, that is, the number
- of processes that are ready to run.</p>
- </item>
- <tag><c>runtime</c></tag>
- <item>
- <p>Returns <c>{Total_Run_Time, Time_Since_Last_Call}</c>.
- Note that the run-time is the sum of the run-time for all
- threads in the Erlang run-time system and may therefore be greater
- than the wall-clock time.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="6"/>
+ <fsummary>Information about the run-queue</fsummary>
+ <desc>
+ <p>Returns the length of the run queue, that is, the number
+ of processes that are ready to run.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="7"/>
+ <fsummary>Information about run-time</fsummary>
+ <desc>
+ <p>Note that the run-time is the sum of the run-time for all
+ threads in the Erlang run-time system and may therefore be greater
+ than the wall-clock time.</p>
<pre>
> <input>statistics(runtime).</input>
{1690,1620}
</pre>
- </item>
- <tag><marker id="statistics_scheduler_wall_time"><c>scheduler_wall_time</c></marker></tag>
- <item>
- <p>Returns a list of tuples with
- <c>{SchedulerId, ActiveTime, TotalTime}</c>, where <c>SchedulerId</c> is an integer id of the scheduler, <c>ActiveTime</c> is
- the duration the scheduler has been busy, <c>TotalTime</c> is the total time duration since
- <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
- activation. The time unit is not defined and may be subject to change
- between releases, operating systems and system restarts.
- <c>scheduler_wall_time</c> should only be used to calculate relative
- values for scheduler-utilization. <c>ActiveTime</c> can never exceed <c>TotalTime</c>.
- </p>
+ </desc>
+ </func>
+ <func>
+ <name name="statistics" arity="1" clause_i="8"/>
+ <fsummary>Information about each schedulers work time</fsummary>
+ <desc>
+ <marker id="statistics_scheduler_wall_time"></marker>
+ <p>
+ Returns a list of tuples with <c>{<anno>SchedulerId</anno>,
+ <anno>ActiveTime</anno>, <anno>TotalTime</anno>}</c>, where
+ <c>SchedulerId</c> is an integer id of the scheduler, <c>ActiveTime</c> is
+ the duration the scheduler has been busy, <c>TotalTime</c> is the total time duration since
+ <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ activation. The time unit is not defined and may be subject to change
+ between releases, operating systems and system restarts.
+ <c>scheduler_wall_time</c> should only be used to calculate relative
+ values for scheduler-utilization. <c>ActiveTime</c> can never exceed <c>TotalTime</c>.
+ </p>
- <p>The definition of a busy scheduler is when it is not idle or not
- scheduling (selecting) a process or port, meaning; executing process
- code, executing linked-in-driver or NIF code, executing
- built-in-functions or any other runtime handling, garbage collecting
- or handling any other memory management. Note, a scheduler may also be
- busy even if the operating system has scheduled out the scheduler
- thread.
- </p>
+ <p>The definition of a busy scheduler is when it is not idle or not
+ scheduling (selecting) a process or port, meaning; executing process
+ code, executing linked-in-driver or NIF code, executing
+ built-in-functions or any other runtime handling, garbage collecting
+ or handling any other memory management. Note, a scheduler may also be
+ busy even if the operating system has scheduled out the scheduler
+ thread.
+ </p>
- <p>
- Returns <c>undefined</c> if the system flag <seealso marker="#system_flag_scheduler_wall_time">
- scheduler_wall_time</seealso> is turned off.
- </p>
+ <p>
+ Returns <c>undefined</c> if the system flag
+ <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ is turned off.
+ </p>
- <p>The list of scheduler information is unsorted and may appear in different order
- between calls.
- </p>
- <p>Using <c>scheduler_wall_time</c> to calculate scheduler utilization.</p>
+ <p>The list of scheduler information is unsorted and may appear in different order
+ between calls.
+ </p>
+ <p>Using <c>scheduler_wall_time</c> to calculate scheduler utilization.</p>
<pre>
> <input>erlang:system_flag(scheduler_wall_time, true).</input>
false
@@ -5049,34 +4803,26 @@ ok
{Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.</input>
0.9769136803764825
</pre>
-
<note>
<p><c>scheduler_wall_time</c> is by default disabled. Use <c>erlang:system_flag(scheduler_wall_time, true)</c> to enable it. </p>
</note>
- </item>
-
- <tag><c>wall_clock</c></tag>
- <item>
- <p>Returns
- <c>{Total_Wallclock_Time, Wallclock_Time_Since_Last_Call}</c>.
- <c>wall_clock</c> can be used in the same manner as
- <c>runtime</c>, except that real time is measured as
- opposed to runtime or CPU time.</p>
- </item>
- </taglist>
</desc>
</func>
<func>
- <name>erlang:suspend_process(Suspendee, OptList) -> boolean()</name>
+ <name name="statistics" arity="1" clause_i="9"/>
+ <fsummary>Information about wall-clock</fsummary>
+ <desc>
+ <p><c>wall_clock</c> can be used in the same manner as
+ <c>runtime</c>, except that real time is measured as
+ opposed to runtime or CPU time.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="suspend_process" arity="2"/>
<fsummary>Suspend a process</fsummary>
- <type>
- <v>Suspendee = pid()</v>
- <v>OptList = [Opt]</v>
- <v>Opt = atom()</v>
- </type>
<desc>
<p>Increases the suspend count on the process identified by
- <c>Suspendee</c> and puts it in the suspended state if it isn't
+ <c><anno>Suspendee</anno></c> and puts it in the suspended state if it isn't
already in the suspended state. A suspended process will not be
scheduled for execution until the process has been resumed.
</p>
@@ -5084,49 +4830,49 @@ ok
<p>A process can be suspended by multiple processes and can
be suspended multiple times by a single process. A suspended
process will not leave the suspended state until its suspend
- count reach zero. The suspend count of <c>Suspendee</c> is
- decreased when
- <seealso marker="#resume_process/1">erlang:resume_process(Suspendee)</seealso>
+ count reach zero. The suspend count of <c><anno>Suspendee</anno></c>
+ is decreased when
+ <seealso marker="#resume_process/1">erlang:resume_process(<anno>Suspendee</anno>)</seealso>
is called by the same process that called
- <c>erlang:suspend_process(Suspendee)</c>. All increased suspend
+ <c>erlang:suspend_process(<anno>Suspendee</anno>)</c>. All increased suspend
counts on other processes acquired by a process will automatically be
decreased when the process terminates.</p>
- <p>Currently the following options (<c>Opt</c>s) are available:</p>
+ <p>Currently the following options (<c><anno>Opt</anno></c>s) are available:</p>
<taglist>
<tag><c>asynchronous</c></tag>
<item>
A suspend request is sent to the process identified by
- <c>Suspendee</c>. <c>Suspendee</c> will eventually suspend
+ <c><anno>Suspendee</anno></c>. <c><anno>Suspendee</anno></c> will eventually suspend
unless it is resumed before it was able to suspend. The caller
of <c>erlang:suspend_process/2</c> will return immediately,
- regardless of whether the <c>Suspendee</c> has suspended yet
- or not. Note that the point in time when the <c>Suspendee</c>
+ regardless of whether the <c><anno>Suspendee</anno></c> has suspended yet
+ or not. Note that the point in time when the <c><anno>Suspendee</anno></c>
will actually suspend cannot be deduced from other events
in the system. The only guarantee given is that the
- <c>Suspendee</c> will <em>eventually</em> suspend (unless it
+ <c><anno>Suspendee</anno></c> will <em>eventually</em> suspend (unless it
is resumed). If the <c>asynchronous</c> option has <em>not</em>
been passed, the caller of <c>erlang:suspend_process/2</c> will
- be blocked until the <c>Suspendee</c> has actually suspended.
+ be blocked until the <c><anno>Suspendee</anno></c> has actually suspended.
</item>
<tag><c>unless_suspending</c></tag>
<item>
- The process identified by <c>Suspendee</c> will be suspended
+ The process identified by <c><anno>Suspendee</anno></c> will be suspended
unless the calling process already is suspending the
- <c>Suspendee</c>. If <c>unless_suspending</c> is combined
+ <c><anno>Suspendee</anno></c>. If <c>unless_suspending</c> is combined
with the <c>asynchronous</c> option, a suspend request will be
sent unless the calling process already is suspending the
- <c>Suspendee</c> or if a suspend request already has been sent
+ <c><anno>Suspendee</anno></c> or if a suspend request already has been sent
and is in transit. If the calling process already is suspending
- the <c>Suspendee</c>, or if combined with the <c>asynchronous</c>
+ the <c><anno>Suspendee</anno></c>, or if combined with the <c>asynchronous</c>
option and a send request already is in transit,
- <c>false</c> is returned and the suspend count on <c>Suspendee</c>
+ <c>false</c> is returned and the suspend count on <c><anno>Suspendee</anno></c>
will remain unchanged.
</item>
</taglist>
<p>If the suspend count on the process identified by
- <c>Suspendee</c> was increased, <c>true</c> is returned; otherwise,
+ <c><anno>Suspendee</anno></c> was increased, <c>true</c> is returned; otherwise,
<c>false</c> is returned.</p>
<warning>
@@ -5136,28 +4882,28 @@ ok
<taglist>
<tag><c>badarg</c></tag>
<item>
- If <c>Suspendee</c> isn't a process identifier.
+ If <c><anno>Suspendee</anno></c> isn't a process identifier.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> is same the process as
+ If the process identified by <c><anno>Suspendee</anno></c> is same the process as
the process calling <c>erlang:suspend_process/2</c>.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> is not alive.
+ If the process identified by <c><anno>Suspendee</anno></c> is not alive.
</item>
<tag><c>badarg</c></tag>
<item>
- If the process identified by <c>Suspendee</c> resides on another node.
+ If the process identified by <c><anno>Suspendee</anno></c> resides on another node.
</item>
<tag><c>badarg</c></tag>
<item>
- If <c>OptList</c> isn't a proper list of valid <c>Opt</c>s.
+ If <c><anno>OptList</anno></c> isn't a proper list of valid <c><anno>Opt</anno></c>s.
</item>
<tag><c>system_limit</c></tag>
<item>
- If the process identified by <c>Suspendee</c> has been suspended more
+ If the process identified by <c><anno>Suspendee</anno></c> has been suspended more
times by the calling process than can be represented by the
currently used internal data structures. The current system limit
is larger than 2 000 000 000 suspends, and it will never be less
@@ -5180,292 +4926,322 @@ ok
</desc>
</func>
<func>
- <name>erlang:system_flag(Flag, Value) -> OldValue</name>
- <fsummary>Set system flags</fsummary>
- <type>
- <v>Flag, Value, OldValue -- see below</v>
- </type>
+ <name name="system_flag" arity="2" clause_i="1"/>
+ <fsummary>Set system flag backtrace_depth</fsummary>
+ <desc>
+ <p>Sets the maximum depth of call stack back-traces in the
+ exit reason element of <c>'EXIT'</c> tuples.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="2"/>
+ <type name="cpu_topology"/>
+ <type name="level_entry"/>
+ <type name="level_tag"/>
+ <type name="sub_level"/>
+ <type name="info_list"/>
+ <fsummary>Set system flag cpu_topology</fsummary>
<desc>
<warning>
- <p>The
- <seealso marker="#system_flag_cpu_topology">cpu_topology</seealso>,
- and
- <seealso marker="#system_flag_scheduler_bind_type">scheduler_bind_type</seealso>
- <c>Flag</c>s are <em>deprecated</em> and have been scheduled for
- removal in erts-5.10/OTP-R16.</p>
+ <p><marker id="system_flag_cpu_topology"></marker>
+ This argument is <em>deprecated</em> and
+ scheduled for removal in erts-5.10/OTP-R16. Instead of using
+ this argument you are advised to use the <c>erl</c> command
+ line argument <seealso marker="erts:erl#+sct">+sct</seealso>.
+ When this argument has been removed a final CPU topology to use
+ will be determined at emulator boot time.</p>
</warning>
- <p>Sets various system properties of the Erlang node. Returns
- the old value of the flag.</p>
+ <p>Sets the user defined <c><anno>CpuTopology</anno></c>. The user defined
+ CPU topology will override any automatically detected
+ CPU topology. By passing <c>undefined</c> as <c><anno>CpuTopology</anno></c>
+ the system will revert back to the CPU topology automatically
+ detected. The returned value equals the value returned
+ from <c>erlang:system_info(cpu_topology)</c> before the
+ change was made.
+ </p>
+ <p>Returns the old value of the flag.</p>
+ <p>The CPU topology is used when binding schedulers to logical
+ processors. If schedulers are already bound when the CPU
+ topology is changed, the schedulers will be sent a request
+ to rebind according to the new CPU topology.
+ </p>
+ <p>The user defined CPU topology can also be set by passing
+ the <seealso marker="erts:erl#+sct">+sct</seealso> command
+ line argument to <c>erl</c>.
+ </p>
+ <p>For information on the <c><anno>CpuTopology</anno></c> type
+ and more, see the documentation of
+ <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>,
+ and the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso>
+ and <seealso marker="erts:erl#+sbt">+sbt</seealso>
+ command line flags.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="3"/>
+ <fsummary>Set system flag fullsweep_after</fsummary>
+ <desc>
+ <p><c><anno>Number</anno></c> is a non-negative integer which indicates
+ how many times generational garbage collections can be
+ done without forcing a fullsweep collection. The value
+ applies to new processes; processes already running are
+ not affected.</p>
+ <p>Returns the old value of the flag.</p>
+ <p>In low-memory systems (especially without virtual
+ memory), setting the value to 0 can help to conserve
+ memory.</p>
+ <p>An alternative way to set this value is through the
+ (operating system) environment variable
+ <c>ERL_FULLSWEEP_AFTER</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="4"/>
+ <fsummary>Set system flag min_heap_size</fsummary>
+ <desc>
+ <p>Sets the default minimum heap size for processes. The
+ size is given in words. The new <c>min_heap_size</c> only
+ effects processes spawned after the change of
+ <c>min_heap_size</c> has been made.
+ The <c>min_heap_size</c> can be set for individual
+ processes by use of
+ <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
+ <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="5"/>
+ <fsummary>Set system flag min_bin_vheap_size</fsummary>
+ <desc>
+ <p>Sets the default minimum binary virtual heap size for processes. The
+ size is given in words. The new <c>min_bin_vhheap_size</c> only
+ effects processes spawned after the change of
+ <c>min_bin_vhheap_size</c> has been made.
+ The <c>min_bin_vheap_size</c> can be set for individual
+ processes by use of
+ <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
+ <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="6"/>
+ <fsummary>Set system flag multi_scheduling</fsummary>
+ <desc>
+ <p><marker id="system_flag_multi_scheduling"></marker>
+ If multi-scheduling is enabled, more than one scheduler
+ thread is used by the emulator. Multi-scheduling can be
+ blocked. When multi-scheduling has been blocked, only
+ one scheduler thread will schedule Erlang processes.</p>
+ <p>If <c><anno>BlockState</anno> =:= block</c>, multi-scheduling will
+ be blocked. If <c><anno>BlockState</anno> =:= unblock</c> and no-one
+ else is blocking multi-scheduling and this process has
+ only blocked one time, multi-scheduling will be unblocked.
+ One process can block multi-scheduling multiple times.
+ If a process has blocked multiple times, it has to
+ unblock exactly as many times as it has blocked before it
+ has released its multi-scheduling block. If a process that
+ has blocked multi-scheduling exits, it will release its
+ blocking of multi-scheduling.</p>
+ <p>The return values are <c>disabled</c>, <c>blocked</c>,
+ or <c>enabled</c>. The returned value describes the
+ state just after the call to
+ <c>erlang:system_flag(multi_scheduling, <anno>BlockState</anno>)</c>
+ has been made. The return values are described in the
+ documentation of <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p>
+ <p><em>NOTE</em>: Blocking of multi-scheduling should normally
+ not be needed. If you feel that you need to
+ block multi-scheduling, think through the
+ problem at least a couple of times again.
+ Blocking multi-scheduling should only be used
+ as a last resort since it will most likely be
+ a <em>very inefficient</em> way to solve the
+ problem.</p>
+ <p>See also <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
+ <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, and
+ <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="7"/>
+ <type name="scheduler_bind_type"/>
+ <fsummary>Set system flag scheduler_bind_type</fsummary>
+ <desc>
+ <warning>
+ <p><marker id="system_flag_scheduler_bind_type"></marker>
+ This argument is <em>deprecated</em> and
+ scheduled for removal in erts-5.10/OTP-R16. Instead of using
+ this argument you are advised to use the <c>erl</c> command
+ line argument <seealso marker="erts:erl#+sbt">+sbt</seealso>.
+ When this argument has been removed a final scheduler bind type
+ to use will be determined at emulator boot time.</p>
+ </warning>
+ <p>Controls if and how schedulers are bound to logical
+ processors.</p>
+ <p>When <c>erlang:system_flag(scheduler_bind_type, <anno>How</anno>)</c> is
+ called, an asynchronous signal is sent to all schedulers
+ online which causes them to try to bind or unbind as requested.
+ <em>NOTE:</em> If a scheduler fails to bind, this
+ will often be silently ignored. This since it isn't always
+ possible to verify valid logical processor identifiers. If
+ an error is reported, it will be reported to the
+ <c>error_logger</c>. If you want to verify that the
+ schedulers actually have bound as requested, call
+ <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.
+ </p>
+ <p>Schedulers can currently only be bound on newer Linux,
+ Solaris, FreeBSD, and Windows systems, but more systems will be
+ supported in the future.
+ </p>
+ <p>In order for the runtime system to be able to bind schedulers,
+ the CPU topology needs to be known. If the runtime system fails
+ to automatically detect the CPU topology, it can be defined.
+ For more information on how to define the CPU topology, see
+ the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso> command
+ line flag.
+ </p>
+ <p>The runtime system will by default <em>not</em> bind schedulers
+ to logical processors.
+ </p>
+ <p><em>NOTE:</em> If the Erlang runtime system is the only
+ operating system process that binds threads to logical processors,
+ this improves the performance of the runtime system. However,
+ if other operating system processes (as for example another Erlang
+ runtime system) also bind threads to logical processors, there
+ might be a performance penalty instead. In some cases this
+ performance penalty might be severe. If this is the case, you
+ are advised to not bind the schedulers.</p>
+ <p>Schedulers can be bound in different ways. The <c><anno>How</anno></c>
+ argument determines how schedulers are bound. <c><anno>How</anno></c> can
+ currently be one of:</p>
<taglist>
- <tag><c>erlang:system_flag(backtrace_depth, Depth)</c></tag>
- <item>
- <p>Sets the maximum depth of call stack back-traces in the
- exit reason element of <c>'EXIT'</c> tuples.</p>
- </item>
- <tag><marker id="system_flag_cpu_topology"><c>erlang:system_flag(cpu_topology, CpuTopology)</c></marker></tag>
- <item>
- <p><em>NOTE:</em> This argument is <em>deprecated</em> and
- scheduled for removal in erts-5.10/OTP-R16. Instead of using
- this argument you are advised to use the <c>erl</c> command
- line argument <seealso marker="erts:erl#+sct">+sct</seealso>.
- When this argument has been removed a final CPU topology to use
- will be determined at emulator boot time.</p>
- <p>Sets the user defined <c>CpuTopology</c>. The user defined
- CPU topology will override any automatically detected
- CPU topology. By passing <c>undefined</c> as <c>CpuTopology</c>
- the system will revert back to the CPU topology automatically
- detected. The returned value equals the value returned
- from <c>erlang:system_info(cpu_topology)</c> before the
- change was made.
- </p>
- <p>The CPU topology is used when binding schedulers to logical
- processors. If schedulers are already bound when the CPU
- topology is changed, the schedulers will be sent a request
- to rebind according to the new CPU topology.
- </p>
- <p>The user defined CPU topology can also be set by passing
- the <seealso marker="erts:erl#+sct">+sct</seealso> command
- line argument to <c>erl</c>.
- </p>
- <p>For information on the <c>CpuTopology</c> type
- and more, see the documentation of
- <seealso marker="#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>,
- and the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso>
- and <seealso marker="erts:erl#+sbt">+sbt</seealso>
- command line flags.
- </p>
- </item>
- <tag><c>erlang:system_flag(fullsweep_after, Number)</c></tag>
- <item>
- <p><c>Number</c> is a non-negative integer which indicates
- how many times generational garbage collections can be
- done without forcing a fullsweep collection. The value
- applies to new processes; processes already running are
- not affected.</p>
- <p>In low-memory systems (especially without virtual
- memory), setting the value to 0 can help to conserve
- memory.</p>
- <p>An alternative way to set this value is through the
- (operating system) environment variable
- <c>ERL_FULLSWEEP_AFTER</c>.</p>
- </item>
- <tag><c>erlang:system_flag(min_heap_size, MinHeapSize)</c></tag>
- <item>
- <p>Sets the default minimum heap size for processes. The
- size is given in words. The new <c>min_heap_size</c> only
- effects processes spawned after the change of
- <c>min_heap_size</c> has been made.
- The <c>min_heap_size</c> can be set for individual
- processes by use of
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
- </item>
- <tag><c>erlang:system_flag(min_bin_vheap_size, MinBinVHeapSize)</c></tag>
- <item>
- <p>Sets the default minimum binary virtual heap size for processes. The
- size is given in words. The new <c>min_bin_vhheap_size</c> only
- effects processes spawned after the change of
- <c>min_bin_vhheap_size</c> has been made.
- The <c>min_bin_vheap_size</c> can be set for individual
- processes by use of
- <seealso marker="#spawn_opt/4">spawn_opt/N</seealso> or
- <seealso marker="#process_flag/2">process_flag/2</seealso>. </p>
- </item>
- <tag><marker id="system_flag_multi_scheduling"><c>erlang:system_flag(multi_scheduling, BlockState)</c></marker></tag>
- <item>
- <p><c>BlockState = block | unblock</c></p>
- <p>If multi-scheduling is enabled, more than one scheduler
- thread is used by the emulator. Multi-scheduling can be
- blocked. When multi-scheduling has been blocked, only
- one scheduler thread will schedule Erlang processes.</p>
- <p>If <c>BlockState =:= block</c>, multi-scheduling will
- be blocked. If <c>BlockState =:= unblock</c> and no-one
- else is blocking multi-scheduling and this process has
- only blocked one time, multi-scheduling will be unblocked.
- One process can block multi-scheduling multiple times.
- If a process has blocked multiple times, it has to
- unblock exactly as many times as it has blocked before it
- has released its multi-scheduling block. If a process that
- has blocked multi-scheduling exits, it will release its
- blocking of multi-scheduling.</p>
- <p>The return values are <c>disabled</c>, <c>blocked</c>,
- or <c>enabled</c>. The returned value describes the
- state just after the call to
- <c>erlang:system_flag(multi_scheduling, BlockState)</c>
- has been made. The return values are described in the
- documentation of <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>.</p>
- <p><em>NOTE</em>: Blocking of multi-scheduling should normally
- not be needed. If you feel that you need to
- block multi-scheduling, think through the
- problem at least a couple of times again.
- Blocking multi-scheduling should only be used
- as a last resort since it will most likely be
- a <em>very inefficient</em> way to solve the
- problem.</p>
- <p>See also <seealso marker="#system_info_multi_scheduling">erlang:system_info(multi_scheduling)</seealso>,
- <seealso marker="#system_info_multi_scheduling_blockers">erlang:system_info(multi_scheduling_blockers)</seealso>, and
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
- </item>
- <tag><marker id="system_flag_scheduler_bind_type"><c>erlang:system_flag(scheduler_bind_type, How)</c></marker></tag>
- <item>
- <p><em>NOTE:</em> This argument is <em>deprecated</em> and
- scheduled for removal in erts-5.10/OTP-R16. Instead of using
- this argument you are advised to use the <c>erl</c> command
- line argument <seealso marker="erts:erl#+sbt">+sbt</seealso>.
- When this argument has been removed a final scheduler bind type
- to use will be determined at emulator boot time.</p>
- <p>Controls if and how schedulers are bound to logical
- processors.</p>
- <p>When <c>erlang:system_flag(scheduler_bind_type, How)</c> is
- called, an asynchronous signal is sent to all schedulers
- online which causes them to try to bind or unbind as requested.
- <em>NOTE:</em> If a scheduler fails to bind, this
- will often be silently ignored. This since it isn't always
- possible to verify valid logical processor identifiers. If
- an error is reported, it will be reported to the
- <c>error_logger</c>. If you want to verify that the
- schedulers actually have bound as requested, call
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>.
- </p>
- <p>Schedulers can currently only be bound on newer Linux,
- Solaris, FreeBSD, and Windows systems, but more systems will be
- supported in the future.
- </p>
- <p>In order for the runtime system to be able to bind schedulers,
- the CPU topology needs to be known. If the runtime system fails
- to automatically detect the CPU topology, it can be defined.
- For more information on how to define the CPU topology, see
- the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso> command
- line flag.
- </p>
- <p>The runtime system will by default <em>not</em> bind schedulers
- to logical processors.
- </p>
- <p><em>NOTE:</em> If the Erlang runtime system is the only
- operating system process that binds threads to logical processors,
- this improves the performance of the runtime system. However,
- if other operating system processes (as for example another Erlang
- runtime system) also bind threads to logical processors, there
- might be a performance penalty instead. In some cases this
- performance penalty might be severe. If this is the case, you
- are advised to not bind the schedulers.</p>
- <p>Schedulers can be bound in different ways. The <c>How</c>
- argument determines how schedulers are bound. <c>How</c> can
- currently be one of:</p>
- <taglist>
- <tag><c>unbound</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt u</seealso>.
- </p></item>
- <tag><c>no_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt ns</seealso>.
- </p></item>
- <tag><c>thread_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt ts</seealso>.
- </p></item>
- <tag><c>processor_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt ps</seealso>.
- </p></item>
- <tag><c>spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt s</seealso>.
- </p></item>
- <tag><c>no_node_thread_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt nnts</seealso>.
- </p></item>
- <tag><c>no_node_processor_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt nnps</seealso>.
- </p></item>
- <tag><c>thread_no_node_processor_spread</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso>.
- </p></item>
- <tag><c>default_bind</c></tag>
- <item><p>Same as the <c>erl</c> command line argument
- <seealso marker="erts:erl#+sbt">+sbt db</seealso>.
- </p></item>
- </taglist>
- <p>The value returned equals <c>How</c> before the
- <c>scheduler_bind_type</c> flag was changed.</p>
- <p>Failure:</p>
- <taglist>
- <tag><c>notsup</c></tag>
- <item>
- <p>If binding of schedulers is not supported.</p>
- </item>
- <tag><c>badarg</c></tag>
- <item>
- <p>If <c>How</c> isn't one of the documented alternatives.</p>
- </item>
- <tag><c>badarg</c></tag>
- <item>
- <p>If no CPU topology information is available.</p>
- </item>
- </taglist>
- <p>The scheduler bind type can also be set by passing
- the <seealso marker="erts:erl#+sbt">+sbt</seealso> command
- line argument to <c>erl</c>.
- </p>
- <p>For more information, see
- <seealso marker="#system_info_scheduler_bind_type">erlang:system_info(scheduler_bind_type)</seealso>,
- <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>,
- the <c>erl</c> <seealso marker="erts:erl#+sbt">+sbt</seealso>
- and <seealso marker="erts:erl#+sct">+sct</seealso> command line
- flags.
- </p>
- </item>
- <tag><marker id="system_flag_scheduler_wall_time"><c>erlang:system_flag(scheduler_wall_time, Boolean)</c></marker></tag>
+ <tag><c>unbound</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt u</seealso>.
+ </p></item>
+ <tag><c>no_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt ns</seealso>.
+ </p></item>
+ <tag><c>thread_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt ts</seealso>.
+ </p></item>
+ <tag><c>processor_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt ps</seealso>.
+ </p></item>
+ <tag><c>spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt s</seealso>.
+ </p></item>
+ <tag><c>no_node_thread_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt nnts</seealso>.
+ </p></item>
+ <tag><c>no_node_processor_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt nnps</seealso>.
+ </p></item>
+ <tag><c>thread_no_node_processor_spread</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt tnnps</seealso>.
+ </p></item>
+ <tag><c>default_bind</c></tag>
+ <item><p>Same as the <c>erl</c> command line argument
+ <seealso marker="erts:erl#+sbt">+sbt db</seealso>.
+ </p></item>
+ </taglist>
+ <p>The value returned equals <c><anno>How</anno></c> before the
+ <c>scheduler_bind_type</c> flag was changed.</p>
+ <p>Failure:</p>
+ <taglist>
+ <tag><c>notsup</c></tag>
<item>
- <p>Turns on/off scheduler wall time measurements. </p>
- <p>For more information see,
- <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.
- </p>
+ <p>If binding of schedulers is not supported.</p>
</item>
-
- <tag><marker id="system_flag_schedulers_online"><c>erlang:system_flag(schedulers_online, SchedulersOnline)</c></marker></tag>
+ <tag><c>badarg</c></tag>
<item>
- <p>Sets the amount of schedulers online. Valid range is
- <![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers)]]>.
- </p>
- <p>For more information see,
- <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>,
- and
- <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.
- </p>
+ <p>If <c>How</c> isn't one of the documented alternatives.</p>
</item>
-
- <tag><c>erlang:system_flag(trace_control_word, TCW)</c></tag>
+ <tag><c>badarg</c></tag>
<item>
- <p>Sets the value of the node's trace control word to
- <c>TCW</c>. <c>TCW</c> should be an unsigned integer. For
- more information see documentation of the
- <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso>
- function in the match specification documentation in the
- ERTS User's Guide.</p>
+ <p>If no CPU topology information is available.</p>
</item>
</taglist>
- <note>
- <p>The <c>schedulers</c> option has been removed as
- of erts version 5.5.3. The number of scheduler
- threads is determined at emulator boot time, and
- cannot be changed after that.</p>
- </note>
+ <p>The scheduler bind type can also be set by passing
+ the <seealso marker="erts:erl#+sbt">+sbt</seealso> command
+ line argument to <c>erl</c>.
+ </p>
+ <p>For more information, see
+ <seealso marker="#system_info_scheduler_bind_type">erlang:system_info(scheduler_bind_type)</seealso>,
+ <seealso marker="#system_info_scheduler_bindings">erlang:system_info(scheduler_bindings)</seealso>,
+ the <c>erl</c> <seealso marker="erts:erl#+sbt">+sbt</seealso>
+ and <seealso marker="erts:erl#+sct">+sct</seealso> command line
+ flags.
+ </p>
</desc>
</func>
<func>
- <name>erlang:system_info(Type) -> Res</name>
- <fsummary>Information about the system</fsummary>
- <type>
- <v>Type, Res -- see below</v>
- </type>
+ <name name="system_flag" arity="2" clause_i="8"/>
+ <fsummary>Set system flag scheduler_wall_time</fsummary>
+ <desc><p><marker id="system_flag_scheduler_wall_time"></marker>
+ Turns on/off scheduler wall time measurements. </p>
+ <p>For more information see,
+ <seealso marker="#statistics_scheduler_wall_time">erlang:statistics(scheduler_wall_time)</seealso>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="9"/>
+ <fsummary>Set system flag schedulers_online</fsummary>
<desc>
- <p>Returns various information about the current system
- (emulator) as specified by <c>Type</c>:</p>
+ <p><marker id="system_flag_schedulers_online"></marker>
+ Sets the amount of schedulers online. Valid range is
+ <![CDATA[1 <= SchedulersOnline <= erlang:system_info(schedulers)]]>.
+ </p>
+ <p>Returns the old value of the flag.</p>
+ <p>For more information see,
+ <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>,
+ and
+ <seealso marker="#system_info_schedulers_online">erlang:system_info(schedulers_online)</seealso>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_flag" arity="2" clause_i="10"/>
+ <fsummary>Set system flag trace_control_word</fsummary>
+ <desc>
+ <p>Sets the value of the node's trace control word to
+ <c><anno>TCW</anno></c>. <c><anno>TCW</anno></c> should be an unsigned integer. For
+ more information see documentation of the
+ <seealso marker="erts:match_spec#set_tcw">set_tcw</seealso>
+ function in the match specification documentation in the
+ ERTS User's Guide.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="system_info" arity="1" clause_i="1"/>
+ <name name="system_info" arity="1" clause_i="2"/>
+ <name name="system_info" arity="1" clause_i="3"/>
+ <name name="system_info" arity="1" clause_i="4"/>
+ <name name="system_info" arity="1" clause_i="5"/>
+ <type variable="Allocator" name_i="2"/>
+ <type variable="Version" name_i="2"/>
+ <type variable="Features" name_i="2"/>
+ <type variable="Settings" name_i="2"/>
+ <type variable="Alloc" name_i="3"/>
+ <fsummary>Information about the allocators of the system</fsummary>
+ <desc>
+ <p>
+ Returns various information about the
+ <marker id="system_info_allocator_tags">allocators</marker> of the
+ current system (emulator) as specified by
+ <c><anno>Item</anno></c>:</p>
<taglist>
<tag><marker id="system_info_allocated_areas"><c>allocated_areas</c></marker></tag>
<item>
@@ -5490,37 +5266,27 @@ ok
</item>
<tag><marker id="system_info_allocator"><c>allocator</c></marker></tag>
<item>
- <p>Returns <c>{Allocator, Version, Features, Settings}.</c></p>
- <p>Types:</p>
- <list type="bulleted">
- <item><c>Allocator = undefined | glibc</c></item>
- <item><c>Version = [integer()]</c></item>
- <item><c>Features = [atom()]</c></item>
- <item><c>Settings = [{Subsystem, [{Parameter, Value}]}]</c></item>
- <item><c>Subsystem = atom()</c></item>
- <item><c>Parameter = atom()</c></item>
- <item><c>Value = term()</c></item>
- </list>
+ <p>Returns <c>{<anno>Allocator</anno>, <anno>Version</anno>, <anno>Features</anno>, <anno>Settings</anno>}.</c></p>
<p>Explanation:</p>
<list type="bulleted">
<item>
- <p><c>Allocator</c> corresponds to the <c>malloc()</c>
- implementation used. If <c>Allocator</c> equals
+ <p><c><anno>Allocator</anno></c> corresponds to the <c>malloc()</c>
+ implementation used. If <c><anno>Allocator</anno></c> equals
<c>undefined</c>, the <c>malloc()</c> implementation
used could not be identified. Currently
<c>glibc</c> can be identified.</p>
</item>
<item>
- <p><c>Version</c> is a list of integers (but not a
+ <p><c><anno>Version</anno></c> is a list of integers (but not a
string) representing the version of
the <c>malloc()</c> implementation used.</p>
</item>
<item>
- <p><c>Features</c> is a list of atoms representing
+ <p><c><anno>Features</anno></c> is a list of atoms representing
allocation features used.</p>
</item>
<item>
- <p><c>Settings</c> is a list of subsystems, their
+ <p><c><anno>Settings</anno></c> is a list of subsystems, their
configurable parameters, and used values. Settings
may differ between different combinations of
platforms, allocators, and allocation features.
@@ -5540,15 +5306,15 @@ ok
erts_alloc(3)</seealso> documentation.
</p>
</item>
- <tag><marker id="system_info_allocator_tuple"><c>{allocator, Alloc}</c></marker></tag>
+ <tag><marker id="system_info_allocator_tuple"><c>{allocator, <anno>Alloc</anno>}</c></marker></tag>
<item>
<p>Returns information about the specified allocator.
As of erts version 5.6.1 the return value is a list
of <c>{instance, InstanceNo, InstanceInfo}</c> tuples
where <c>InstanceInfo</c> contains information about
a specific instance of the allocator.
- If <c>Alloc</c> is not a recognized allocator,
- <c>undefined</c> is returned. If <c>Alloc</c> is disabled,
+ If <c><anno>Alloc</anno></c> is not a recognized allocator,
+ <c>undefined</c> is returned. If <c><anno>Alloc</anno></c> is disabled,
<c>false</c> is returned.</p>
<p><em>Note:</em> The information returned is highly
implementation dependent and may be changed, or removed
@@ -5577,54 +5343,51 @@ ok
values. The first value is memory pool size and
the second value used memory size.</p>
</item>
- <tag><marker id="system_info_allocator_sizes"><c>{allocator_sizes, Alloc}</c></marker></tag>
+ <tag><marker id="system_info_allocator_sizes"><c>{allocator_sizes, <anno>Alloc</anno>}</c></marker></tag>
<item>
<p>Returns various size information for the specified
allocator. The information returned is a subset of the
information returned by
- <seealso marker="#system_info_allocator_tuple">erlang:system_info({allocator, Alloc})</seealso>.
+ <seealso marker="#system_info_allocator_tuple">erlang:system_info({allocator, <anno>Alloc</anno>})</seealso>.
</p>
</item>
- <tag><c>build_type</c></tag>
- <item>
- <p>Returns an atom describing the build type of the runtime
- system. This is normally the atom <c>opt</c> for optimized.
- Other possible return values are <c>debug</c>, <c>purify</c>,
- <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
- <c>gprof</c>, and <c>lcnt</c>. Possible return values
- may be added and/or removed at any time without prior notice.
- </p>
- </item>
- <tag><c>c_compiler_used</c></tag>
- <item>
- <p>Returns a two-tuple describing the C compiler used when
- compiling the runtime system. The first element is an
- atom describing the name of the compiler, or <c>undefined</c>
- if unknown. The second element is a term describing the
- version of the compiler, or <c>undefined</c> if unknown.
- </p>
- </item>
- <tag><c>check_io</c></tag>
- <item>
- <p>Returns a list containing miscellaneous information
- regarding the emulators internal I/O checking. Note,
- the content of the returned list may vary between
- platforms and over time. The only thing guaranteed is
- that a list is returned.</p>
- </item>
- <tag><c>compat_rel</c></tag>
- <item>
- <p>Returns the compatibility mode of the local node as
- an integer. The integer returned represents the
- Erlang/OTP release which the current emulator has been
- set to be backward compatible with. The compatibility
- mode can be configured at startup by using the command
- line flag <c>+R</c>, see
- <seealso marker="erts:erl#compat_rel">erl(1)</seealso>.</p>
- </item>
- <tag><marker id="system_info_cpu_topology"><c>cpu_topology</c></marker></tag>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name name="system_info" arity="1" clause_i="10"/>
+ <name name="system_info" arity="1" clause_i="11"/>
+ <type name="cpu_topology"/>
+ <type name="level_entry"/>
+ <type_desc name="cpu_topology">
+ <marker id="system_info_cpu_topology"></marker>
+ All <c><anno>LevelEntry</anno></c>s of a list
+ must contain the same <c><anno>LevelTag</anno></c>, except
+ on the top level where both <c>node</c> and
+ <c>processor</c> <c><anno>LevelTag</anno></c>s may co-exist.
+ </type_desc>
+ <type_desc name="level_entry">
+ <c>{<anno>LevelTag</anno>, <anno>SubLevel</anno>} == {<anno>LevelTag</anno>, [], <anno>SubLevel</anno>}</c>
+ </type_desc>
+ <type name="level_tag"/>
+ <type_desc name="level_tag">
+ More <c><anno>LevelTag</anno></c>s may be introduced in the future.
+ </type_desc>
+ <type name="sub_level"/>
+ <type name="info_list"/>
+ <type_desc name="info_list">
+ The <c>info_list()</c> may be extended in the future.
+ </type_desc>
+ <fsummary>Information about the CPU topology of the system</fsummary>
+ <desc>
+ <p>Returns various information about the
+ <marker id="system_info_cpu_topology_tags">CPU topology</marker>
+ of the current system
+ (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><c>cpu_topology</c></tag>
<item>
- <p>Returns the <c>CpuTopology</c> which currently is used by the
+ <p>Returns the <c><anno>CpuTopology</anno></c> which currently is used by the
emulator. The CPU topology is used when binding schedulers
to logical processors. The CPU topology used is the
<seealso marker="erlang#system_info_cpu_topology_defined">user
@@ -5632,31 +5395,11 @@ ok
<seealso marker="erlang#system_info_cpu_topology_detected">automatically
detected CPU topology</seealso> if such exists. If no CPU topology
exists, <c>undefined</c> is returned.</p>
- <p>Types:</p>
- <list type="bulleted">
- <item><c>CpuTopology = LevelEntryList | undefined</c></item>
- <item><c>LevelEntryList = [LevelEntry]</c> (all
- <c>LevelEntry</c>s of a <c>LevelEntryList</c>
- must contain the same <c>LevelTag</c>, except
- on the top level where both <c>node</c> and
- <c>processor</c> <c>LevelTag</c>s may co-exist)</item>
- <item><c>LevelEntry = {LevelTag, SubLevel}
- | {LevelTag, InfoList, SubLevel}</c>
- (<c>{LevelTag, SubLevel}
- == {LevelTag, [], SubLevel}</c>)</item>
- <item><c>LevelTag = node|processor|core|thread</c>
- (more <c>LevelTag</c>s may be introduced in
- the future)</item>
- <item><c>SubLevel = [LevelEntry] | LogicalCpuId</c></item>
- <item><c>LogicalCpuId = {logical, integer()}</c></item>
- <item><c>InfoList = []</c> (the <c>InfoList</c>
- may be extended in the future)</item>
- </list>
<p><c>node</c> refers to NUMA (non-uniform memory access)
nodes, and <c>thread</c> refers to hardware threads
(e.g. Intels hyper-threads).</p>
- <p>A level in the <c>CpuTopology</c> term can be omitted if
- only one entry exists and the <c>InfoList</c> is empty.
+ <p>A level in the <c><anno>CpuTopology</anno></c> term can be omitted if
+ only one entry exists and the <c><anno>InfoList</anno></c> is empty.
</p>
<p><c>thread</c> can only be a sub level to <c>core</c>.
<c>core</c> can be a sub level to either <c>processor</c>
@@ -5668,15 +5411,15 @@ ok
consist of a mix of processor internal and external
NUMA nodes, as long as each logical CPU belongs to one
and only one NUMA node. Cache hierarchy is not part of
- the <c>CpuTopology</c> type yet, but will be in the
+ the <c><anno>CpuTopology</anno></c> type yet, but will be in the
future. Other things may also make it into the CPU
topology in the future. In other words, expect the
- <c>CpuTopology</c> type to change.
+ <c><anno>CpuTopology</anno></c> type to change.
</p>
</item>
<tag><marker id="system_info_cpu_topology_defined"><c>{cpu_topology, defined}</c></marker></tag>
<item>
- <p>Returns the user defined <c>CpuTopology</c>. For more
+ <p>Returns the user defined <c><anno>CpuTopology</anno></c>. For more
information see the documentation of
the <c>erl</c> <seealso marker="erts:erl#+sct">+sct</seealso> command
line flag, and the documentation of the
@@ -5686,7 +5429,7 @@ ok
</item>
<tag><marker id="system_info_cpu_topology_detected"><c>{cpu_topology, detected}</c></marker></tag>
<item>
- <p>Returns the automatically detected <c>CpuTopology</c>. The
+ <p>Returns the automatically detected <c><anno>CpuTopology</anno></c>. The
emulator currently only detects the CPU topology on some newer
Linux, Solaris, FreeBSD, and Windows systems. On Windows system with
more than 32 logical processors the CPU topology is not detected.
@@ -5698,13 +5441,112 @@ ok
</item>
<tag><c>{cpu_topology, used}</c></tag>
<item>
- <p>Returns the <c>CpuTopology</c> which is used by the
+ <p>Returns the <c><anno>CpuTopology</anno></c> which is used by the
emulator. For more information see the
documentation of the
<seealso marker="#system_info_cpu_topology">cpu_topology</seealso>
argument.
</p>
</item>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name name="system_info" arity="1" clause_i="6"/>
+ <name name="system_info" arity="1" clause_i="7"/>
+ <name name="system_info" arity="1" clause_i="8"/>
+ <name name="system_info" arity="1" clause_i="9"/>
+ <name name="system_info" arity="1" clause_i="12"/>
+ <name name="system_info" arity="1" clause_i="13"/>
+ <name name="system_info" arity="1" clause_i="14"/>
+ <name name="system_info" arity="1" clause_i="15"/>
+ <name name="system_info" arity="1" clause_i="16"/>
+ <name name="system_info" arity="1" clause_i="17"/>
+ <name name="system_info" arity="1" clause_i="18"/>
+ <name name="system_info" arity="1" clause_i="19"/>
+ <name name="system_info" arity="1" clause_i="20"/>
+ <name name="system_info" arity="1" clause_i="21"/>
+ <name name="system_info" arity="1" clause_i="22"/>
+ <name name="system_info" arity="1" clause_i="23"/>
+ <name name="system_info" arity="1" clause_i="24"/>
+ <name name="system_info" arity="1" clause_i="25"/>
+ <name name="system_info" arity="1" clause_i="26"/>
+ <name name="system_info" arity="1" clause_i="27"/>
+ <name name="system_info" arity="1" clause_i="28"/>
+ <name name="system_info" arity="1" clause_i="29"/>
+ <name name="system_info" arity="1" clause_i="30"/>
+ <name name="system_info" arity="1" clause_i="31"/>
+ <name name="system_info" arity="1" clause_i="32"/>
+ <name name="system_info" arity="1" clause_i="33"/>
+ <name name="system_info" arity="1" clause_i="34"/>
+ <name name="system_info" arity="1" clause_i="35"/>
+ <name name="system_info" arity="1" clause_i="36"/>
+ <name name="system_info" arity="1" clause_i="37"/>
+ <name name="system_info" arity="1" clause_i="38"/>
+ <name name="system_info" arity="1" clause_i="39"/>
+ <name name="system_info" arity="1" clause_i="40"/>
+ <name name="system_info" arity="1" clause_i="41"/>
+ <name name="system_info" arity="1" clause_i="42"/>
+ <name name="system_info" arity="1" clause_i="43"/>
+ <name name="system_info" arity="1" clause_i="44"/>
+ <name name="system_info" arity="1" clause_i="45"/>
+ <name name="system_info" arity="1" clause_i="46"/>
+ <name name="system_info" arity="1" clause_i="47"/>
+ <name name="system_info" arity="1" clause_i="48"/>
+ <name name="system_info" arity="1" clause_i="49"/>
+ <name name="system_info" arity="1" clause_i="50"/>
+ <name name="system_info" arity="1" clause_i="51"/>
+ <fsummary>Information about the system</fsummary>
+ <desc>
+ <p>Returns various information about the current system
+ (emulator) as specified by <c><anno>Item</anno></c>:</p>
+ <taglist>
+ <tag><c>allocated_areas</c>, <c>allocator</c>,
+ <c>alloc_util_allocators</c>, <c>allocator_sizes</c></tag>
+ <item>
+ <p>See <seealso marker="#system_info_allocator_tags">above</seealso>.</p>
+ </item>
+ <tag><c>build_type</c></tag>
+ <item>
+ <p>Returns an atom describing the build type of the runtime
+ system. This is normally the atom <c>opt</c> for optimized.
+ Other possible return values are <c>debug</c>, <c>purify</c>,
+ <c>quantify</c>, <c>purecov</c>, <c>gcov</c>, <c>valgrind</c>,
+ <c>gprof</c>, and <c>lcnt</c>. Possible return values
+ may be added and/or removed at any time without prior notice.
+ </p>
+ </item>
+ <tag><c>c_compiler_used</c></tag>
+ <item>
+ <p>Returns a two-tuple describing the C compiler used when
+ compiling the runtime system. The first element is an
+ atom describing the name of the compiler, or <c>undefined</c>
+ if unknown. The second element is a term describing the
+ version of the compiler, or <c>undefined</c> if unknown.
+ </p>
+ </item>
+ <tag><c>check_io</c></tag>
+ <item>
+ <p>Returns a list containing miscellaneous information
+ regarding the emulators internal I/O checking. Note,
+ the content of the returned list may vary between
+ platforms and over time. The only thing guaranteed is
+ that a list is returned.</p>
+ </item>
+ <tag><c>compat_rel</c></tag>
+ <item>
+ <p>Returns the compatibility mode of the local node as
+ an integer. The integer returned represents the
+ Erlang/OTP release which the current emulator has been
+ set to be backward compatible with. The compatibility
+ mode can be configured at startup by using the command
+ line flag <c>+R</c>, see
+ <seealso marker="erts:erl#compat_rel">erl(1)</seealso>.</p>
+ </item>
+ <tag><c>cpu_topology</c></tag>
+ <item>
+ <p>See <seealso marker="#system_info_cpu_topology_tags">above</seealso>.</p>
+ </item>
<tag><c>creation</c></tag>
<item>
<p>Returns the creation of the local node as an integer.
@@ -5734,10 +5576,10 @@ ok
<item>
<p>Returns a list of tuples
<c>{Node, ControllingEntity}</c>, one entry for each
- connected remote node. The <c>Node</c> is the name of the
- node and the <c>ControllingEntity</c> is the port or pid
+ connected remote node. The <c><anno>Node</anno></c> is the name of the
+ node and the <c><anno>ControllingEntity</anno></c> is the port or pid
responsible for the communication to that node. More
- specifically, the <c>ControllingEntity</c> for nodes
+ specifically, the <c><anno>ControllingEntity</anno></c> for nodes
connected via TCP/IP (the normal case) is the socket
actually used in communication with the specific node.</p>
</item>
@@ -5785,7 +5627,7 @@ ok
</item>
<tag><c>fullsweep_after</c></tag>
<item>
- <p>Returns <c>{fullsweep_after, integer()}</c> which is the
+ <p>Returns <c>{fullsweep_after, integer() >= 0}</c> which is the
<c>fullsweep_after</c> garbage collection setting used
by default. For more information see
<c>garbage_collection</c> described below.</p>
@@ -5878,12 +5720,12 @@ ok
</item>
<tag><c>min_heap_size</c></tag>
<item>
- <p>Returns <c>{min_heap_size, MinHeapSize}</c> where <c>MinHeapSize</c> is the current system wide
+ <p>Returns <c>{min_heap_size, <anno>MinHeapSize</anno>}</c> where <c><anno>MinHeapSize</anno></c> is the current system wide
minimum heap size for spawned processes.</p>
</item>
<tag><c>min_bin_vheap_size</c></tag>
<item>
- <p>Returns <c>{min_bin_vheap_size, MinBinVHeapSize}</c> where <c>MinBinVHeapSize</c> is the current system wide
+ <p>Returns <c>{min_bin_vheap_size, <anno>MinBinVHeapSize</anno>}</c> where <c><anno>MinBinVHeapSize</anno></c> is the current system wide
minimum binary virtual heap size for spawned processes.</p>
</item>
<tag><c>modified_timing_level</c></tag>
@@ -5927,10 +5769,10 @@ ok
</item>
<tag><marker id="system_info_multi_scheduling_blockers"><c>multi_scheduling_blockers</c></marker></tag>
<item>
- <p>Returns a list of <c>PID</c>s when multi-scheduling
- is blocked; otherwise, the empty list. The <c>PID</c>s
- in the list is <c>PID</c>s of the processes currently
- blocking multi-scheduling. A <c>PID</c> will only be
+ <p>Returns a list of <c><anno>PID</anno></c>s when multi-scheduling
+ is blocked; otherwise, the empty list. The <c><anno>PID</anno></c>s
+ in the list is <c><anno>PID</anno></c>s of the processes currently
+ blocking multi-scheduling. A <c><anno>PID</anno></c> will only be
present once in the list, even if the corresponding
process has blocked multiple times.</p>
<p>See also <seealso marker="#system_flag_multi_scheduling">erlang:system_flag(multi_scheduling, BlockState)</seealso>,
@@ -5941,19 +5783,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>
@@ -6005,7 +5868,7 @@ ok
<item>
<p>Returns the scheduler id (<c>SchedulerId</c>) of the
scheduler thread that the calling process is executing
- on. <c>SchedulerId</c> is a positive integer; where
+ on. <c><anno>SchedulerId</anno></c> is a positive integer; where
<c><![CDATA[1 <= SchedulerId <= erlang:system_info(schedulers)]]></c>. See also
<seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p>
</item>
@@ -6037,7 +5900,8 @@ ok
<seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>,
and
<seealso marker="#system_flag_schedulers_online">erlang:system_flag(schedulers_online, SchedulersOnline)</seealso>.
- </p>
+ </p> <name name="system_info" arity="1" clause_i="49"/>
+
</item>
<tag><c>smp_support</c></tag>
<item>
@@ -6131,53 +5995,39 @@ ok
</func>
<func>
- <name>erlang:system_monitor() -> MonSettings</name>
+ <name name="system_monitor" arity="0"/>
+ <type name="system_monitor_option"/>
<fsummary>Current system performance monitoring settings</fsummary>
- <type>
- <v>MonSettings -> {MonitorPid, Options} | undefined</v>
- <v>&nbsp;MonitorPid = pid()</v>
- <v>&nbsp;Options = [Option]</v>
- <v>&nbsp;&nbsp;Option = {long_gc, Time} | {large_heap, Size} | busy_port | busy_dist_port</v>
- <v>&nbsp;&nbsp;&nbsp;Time = Size = integer()</v>
- </type>
<desc>
<p>Returns the current system monitoring settings set by
<seealso marker="#system_monitor/2">erlang:system_monitor/2</seealso>
- as <c>{MonitorPid, Options}</c>, or <c>undefined</c> if there
+ as <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c>, or <c>undefined</c> if there
are no settings. The order of the options may be different
from the one that was set.</p>
</desc>
</func>
<func>
- <name>erlang:system_monitor(undefined | {MonitorPid, Options}) -> MonSettings</name>
+ <name name="system_monitor" arity="1"/>
+ <type name="system_monitor_option"/>
<fsummary>Set or clear system performance monitoring options</fsummary>
- <type>
- <v>MonitorPid, Options, MonSettings -- see below</v>
- </type>
<desc>
<p>When called with the argument <c>undefined</c>, all
system performance monitoring settings are cleared.</p>
- <p>Calling the function with <c>{MonitorPid, Options}</c> as
+ <p>Calling the function with <c>{<anno>MonitorPid</anno>, <anno>Options</anno>}</c> as
argument, is the same as calling
- <seealso marker="#system_monitor/2">erlang:system_monitor(MonitorPid, Options)</seealso>.</p>
+ <seealso marker="#system_monitor/2">erlang:system_monitor(<anno>MonitorPid</anno>, <anno>Options</anno>)</seealso>.</p>
<p>Returns the previous system monitor settings just like
<seealso marker="#system_monitor/0">erlang:system_monitor/0</seealso>.</p>
</desc>
</func>
<func>
- <name>erlang:system_monitor(MonitorPid, [Option]) -> MonSettings</name>
+ <name name="system_monitor" arity="2"/>
+ <type name="system_monitor_option"/>
<fsummary>Set system performance monitoring options</fsummary>
- <type>
- <v>MonitorPid = pid()</v>
- <v>Option = {long_gc, Time} | {large_heap, Size} | busy_port | busy_dist_port</v>
- <v>&nbsp;Time = Size = integer()</v>
- <v>MonSettings = {OldMonitorPid, [Option]}</v>
- <v>&nbsp;OldMonitorPid = pid()</v>
- </type>
- <desc>
- <p>Sets system performance monitoring options. <c>MonitorPid</c>
+ <desc>
+ <p>Sets system performance monitoring options. <c><anno>MonitorPid</anno></c>
is a local pid that will receive system monitor messages, and
the second argument is a list of monitoring options:</p>
<taglist>
@@ -6186,7 +6036,7 @@ ok
<p>If a garbage collection in the system takes at least
<c>Time</c> wallclock milliseconds, a message
<c>{monitor, GcPid, long_gc, Info}</c> is sent to
- <c>MonitorPid</c>. <c>GcPid</c> is the pid that was
+ <c><anno>MonitorPid</anno></c>. <c>GcPid</c> is the pid that was
garbage collected and <c>Info</c> is a list of two-element
tuples describing the result of the garbage collection.
One of the tuples is <c>{timeout, GcTime}</c> where
@@ -6209,7 +6059,7 @@ ok
<p>If a garbage collection in the system results in
the allocated size of a heap being at least <c>Size</c>
words, a message <c>{monitor, GcPid, large_heap, Info}</c>
- is sent to <c>MonitorPid</c>. <c>GcPid</c> and <c>Info</c>
+ is sent to <c><anno>MonitorPid</anno></c>. <c>GcPid</c> and <c>Info</c>
are the same as for <c>long_gc</c> above, except that
the tuple tagged with <c>timeout</c> is not present.
<em>Note</em>: As of erts version 5.6 the monitor message
@@ -6225,7 +6075,7 @@ ok
<p>If a process in the system gets suspended because it
sends to a busy port, a message
<c>{monitor, SusPid, busy_port, Port}</c> is sent to
- <c>MonitorPid</c>. <c>SusPid</c> is the pid that got
+ <c><anno>MonitorPid</anno></c>. <c>SusPid</c> is the pid that got
suspended when sending to <c>Port</c>.</p>
</item>
<tag><c>busy_dist_port</c></tag>
@@ -6234,7 +6084,7 @@ ok
sends to a process on a remote node whose inter-node
communication was handled by a busy port, a message
<c>{monitor, SusPid, busy_dist_port, Port}</c> is sent to
- <c>MonitorPid</c>. <c>SusPid</c> is the pid that got
+ <c><anno>MonitorPid</anno></c>. <c>SusPid</c> is the pid that got
suspended when sending through the inter-node
communication port <c>Port</c>.</p>
</item>
@@ -6249,48 +6099,48 @@ ok
<p>Keep the monitoring process neat and do not set the system
monitor limits too tight.</p>
</note>
- <p>Failure: <c>badarg</c> if <c>MonitorPid</c> does not exist.</p>
+ <p>Failure: <c>badarg</c> if <c><anno>MonitorPid</anno></c> does not exist or is not a local process.</p>
</desc>
</func>
<func>
- <name>erlang:system_profile() -> ProfilerSettings</name>
+ <name name="system_profile" arity="0"/>
+ <type name="system_profile_option"/>
<fsummary>Current system profiling settings</fsummary>
- <type>
- <v>ProfilerSettings -> {ProfilerPid, Options} | undefined</v>
- <v>&nbsp;ProfilerPid = pid() | port()</v>
- <v>&nbsp;Options = [Option]</v>
- <v>&nbsp;&nbsp;Option = runnable_procs | runnable_ports | scheduler | exclusive</v>
- </type>
<desc>
<p>Returns the current system profiling settings set by
<seealso marker="#system_profile/2">erlang:system_profile/2</seealso>
- as <c>{ProfilerPid, Options}</c>, or <c>undefined</c> if there
+ as <c>{<anno>ProfilerPid</anno>, <anno>Options</anno>}</c>, or <c>undefined</c> if there
are no settings. The order of the options may be different
from the one that was set.</p>
</desc>
</func>
<func>
- <name>erlang:system_profile(ProfilerPid, Options) -> ProfilerSettings</name>
+ <name name="system_profile" arity="2"/>
+ <type name="system_profile_option"/>
<fsummary>Current system profiling settings</fsummary>
- <type>
- <v>ProfilerSettings -> {ProfilerPid, Options} | undefined</v>
- <v>&nbsp;ProfilerPid = pid() | port()</v>
- <v>&nbsp;Options = [Option]</v>
- <v>&nbsp;&nbsp;Option = runnable_procs | runnable_ports | scheduler | exclusive</v>
- </type>
- <desc>
- <p>Sets system profiler options. <c>ProfilerPid</c>
+ <desc>
+ <p>Sets system profiler options. <c><anno>ProfilerPid</anno></c>
is a local pid or port that will receive profiling messages. The
receiver is excluded from all profiling.
The second argument is a list of profiling options:</p>
<taglist>
+ <tag><c>exclusive</c></tag>
+ <item>
+ <p>
+ If a synchronous call to a port from a process is done, the
+ calling process is considered not runnable during the call
+ runtime to the port. The calling process is notified as
+ <c>inactive</c> and subsequently <c>active</c> when the port
+ callback returns.
+ </p>
+ </item>
<tag><c>runnable_procs</c></tag>
<item>
<p>If a process is put into or removed from the run queue a message,
<c>{profile, Pid, State, Mfa, Ts}</c>, is sent to
- <c>ProfilerPid</c>. Running processes that is reinserted into the
+ <c><anno>ProfilerPid</anno></c>. Running processes that is reinserted into the
run queue after having been preemptively scheduled out will not trigger this
message.
</p>
@@ -6299,24 +6149,14 @@ ok
<item>
<p>If a port is put into or removed from the run queue a message,
<c>{profile, Port, State, 0, Ts}</c>, is sent to
- <c>ProfilerPid</c>.
+ <c><anno>ProfilerPid</anno></c>.
</p>
</item>
<tag><c>scheduler</c></tag>
<item>
<p>If a scheduler is put to sleep or awoken a message,
<c>{profile, scheduler, Id, State, NoScheds, Ts}</c>, is sent
- to <c>ProfilerPid</c>.
- </p>
- </item>
- <tag><c>exclusive</c></tag>
- <item>
- <p>
- If a synchronous call to a port from a process is done, the
- calling process is considered not runnable during the call
- runtime to the port. The calling process is notified as
- <c>inactive</c> and subsequently <c>active</c> when the port
- callback returns.
+ to <c><anno>ProfilerPid</anno></c>.
</p>
</item>
</taglist>
@@ -6327,14 +6167,11 @@ ok
</func>
<func>
- <name>term_to_binary(Term) -> ext_binary()</name>
+ <name name="term_to_binary" arity="1"/>
<fsummary>Encode a term to an Erlang external term format binary</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
<p>Returns a binary data object which is the result of encoding
- <c>Term</c> according to the Erlang external term format.</p>
+ <c><anno>Term</anno></c> according to the Erlang external term format.</p>
<p>This can be used for a variety of purposes, for example
writing a term to a file in an efficient way, or sending an
Erlang term to some type of communications channel not
@@ -6344,20 +6181,16 @@ ok
</desc>
</func>
<func>
- <name>term_to_binary(Term, [Option]) -> ext_binary()</name>
+ <name name="term_to_binary" arity="2"/>
<fsummary>Encode a term to en Erlang external term format binary</fsummary>
- <type>
- <v>Term = term()</v>
- <v>Option = compressed | {compressed,Level} | {minor_version,Version}</v>
- </type>
<desc>
<p>Returns a binary data object which is the result of encoding
- <c>Term</c> according to the Erlang external term format.</p>
+ <c><anno>Term</anno></c> according to the Erlang external term format.</p>
<p>If the option <c>compressed</c> is provided, the external
term format will be compressed. The compressed format is
automatically recognized by <c>binary_to_term/1</c> in R7B and later.</p>
<p>It is also possible to specify a compression level by giving
- the option <c>{compressed,Level}</c>, where <c>Level</c> is an
+ the option <c>{compressed, <anno>Level</anno>}</c>, where <c><anno>Level</anno></c> is an
integer from 0 through 9. <c>0</c> means that no compression
will be done (it is the same as not giving any <c>compressed</c> option);
<c>1</c> will take the least time but may not compress as well as
@@ -6366,16 +6199,16 @@ ok
on the input term, level 9 compression may or may not produce a smaller
result than level 1 compression.</p>
<p>Currently, <c>compressed</c> gives the same result as
- <c>{compressed,6}</c>.</p>
- <p>The option <c>{minor_version,Version}</c> can be use to control
+ <c>{compressed, 6}</c>.</p>
+ <p>The option <c>{minor_version, <anno>Version</anno>}</c> can be use to control
some details of the encoding. This option was
- introduced in R11B-4. Currently, the allowed values for <c>Version</c>
+ introduced in R11B-4. Currently, the allowed values for <c><anno>Version</anno></c>
are <c>0</c> and <c>1</c>.</p>
- <p><c>{minor_version,1}</c> forces any floats in the term to be encoded
+ <p><c>{minor_version, 1}</c> forces any floats in the term to be encoded
in a more space-efficient and exact way (namely in the 64-bit IEEE format,
rather than converted to a textual representation). <c>binary_to_term/1</c>
in R11B-4 and later is able decode the new representation.</p>
- <p><c>{minor_version,0}</c> is currently the default, meaning that floats
+ <p><c>{minor_version, 0}</c> is currently the default, meaning that floats
will be encoded using a textual representation; this option is useful if
you want to ensure that releases prior to R11B-4 can decode resulting
binary.</p>
@@ -6384,14 +6217,11 @@ ok
</desc>
</func>
<func>
- <name>throw(Any)</name>
+ <name name="throw" arity="1"/>
<fsummary>Throw an exception</fsummary>
- <type>
- <v>Any = term()</v>
- </type>
<desc>
<p>A non-local return from a function. If evaluated within a
- <c>catch</c>, <c>catch</c> will return the value <c>Any</c>.</p>
+ <c>catch</c>, <c>catch</c> will return the value <c><anno>Any</anno></c>.</p>
<pre>
> <input>catch throw({hello, there}).</input>
{hello,there}</pre>
@@ -6399,11 +6229,8 @@ ok
</desc>
</func>
<func>
- <name>time() -> {Hour, Minute, Second}</name>
+ <name name="time" arity="0"/>
<fsummary>Current time</fsummary>
- <type>
- <v>Hour = Minute = Second = integer() >= 0</v>
- </type>
<desc>
<p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p>
<p>The time zone and daylight saving time correction depend on
@@ -6414,35 +6241,27 @@ ok
</desc>
</func>
<func>
- <name>tl(List1) -> List2</name>
+ <name name="tl" arity="1"/>
<fsummary>Tail of a list</fsummary>
- <type>
- <v>List1 = List2 = [term()]</v>
- </type>
<desc>
- <p>Returns the tail of <c>List1</c>, that is, the list minus
+ <p>Returns the tail of <c><anno>List</anno></c>, that is, the list minus
the first element.</p>
<pre>
> <input>tl([geesties, guilies, beasties]).</input>
[guilies, beasties]</pre>
<p>Allowed in guard tests.</p>
- <p>Failure: <c>badarg</c> if <c>List</c> is the empty list [].</p>
+ <p>Failure: <c>badarg</c> if <c><anno>List</anno></c> is the empty list [].</p>
</desc>
</func>
<func>
- <name>erlang:trace(PidSpec, How, FlagList) -> integer() >= 0</name>
+ <name name="trace" arity="3"/>
+ <type name="trace_flag"/>
<fsummary>Set trace flags for a process or processes</fsummary>
- <type>
- <v>PidSpec = pid() | existing | new | all</v>
- <v>How = boolean()</v>
- <v>FlagList = [Flag]</v>
- <v>&nbsp;Flag -- see below</v>
- </type>
- <desc>
- <p>Turns on (if <c>How == true</c>) or off (if
- <c>How == false</c>) the trace flags in <c>FlagList</c> for
- the process or processes represented by <c>PidSpec</c>.</p>
- <p><c>PidSpec</c> is either a pid for a local process, or one of
+ <desc>
+ <p>Turns on (if <c><anno>How</anno> == true</c>) or off (if
+ <c><anno>How</anno> == false</c>) the trace flags in <c><anno>FlagList</anno></c> for
+ the process or processes represented by <c><anno>PidSpec</anno></c>.</p>
+ <p><c><anno>PidSpec</anno></c> is either a pid for a local process, or one of
the following atoms:</p>
<taglist>
<tag><c>existing</c></tag>
@@ -6459,7 +6278,7 @@ ok
will be created in the future.</p>
</item>
</taglist>
- <p><c>FlagList</c> can contain any number of the following
+ <p><c><anno>FlagList</anno></c> can contain any number of the following
flags (the "message tags" refers to the list of messages
following below):</p>
<taglist>
@@ -6774,11 +6593,11 @@ ok
<p>Only one process can trace a particular process. For this
reason, attempts to trace an already traced process will fail.</p>
<p>Returns: A number indicating the number of processes that
- matched <c>PidSpec</c>. If <c>PidSpec</c> is a pid,
- the return value will be <c>1</c>. If <c>PidSpec</c> is
+ matched <c><anno>PidSpec</anno></c>. If <c><anno>PidSpec</anno></c> is a pid,
+ the return value will be <c>1</c>. If <c><anno>PidSpec</anno></c> is
<c>all</c> or <c>existing</c> the return value will be
the number of processes running, excluding tracer processes.
- If <c>PidSpec</c> is <c>new</c>, the return value will be
+ If <c><anno>PidSpec</anno></c> is <c>new</c>, the return value will be
<c>0</c>.</p>
<p>Failure: If specified arguments are not supported. For
example <c>cpu_timestamp</c> is not supported on all
@@ -6786,64 +6605,58 @@ ok
</desc>
</func>
<func>
- <name>erlang:trace_delivered(Tracee) -> Ref</name>
+ <name name="trace_delivered" arity="1"/>
<fsummary>Notification when trace has been delivered</fsummary>
- <type>
- <v>Tracee = pid() | all</v>
- <v>Ref = reference()</v>
- </type>
<desc>
<p>The delivery of trace messages is dislocated on the time-line
compared to other events in the system. If you know that the
- <c>Tracee</c> has passed some specific point in its execution,
+ <c><anno>Tracee</anno></c> has passed some specific point in its execution,
and you want to know when at least all trace messages
corresponding to events up to this point have reached the tracer
- you can use <c>erlang:trace_delivered(Tracee)</c>. A
- <c>{trace_delivered, Tracee, Ref}</c> message is sent to
- the caller of <c>erlang:trace_delivered(Tracee)</c> when it
+ you can use <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>. A
+ <c>{trace_delivered, <anno>Tracee</anno>, <anno>Ref</anno>}</c> message is sent to
+ the caller of <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> when it
is guaranteed that all trace messages have been delivered to
- the tracer up to the point that the <c>Tracee</c> had reached
+ the tracer up to the point that the <c><anno>Tracee</anno></c> had reached
at the time of the call to
- <c>erlang:trace_delivered(Tracee)</c>.</p>
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c>.</p>
<p>Note that the <c>trace_delivered</c> message does <em>not</em>
imply that trace messages have been delivered; instead, it implies
that all trace messages that <em>should</em> be delivered have
- been delivered. It is not an error if <c>Tracee</c> isn't, and
+ been delivered. It is not an error if <c><anno>Tracee</anno></c> isn't, and
hasn't been traced by someone, but if this is the case,
<em>no</em> trace messages will have been delivered when the
<c>trace_delivered</c> message arrives.</p>
- <p>Note that <c>Tracee</c> has to refer to a process currently,
+ <p>Note that <c><anno>Tracee</anno></c> has to refer to a process currently,
or previously existing on the same node as the caller of
- <c>erlang:trace_delivered(Tracee)</c> resides on.
- The special <c>Tracee</c> atom <c>all</c> denotes all processes
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> resides on.
+ The special <c><anno>Tracee</anno></c> atom <c>all</c> denotes all processes
that currently are traced in the node.</p>
- <p>An example: Process <c>A</c> is tracee, port <c>B</c> is
+ <p>An example: Process <c>A</c> is <c><anno>Tracee</anno></c>, port <c>B</c> is
tracer, and process <c>C</c> is the port owner of <c>B</c>.
<c>C</c> wants to close <c>B</c> when <c>A</c> exits. <c>C</c>
can ensure that the trace isn't truncated by calling
<c>erlang:trace_delivered(A)</c> when <c>A</c> exits and wait
- for the <c>{trace_delivered, A, Ref}</c> message before closing
+ for the <c>{trace_delivered, A, <anno>Ref</anno>}</c> message before closing
<c>B</c>.</p>
- <p>Failure: <c>badarg</c> if <c>Tracee</c> does not refer to a
+ <p>Failure: <c>badarg</c> if <c><anno>Tracee</anno></c> does not refer to a
process (dead or alive) on the same node as the caller of
- <c>erlang:trace_delivered(Tracee)</c> resides on.</p>
+ <c>erlang:trace_delivered(<anno>Tracee</anno>)</c> resides on.</p>
</desc>
</func>
<func>
- <name>erlang:trace_info(PidOrFunc, Item) -> Res</name>
+ <name name="trace_info" arity="2"/>
+ <type name="trace_info_return"/>
+ <type name="trace_info_item_result"/>
+ <type name="trace_info_flag"/>
+ <type name="trace_match_spec"/>
<fsummary>Trace information about a process or function</fsummary>
- <type>
- <v>PidOrFunc = pid() | new | {Module, Function, Arity} | on_load</v>
- <v>&nbsp;Module = Function = atom()</v>
- <v>&nbsp;Arity = arity()</v>
- <v>Item, Res -- see below</v>
- </type>
<desc>
<p>Returns trace information about a process or function.</p>
- <p>To get information about a process, <c>PidOrFunc</c> should
+ <p>To get information about a process, <c><anno>PidOrFunc</anno></c> should
be a pid or the atom <c>new</c>. The atom <c>new</c> means
that the default trace state for processes to be created will
- be returned. <c>Item</c> must have one of the following
+ be returned. <c><anno>Item</anno></c> must have one of the following
values:</p>
<taglist>
<tag><c>flags</c></tag>
@@ -6923,22 +6736,24 @@ ok
<tag><c>all</c></tag>
<item>
- <p>Return a list containing the <c>{Item, Value}</c> tuples
+ <p>Return a list containing the <c>{<anno>Item</anno>, Value}</c> tuples
for all other items, or return <c>false</c> if no tracing
is active for this function.</p>
</item>
</taglist>
- <p>The actual return value will be <c>{Item, Value}</c>, where
+ <p>The actual return value will be <c>{<anno>Item</anno>, Value}</c>, where
<c>Value</c> is the requested information as described above.
If a pid for a dead process was given, or the name of a
non-existing function, <c>Value</c> will be <c>undefined</c>.</p>
- <p>If <c>PidOrFunc</c> is the <c>on_load</c>, the information
+ <p>If <c><anno>PidOrFunc</anno></c> is the <c>on_load</c>, the information
returned refers to the default value for code that will be
loaded.</p>
</desc>
</func>
<func>
- <name>erlang:trace_pattern(MFA, MatchSpec) -> integer() >= 0</name>
+ <name name="trace_pattern" arity="2" clause_i="1"/>
+ <type name="trace_pattern_mfa"/>
+ <type name="trace_match_spec"/>
<fsummary>Set trace patterns for global call tracing</fsummary>
<desc>
<p>The same as
@@ -6947,11 +6762,11 @@ ok
</desc>
</func>
<func>
- <name>erlang:trace_pattern(MFA, MatchSpec, FlagList) -> integer() >= 0</name>
+ <name name="trace_pattern" arity="3"/>
+ <type name="trace_pattern_mfa"/>
+ <type name="trace_match_spec"/>
+ <type name="trace_pattern_flag"/>
<fsummary>Set trace patterns for tracing of function calls</fsummary>
- <type>
- <v>MFA, MatchSpec, FlagList -- see below</v>
- </type>
<desc>
<p>This BIF is used to enable or disable call tracing for
exported functions. It must be combined with
@@ -6976,7 +6791,7 @@ ok
and an action to be performed. The default action is to send a
trace message. If the pattern does not match or the guard
fails, the action will not be executed.</p>
- <p>The <c>MFA</c> argument should be a tuple like
+ <p>The <c><anno>MFA</anno></c> argument should be a tuple like
<c>{Module, Function, Arity}</c> or the atom <c>on_load</c>
(described below). It can be the module, function, and arity
for an exported function (or a BIF in any module).
@@ -6999,11 +6814,11 @@ ok
</taglist>
<p>Other combinations, such as <c>{Module,'_',Arity}</c>, are
not allowed. Local functions will match wildcards only if
- the <c>local</c> option is in the <c>FlagList</c>.</p>
- <p>If the <c>MFA</c> argument is the atom <c>on_load</c>,
+ the <c>local</c> option is in the <c><anno>FlagList</anno></c>.</p>
+ <p>If the <c><anno>MFA</anno></c> argument is the atom <c>on_load</c>,
the match specification and flag list will be used on all
modules that are newly loaded.</p>
- <p>The <c>MatchSpec</c> argument can take any of the following
+ <p>The <c><anno>MatchSpec</anno></c> argument can take any of the following
forms:</p>
<taglist>
<tag><c>false</c></tag>
@@ -7015,7 +6830,7 @@ ok
<item>
<p>Enable tracing for the matching function(s).</p>
</item>
- <tag><c>MatchSpecList</c></tag>
+ <tag><c><anno>MatchSpecList</anno></c></tag>
<item>
<p>A list of match specifications. An empty list is
equivalent to <c>true</c>. See the ERTS User's Guide
@@ -7023,18 +6838,18 @@ ok
</item>
<tag><c>restart</c></tag>
<item>
- <p>For the <c>FlagList</c> option <c>call_count</c> and <c>call_time</c>:
+ <p>For the <c><anno>FlagList</anno></c> option <c>call_count</c> and <c>call_time</c>:
restart the existing counters. The behaviour is undefined
- for other <c>FlagList</c> options.</p>
+ for other <c><anno>FlagList</anno></c> options.</p>
</item>
<tag><c>pause</c></tag>
<item>
- <p>For the <c>FlagList</c> option <c>call_count</c> and <c>call_time</c>: pause
+ <p>For the <c><anno>FlagList</anno></c> option <c>call_count</c> and <c>call_time</c>: pause
the existing counters. The behaviour is undefined for
other <c>FlagList</c> options.</p>
</item>
</taglist>
- <p>The <c>FlagList</c> parameter is a list of options.
+ <p>The <c><anno>FlagList</anno></c> parameter is a list of options.
The following options are allowed:</p>
<taglist>
<tag><c>global</c></tag>
@@ -7053,13 +6868,13 @@ ok
the process, a <c>return_to</c> message will also be sent
when this function returns to its caller.</p>
</item>
- <tag><c>meta | {meta, Pid}</c></tag>
+ <tag><c>meta | {meta, <anno>Pid</anno>}</c></tag>
<item>
<p>Turn on or off meta tracing for all types of function
calls. Trace messages will be sent to the tracer process
- or port <c>Pid</c> whenever any of the specified
+ or port <c><anno>Pid</anno></c> whenever any of the specified
functions are called, regardless of how they are called.
- If no <c>Pid</c> is specified, <c>self()</c> is used as a
+ If no <c><anno>Pid</anno></c> is specified, <c>self()</c> is used as a
default tracer process.</p>
<p>Meta tracing traces all processes and does not care
about the process trace flags set by <c>trace/3</c>,
@@ -7071,32 +6886,32 @@ ok
</item>
<tag><c>call_count</c></tag>
<item>
- <p>Starts (<c>MatchSpec == true</c>) or stops
- (<c>MatchSpec == false</c>) call count tracing for all
+ <p>Starts (<c><anno>MatchSpec</anno> == true</c>) or stops
+ (<c><anno>MatchSpec</anno> == false</c>) call count tracing for all
types of function calls. For every function a counter is
incremented when the function is called, in any process.
No process trace flags need to be activated.</p>
<p>If call count tracing is started while already running,
the count is restarted from zero. Running counters can be
- paused with <c>MatchSpec == pause</c>. Paused and running
+ paused with <c><anno>MatchSpec</anno> == pause</c>. Paused and running
counters can be restarted from zero with
- <c>MatchSpec == restart</c>.</p>
+ <c><anno>MatchSpec</anno> == restart</c>.</p>
<p>The counter value can be read with
<seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
</item>
<tag><c>call_time</c></tag>
<item>
- <p>Starts (<c>MatchSpec == true</c>) or stops
- (<c>MatchSpec == false</c>) call time tracing for all
+ <p>Starts (<c><anno>MatchSpec</anno> == true</c>) or stops
+ (<c><anno>MatchSpec</anno> == false</c>) call time tracing for all
types of function calls. For every function a counter is
incremented when the function is called. Time spent in the function
is accumulated in two other counters, seconds and micro-seconds.
The counters are stored for each call traced process.</p>
<p>If call time tracing is started while already running,
the count and time is restarted from zero. Running counters can be
- paused with <c>MatchSpec == pause</c>. Paused and running
+ paused with <c><anno>MatchSpec</anno> == pause</c>. Paused and running
counters can be restarted from zero with
- <c>MatchSpec == restart</c>.</p>
+ <c><anno>MatchSpec</anno> == restart</c>.</p>
<p>The counter value can be read with
<seealso marker="#trace_info/2">erlang:trace_info/2</seealso>.</p>
</item>
@@ -7122,18 +6937,15 @@ ok
<seealso marker="#trace_info/2">erlang:trace_info/2</seealso>
BIF to retrieve the existing match specification.</p>
<p>Returns the number of exported functions that matched
- the <c>MFA</c> argument. This will be zero if none matched at
+ the <c><anno>MFA</anno></c> argument. This will be zero if none matched at
all.</p>
</desc>
</func>
<func>
- <name>trunc(Number) -> integer()</name>
+ <name name="trunc" arity="1"/>
<fsummary>Return an integer by the truncating a number</fsummary>
- <type>
- <v>Number = number()</v>
- </type>
<desc>
- <p>Returns an integer by the truncating <c>Number</c>.</p>
+ <p>Returns an integer by the truncating <c><anno>Number</anno></c>.</p>
<pre>
> <input>trunc(5.5).</input>
5</pre>
@@ -7141,13 +6953,10 @@ ok
</desc>
</func>
<func>
- <name>tuple_size(Tuple) -> integer() >= 0</name>
+ <name name="tuple_size" arity="1"/>
<fsummary>Return the size of a tuple</fsummary>
- <type>
- <v>Tuple = tuple()</v>
- </type>
<desc>
- <p>Returns an integer which is the number of elements in <c>Tuple</c>.</p>
+ <p>Returns an integer which is the number of elements in <c><anno>Tuple</anno></c>.</p>
<pre>
> <input>tuple_size({morni, mulle, bwange}).</input>
3</pre>
@@ -7155,25 +6964,19 @@ ok
</desc>
</func>
<func>
- <name>tuple_to_list(Tuple) -> [term()]</name>
+ <name name="tuple_to_list" arity="1"/>
<fsummary>Convert a tuple to a list</fsummary>
- <type>
- <v>Tuple = tuple()</v>
- </type>
<desc>
- <p>Returns a list which corresponds to <c>Tuple</c>.
- <c>Tuple</c> may contain any Erlang terms.</p>
+ <p>Returns a list which corresponds to <c><anno>Tuple</anno></c>.
+ <c><anno>Tuple</anno></c> may contain any Erlang terms.</p>
<pre>
> <input>tuple_to_list({share, {'Ericsson_B', 163}}).</input>
[share,{'Ericsson_B',163}]</pre>
</desc>
</func>
<func>
- <name>erlang:universaltime() -> DateTime</name>
+ <name name="universaltime" arity="0"/>
<fsummary>Current date and time according to Universal Time Coordinated (UTC)</fsummary>
- <type>
- <v>DateTime = <seealso marker="calendar#type-datetime">calendar:datetime()</seealso></v>
- </type>
<desc>
<p>Returns the current date and time according to Universal
Time Coordinated (UTC), also called GMT, in the form
@@ -7187,46 +6990,39 @@ ok
</desc>
</func>
<func>
- <name>erlang:universaltime_to_localtime({Date1, Time1}) -> {Date2, Time2}</name>
+ <name name="universaltime_to_localtime" arity="1"/>
<fsummary>Convert from Universal Time Coordinated (UTC) to local date and time</fsummary>
- <type>
- <v>Date1 = Date2 = <seealso marker="calendar#type-date">calendar:date()</seealso></v>
- <v>Time1 = Time2 = <seealso marker="calendar#type-time">calendar:time()</seealso></v>
- </type>
<desc>
<p>Converts Universal Time Coordinated (UTC) date and time to
local date and time, if this is supported by the underlying
OS. Otherwise, no conversion is done, and
- <c>{Date1, Time1}</c> is returned.</p>
+ <c><anno>Universaltime</anno></c> is returned.</p>
<pre>
> <input>erlang:universaltime_to_localtime({{1996,11,6},{14,18,43}}).</input>
{{1996,11,7},{15,18,43}}</pre>
- <p>Failure: <c>badarg</c> if <c>Date1</c> or <c>Time1</c> do
- not denote a valid date or time.</p>
+ <p>Failure: <c>badarg</c> if <c>Universaltime</c> does not denote
+ a valid date and time.</p>
</desc>
</func>
<func>
- <name>unlink(Id) -> true</name>
+ <name name="unlink" arity="1"/>
<fsummary>Remove a link, if there is one, to another process or port</fsummary>
- <type>
- <v>Id = pid() | port()</v>
- </type>
<desc>
<p>Removes the link, if there is one, between the calling
- process and the process or port referred to by <c>Id</c>.</p>
+ process and the process or port referred to by <c><anno>Id</anno></c>.</p>
<p>Returns <c>true</c> and does not fail, even if there is no
- link to <c>Id</c>, or if <c>Id</c> does not exist.</p>
- <p>Once <c>unlink(Id)</c> has returned it is guaranteed that
+ link to <c><anno>Id</anno></c>, or if <c><anno>Id</anno></c> does not exist.</p>
+ <p>Once <c>unlink(<anno>Id</anno>)</c> has returned it is guaranteed that
the link between the caller and the entity referred to by
- <c>Id</c> has no effect on the caller in the future (unless
+ <c><anno>Id</anno></c> has no effect on the caller in the future (unless
the link is setup again). If caller is trapping exits, an
- <c>{'EXIT', Id, _}</c> message due to the link might have
+ <c>{'EXIT', <anno>Id</anno>, _}</c> message due to the link might have
been placed in the caller's message queue prior to the call,
- though. Note, the <c>{'EXIT', Id, _}</c> message can be the
- result of the link, but can also be the result of <c>Id</c>
+ though. Note, the <c>{'EXIT', <anno>Id</anno>, _}</c> message can be the
+ result of the link, but can also be the result of <c><anno>Id</anno></c>
calling <c>exit/2</c>. Therefore, it <em>may</em> be
appropriate to cleanup the message queue when trapping exits
- after the call to <c>unlink(Id)</c>, as follow:</p>
+ after the call to <c>unlink(<anno>Id</anno>)</c>, as follow:</p>
<code type="none">
unlink(Id),
@@ -7249,13 +7045,10 @@ ok
</desc>
</func>
<func>
- <name>unregister(RegName) -> true</name>
+ <name name="unregister" arity="1"/>
<fsummary>Remove the registered name for a process (or port)</fsummary>
- <type>
- <v>RegName = atom()</v>
- </type>
<desc>
- <p>Removes the registered name <c>RegName</c>, associated with a
+ <p>Removes the registered name <c><anno>RegName</anno></c>, associated with a
pid or a port identifier.</p>
<pre>
> <input>unregister(db).</input>
@@ -7266,7 +7059,7 @@ true</pre>
</desc>
</func>
<func>
- <name>whereis(RegName) -> pid() | port() | undefined</name>
+ <name name="whereis" arity="1"/>
<fsummary>Get the pid (or port) with a given registered name</fsummary>
<desc>
<p>Returns the pid or port identifier with the registered name
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index ec5e7d9b74..c73cdfd290 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2002</year><year>2011</year>
+ <year>2002</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -276,7 +276,7 @@
<item>
Max cached segments. The maximum number of memory segments
stored in the memory segment cache. Valid range is
- 0-30. Default value is 5.</item>
+ 0-30. Default value is 10.</item>
</taglist>
<p>The following flags are available for configuration of
<c>sys_alloc</c>:</p>
@@ -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 e996d3e8e3..7fc61517a8 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,854 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 5.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Threads created internally in the runtime system by
+ vanilla, fd, and spawn drivers on Windows systems could
+ make thread unsafe calls to <c>driver_select()</c>.</p>
+ <p>
+ Own Id: OTP-10802</p>
+ </item>
+ <item>
+ <p>
+ Threads created internally in the runtime system by the
+ vanilla, fd, and spawn drivers on Windows systems could
+ make unsafe memory accesses to driver data after port had
+ terminated.</p>
+ <p>
+ Own Id: OTP-10803</p>
+ </item>
+ <item>
+ <p>
+ The runtime system could crash when flushing data to
+ standard out or standard error on Windows.</p>
+ <p>
+ Own Id: OTP-10807</p>
+ </item>
+ <item>
+ <p>Bugs due to the port optimizations introduced in
+ erts-5.10/OTP-R16A have been fixed:</p> <list>
+ <item>Memory leak when terminating ports</item>
+ <item>Memory leak when reaching the system limit of
+ maximum amount of concurrently existing ports</item>
+ <item>Crashs due to missing, or late test of bad port
+ handle</item> <item>The newly introduced driver API
+ function <c>erl_drv_busy_msgq_limits()</c> could not be
+ used by dynamically linked in drivers on Windows</item>
+ </list>
+ <p>
+ Own Id: OTP-10809 Aux Id: OTP-10336 </p>
+ </item>
+ <item>
+ <p>
+ Fix <c>{packet,httph}</c> header capitalization for
+ unrecognized header fields longer than 20 charachters
+ such as <c>Sec-Websocket-Version</c>. The limit is simply
+ raised from 20 to 50 characters with the hope that valid
+ headers longer than 50 are not used.</p>
+ <p>
+ Own Id: OTP-10824</p>
+ </item>
+ <item>
+ <p>
+ Fix rounding issues in float_to_list/1,2. Thanks to Serge
+ Aleynikov</p>
+ <p>
+ Own Id: OTP-10837</p>
+ </item>
+ <item>
+ <p>
+ Fix memory leak in file driver introduced in R16A.</p>
+ <p>
+ Own Id: OTP-10841</p>
+ </item>
+ <item>
+ <p>A bug in an ERTS internal queue implementation could
+ cause the loss of a wake up signal to a consumer thread.
+ This has now been fixed.</p>
+ <p>The effect of this bug, when triggered, was often only
+ a small or even no delay of certain operations. This
+ since, threads often are woken due to other unrelated
+ reasons. However, if the consumer thread was not woken
+ due to other reasons when the bug was triggered, these
+ operations could be left hanging, potentially for ever.
+ Such effects seems to have been very rare, but we have on
+ at least one occasion gotten a report about such an
+ issue.</p>
+ <p>Operations potentially effected by this bug:</p>
+ <taglist> <tag>Inspection of memory allocation
+ status</tag> <item>The Erlang process calling
+ <c>erlang:memory/[0,1]</c>, or
+ <c>erlang:system_info({allocator|allocator_sizes, _})</c>
+ could potentially hang waiting for responses from
+ involved threads.</item> <tag>Async thread pool
+ jobs</tag> <item>An async thread pool job request and/or
+ reply could potentially be left hanging. In OTP this only
+ effected file operations, but user implemented drivers
+ using the async thread pool were also effected. In the
+ file operation case, this would typically translate into
+ an Erlang process potentially hanging on the file
+ operation.</item> <tag>Shutting down the runtime
+ system</tag> <item>Due to the issue with the async thread
+ pool mentioned above, flushing of I/O while terminating
+ the runtime system could also potentially hang.</item>
+ <tag>ETS memory deallocation</tag> <item>Scheduled jobs
+ handling deallocation of the main structure of an ETS
+ table could potentially hang. This more or less only
+ translates into minor memory leaks.</item> <tag>Shutting
+ down distribution</tag> <item>The distribution shutdown
+ phase used when manually shutting down the distribution,
+ i.e., when calling <c>net_kernel:stop()</c>, could
+ potentially hang.</item> </taglist>
+ <p>
+ Own Id: OTP-10854</p>
+ </item>
+ <item>
+ <p>
+ OS X Snow Leopard now only uses write, as writev does not
+ work properly on very large files.</p>
+ <p>
+ Own Id: OTP-10858</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where line oriented file I/O using read_ahead
+ was very slow for files with very large difference in
+ line length.</p>
+ <p>
+ Own Id: OTP-10859</p>
+ </item>
+ <item>
+ <p>
+ In erts-5.10 (R16A) faulty hashvalues were produced for
+ non-ASCII atoms (characters in byte-range 128..255). This
+ has now been fixed and hashvalues conforms to previous
+ OTP releases.</p>
+ <p>
+ Own Id: OTP-10860</p>
+ </item>
+ <item>
+ <p>
+ Fixes of memory accesses that might be thread unsafe when
+ the runtime system has been linked against third-party
+ libraries for atomic memory operations during the build.
+ Most builds are uneffected by this bug. If triggered, the
+ runtime system will most likely crash more or less
+ immediately.</p>
+ <p>
+ Own Id: OTP-10875 Aux Id: OTP-10854 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug where it was longer possible to give the +sws
+ proposal flag to non-smp emulators.</p>
+ <p>
+ Own Id: OTP-10881 Aux Id: seq12258 </p>
+ </item>
+ <item>
+ <p>
+ Faulty type to bytes read for ReadFile on Windows. This
+ could cause windows systems to misbehave. The correct
+ type is now used.</p>
+ <p>
+ Own Id: OTP-10890</p>
+ </item>
+ <item>
+ <p>
+ Change default max ports for Windows to 8192. Having a
+ too large value caused Windows to not be able to recover
+ properly. If you want to use another value, pass <c>+Q
+ Value</c> to erl or werl.</p>
+ <p>
+ Own Id: OTP-10892</p>
+ </item>
+ <item>
+ <p>
+ Fix rare crash on halfword vm during code loading.</p>
+ <p>
+ Own Id: OTP-10896</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <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>
+ Added four new bifs, <c>erlang:binary_to_integer/1,2</c>,
+ <c>erlang:integer_to_binary/1</c>,
+ <c>erlang:binary_to_float/1</c> and
+ <c>erlang:float_to_binary/1,2</c>. These bifs work
+ similarly to how their list counterparts work, except
+ they operate on binaries. In most cases converting from
+ and to binaries is faster than converting from and to
+ lists. </p>
+ <p>
+ These bifs are auto-imported into erlang source files and
+ can therefore be used without the <c>erlang</c> prefix.</p>
+ <p>
+ Own Id: OTP-10300 Aux Id: kunagi-74 [74] </p>
+ </item>
+ <item>
+ <p>
+ The experimental support for packages has been removed.</p>
+ <p>
+ Own Id: OTP-10348 Aux Id: kunagi-316 [227] </p>
+ </item>
+ <item>
+ <p>The driver API function <seealso
+ marker="erl_driver#erl_drv_consume_timeslice"><c>erl_drv_consume_timeslice()</c></seealso>,
+ and the NIF API function <seealso
+ marker="erl_nif#enif_consume_timeslice"><c>enif_consume_timeslice()</c></seealso>
+ have been introduced.</p>
+ <p>These functions are provided in order to better
+ support co-operative scheduling, improve system
+ responsiveness, and to make it easier to prevent
+ misbehaviors of the VM due to a process or port
+ monopolizing a scheduler thread. They can be used when
+ dividing lengthy work into a number of repeated calls
+ without the need to use threads.</p>
+ <p>
+ Own Id: OTP-10810</p>
+ </item>
+ <item>
+ <p>
+ The list_to_integer/2 bif has been optimized when used
+ with bases other than 10.</p>
+ <p>
+ Own Id: OTP-10834 Aux Id: kunagi-74 [74] </p>
+ </item>
+ <item>
+ <p>
+ The git commit sha of the HEAD commit is now added to the
+ Erlang shell when compiling a non-released Erlang
+ version.</p>
+ <p>
+ Own Id: OTP-10838</p>
+ </item>
+ <item>
+ <p>Change caching policy for memory segment allocator.
+ For instance, prefer sbc segments over mbc segments,
+ caching policy is time-arrow aware, evicting older cached
+ segments to store newer segments. </p> <p>The default
+ number of cachable segment has been increased from five
+ to ten segments. This can be modified, same as before,
+ with the command line option <c>+MMmcs 5</c></p>
+ <p>Impact: Increased speed for processing on larger
+ objects, e.g. binaries. Slight increase of mapped and
+ resident memory. Tune your system with memory options to
+ <c>erl</c> for best performance.</p>
+ <p>
+ Own Id: OTP-10840</p>
+ </item>
+ <item>
+ <p>
+ Updated config.sub and config.guess to latest version
+ from gnu.org</p>
+ <p>
+ Own Id: OTP-10848</p>
+ </item>
+ <item>
+ <p>
+ Add an xcomp file for Blue Gene/Q. Thanks to Kostis
+ Sagonas.</p>
+ <p>
+ Own Id: OTP-10849</p>
+ </item>
+ <item>
+ <p>
+ Cleanup of documentation of the type language. Thanks to
+ Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-10850</p>
+ </item>
+ <item>
+ <p>
+ Change the return value of hipe_bifs:remove_refs_from/1.
+ Thanks to Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-10851</p>
+ </item>
+ <item>
+ <p>
+ As of ERTS-5.10/OTP-R16A node names passed in the EPMD
+ protocol are required to be encoded in UTF-8. Since EPMD
+ previously accepted latin1 encoded node names this is an
+ incompatibility. However, since Erlang nodes always have
+ required characters in node names to be 7-bit ASCII
+ characters (and still do require this), this
+ incompatibility should not effect anyone using EPMD as an
+ Erlang Port Mapper Daemon.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10872 Aux Id: OTP-10753 </p>
+ </item>
+ <item>
+ <p>
+ The +pc flag to erl can be used to set the range of
+ characters considered printable. This affects how the
+ shell and io:format("~tp",...) functionality does
+ heuristic string detection. More can be read in STDLIB
+ users guide.</p>
+ <p>
+ Own Id: OTP-10884</p>
+ </item>
+ <item>
+ <p>
+ Fix a number of type cast errors related to formatted
+ printing on Win64 that can potentially cause problem when
+ the Erlang VM exceeds 4 GB of ram. (Thanks to Blaine
+ Whittle for the original patch)</p>
+ <p>
+ Own Id: OTP-10887</p>
+ </item>
+ <item>
+ <p>
+ The effect of the deprecated environment variable
+ ERL_MAX_PORTS had been removed premeturely. It has now
+ been readded. Note that this is still scheduled to be
+ released in R17B.</p>
+ <p>
+ Own Id: OTP-10895</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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>
+ <item>
+ <p>
+ Fix fd leak when using async thread pool</p>
+ <p> When using the async thread pool, if an erlang
+ process asks to open a file and it gets shutdown/killed
+ while the file:open/2 call hasn't returned, it's possible
+ to leak a file descriptor against the target file. This
+ has now been fixed. (Thanks to Filipe David Manana)</p>
+ <p>
+ Own Id: OTP-10677</p>
+ </item>
+ <item>
+ <p>
+ Use sys/types.h instead of string.h to pull ssize_t
+ definition to erl_driver.h. This fixes build issue on
+ NetBSD. (Thanks to Yamamoto Takashi).</p>
+ <p>
+ Own Id: OTP-10699</p>
+ </item>
+ <item>
+ <p>
+ Arguments given with the -run or -s flags to erl are now
+ translated according to the file name encoding mode of
+ the runtime system.</p>
+ <p>
+ Own Id: OTP-10702</p>
+ </item>
+ <item>
+ <p>
+ The octet counters in the gen_tcp/inet interface could
+ behave in unexpected ways on 64bit platforms. The
+ behaviour is now as expected.</p>
+ <p>
+ Own Id: OTP-10746</p>
+ </item>
+ <item>
+ <p>
+ Certain linux kernels, most notably in redhat and CentOS
+ distribution, had a bug in writev which generated an
+ infinite loop in the tcp code of the VM. The bug is now
+ worked around.</p>
+ <p>
+ Own Id: OTP-10747</p>
+ </item>
+ <item>
+ <p>
+ A process that got killed (got an exit signal) while
+ operating on a compresseed file, could cause a
+ segmentation fault in the VM. This is now corrected.
+ Thanks to Filipe David Manana for identifying the problem
+ and submitting a solution.</p>
+ <p>
+ Own Id: OTP-10748</p>
+ </item>
+ <item>
+ <p>
+ Windows previously used three digit exponent in
+ formatting which caused difference between platforms, as
+ can be seen by float_to_list/1. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-10751</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>Process optimizations. The most notable:</p> <list>
+ <item>New internal process table implementation allowing
+ for both parallel reads as well as writes. Especially
+ read operations have become really cheap. This reduce
+ contention in various situations. For example when,
+ spawning processes, terminating processes, sending
+ messages, etc.</item> <item>Optimizations of run queue
+ management reducing contention.</item>
+ <item>Optimizations of process internal state changes reducing
+ contention.</item> </list> <p>These changes imply changes
+ of the characteristics the system. Most notable: changed
+ timing in the system.</p>
+ <p>
+ Own Id: OTP-9892 Aux Id: OTP-10167 </p>
+ </item>
+ <item>
+ <p>
+ Non-blocking code loading. Earlier when an Erlang module
+ was loaded, all other execution in the VM were halted
+ while the load operation was carried out in single
+ threaded mode. Now modules are loaded without blocking
+ the VM. Processes may continue executing undisturbed in
+ parallel during the entire load operation. The load
+ operation is completed by making the loaded code visible
+ to all processes in a consistent way with one single
+ atomic instruction. Non-blocking code loading will
+ improve realtime characteristics when modules are
+ 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>Remove VxWorks support</p>
+ <p>
+ Own Id: OTP-10146</p>
+ </item>
+ <item>
+ <p>
+ Added a general framework for executing benchmarks of
+ Erlang/OTP. Benchmarks for the Erlang VM and mnesia have
+ been incorporated in the framework. </p>
+ <p>
+ For details about how to add more benchmarks see
+ $ERL_TOP/HOWTO/BENCHMARKS.md in the source distribution.</p>
+ <p>
+ Own Id: OTP-10156</p>
+ </item>
+ <item>
+ <p>
+ Optimized deletion of ETS-tables which significantly
+ improves performance when large amounts of temporary
+ tables are used.</p>
+ <p>
+ This change imply changes of the characteristics the
+ system. Most notable: changed timing in the system.</p>
+ <p>
+ Own Id: OTP-10167 Aux Id: OTP-9892 </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>
+ New internal header scheme for allocators</p>
+ <p>
+ Impact: Reduces size on object allocated in multiblock
+ carriers by one word</p>
+ <p>
+ Own Id: OTP-10273 Aux Id: kunagi-20 [20] </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.This reduce
+ contention in various situations. For example when,
+ creating ports, terminating ports, etc. </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> <item>Improving responsiveness by bumping
+ reductions for a process calling a driver callback
+ directly.</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> <tag>Reduction cost of calling driver
+ callbacks</tag> <item>Calling a driver callback is quite
+ costly. This was previously not reflected in reduction
+ cost at all. Since the reduction cost now has increased,
+ a process performing lots of direct driver calls will be
+ scheduled out more frequently than before.</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: OTP-9892 </p>
+ </item>
+ <item>
+ <p>
+ The experimental support for packages has been removed.</p>
+ <p>
+ Own Id: OTP-10348 Aux Id: kunagi-316 [227] </p>
+ </item>
+ <item>
+ <p>
+ Wrong parameters when setting seq_trace-tokens from
+ within a trace-pattern could crash the VM. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-10522</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>
+ Add insert_element/3 and delete_element/2</p>
+ <p>
+ Own Id: OTP-10643</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-9892, OTP-10336 </p>
+ </item>
+ <item>
+ <p>
+ The previously (in R15) proposed scheduler wakeup
+ strategy is now used by default. This strategy is not as
+ quick to forget about previous overload as the previous
+ strategy.</p>
+ <p>
+ This change imply changes of the characteristics the
+ system. Most notable: When a small overload comes and
+ then disappears repeatedly, the system will for a bit
+ longer time be willing to wake up schedulers than before.
+ Timing in the system will due to this also change.</p>
+ <p>
+ The previous strategy can still be enabled by passing the
+ <seealso marker="erl#+sws">+sws legacy</seealso> command
+ line flag to <c>erl</c>.</p>
+ <p>
+ Own Id: OTP-10661 Aux Id: OTP-10033 </p>
+ </item>
+ <item>
+ <p>
+ The <seealso marker="erl#+stbt">+stbt</seealso> command line
+ argument of <c>erl</c> was added. This argument can be
+ used for trying to set scheduler bind type. Upon failure
+ unbound schedulers will be used.</p>
+ <p>
+ Own Id: OTP-10668</p>
+ </item>
+ <item>
+ <p>
+ Support ANSI in console</p>
+ <p>
+ Unix platforms will no longer filter control sequences to
+ the ttsl driver thus enabling ANSI and colors in console.
+ (Thanks to Pedram Nimreezi)</p>
+ <p>
+ Own Id: OTP-10678</p>
+ </item>
+ <item>
+ <p>Add file:allocate/3 operation</p>
+ <p>This operation allows pre-allocation of space for
+ files. It succeeds only on systems that support such
+ operation. (Thanks to Filipe David Manana)</p>
+ <p>
+ Own Id: OTP-10680</p>
+ </item>
+ <item>
+ <p>Treat <c>-Wreturn-type</c> warnings as error when
+ using GCC (Thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10683</p>
+ </item>
+ <item>
+ <p>
+ Implement ./otp_build configure --enable-silent-rules</p>
+ <p>
+ With silent rules, the output of make is less verbose and
+ compilation warnings are easier to spot. Silent rules are
+ disabled by default and can be disabled or enabled at
+ will by make V=0 and make V=1. (Thanks to Anthony Ramine)</p>
+ <p>
+ Own Id: OTP-10726</p>
+ </item>
+ <item>
+ <p>
+ Use share flags for all file operations on Windows.
+ Thanks to Filipe David Borba Manana.</p>
+ <p>
+ Own Id: OTP-10727</p>
+ </item>
+ <item>
+ <p>
+ Make/fakefop adjustments. Thanks to Tuncer Ayaz and
+ Sebastian Rasmussen.</p>
+ <p>
+ Own Id: OTP-10733</p>
+ </item>
+ <item>
+ <p>
+ The runtime system will now by default use 10 async
+ threads if thread support has been enabled when building
+ the runtime system.</p>
+ <p>
+ This will prevent long blocking file-operations from
+ blocking scheduler threads for long periods of time,
+ which can be harmful. Apart from file-operations, it also
+ effects other operations scheduled on the async thread
+ pool by user implemented drivers.</p>
+ <p>
+ The amount of async threads can be controlled by using
+ the <seealso
+ marker="erl#async_thread_pool_size"><c>+A</c></seealso>
+ command line argument of <c>erl(1)</c>. When running some
+ offline tools you <em>might</em> want to disable async
+ threads, but you are advised <em>not</em> to in the
+ general case. Instead, you might want to increase the
+ amount of async threads used.</p>
+ <p>
+ This change imply changes of the characteristics the
+ system compared to the previous default. The
+ responsiveness of the system as a whole will be improved.
+ Operations scheduled on the async thread pool will get an
+ increased latency. The throughput of these operations may
+ increase, or decrease depending on the type of the
+ operations and how they get scheduled. In the case of
+ file operations, the throughput very much depends on how
+ the Erlang application access files. Multiple concurrent
+ accesses to different files have the potential of an
+ increased throughput.</p>
+ <p>
+ Own Id: OTP-10736</p>
+ </item>
+ <item>
+ <p>
+ The default reader group limit has been increased to 64
+ from 8. This limit can be set using the <c>+rg</c>
+ command line argument of <c>erl(1)</c>.</p>
+ <p>
+ This change of default value will reduce lock contention
+ on ETS tables using the <c>read_concurrency</c> option at
+ the expense of memory consumption when the amount of
+ schedulers and logical processors are beween 8 and 64.
+ For more information, see documentation of the <c>+rg</c>
+ command line argument of <c>erl(1)</c>.</p>
+ <p>
+ Own Id: OTP-10737</p>
+ </item>
+ <item>
+ <p>
+ New BIF float_to_list/2 which solves a problem of
+ float_to_list/1 that doesn't allow specifying the number
+ of digits after the decimal point when formatting floats
+ (Thanks to Serge Aleynikov).</p>
+ <p>
+ Own Id: OTP-10752</p>
+ </item>
+ <item>
+ <p>
+ Limited support for unicode atoms in the external format
+ and in the internal representation of the vm. This is a
+ preparative feature in order to support communication
+ with future releases of Erlang/OTP that may create
+ unicode atoms.</p>
+ <p>
+ Own Id: OTP-10753</p>
+ </item>
+ <item>
+ <p>
+ Increased potential concurrency in ETS for
+ <c>write_concurrency</c> option. The number of internal
+ table locks has increased from 16 to 64. This makes it
+ four times less likely that two concurrent processes
+ writing to the same table would collide and thereby
+ serialized. The cost is an increased constant memory
+ footprint for tables using write_concurrency. The memory
+ consumption per inserted record is not affected. The
+ increased footprint can be particularly large if
+ <c>write_concurrency</c> is combined with
+ <c>read_concurrency</c>.</p>
+ <p>
+ Own Id: OTP-10787</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 5.9.3.1</title>
<section><title>Known Bugs and Problems</title>
diff --git a/erts/doc/src/part.xml b/erts/doc/src/part.xml
index e27b722721..fa50329cad 100644
--- a/erts/doc/src/part.xml
+++ b/erts/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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..e55923c344 100644
--- a/erts/doc/src/ref_man.xml
+++ b/erts/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 985ef72517..133ecd69e4 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -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
@@ -20,6 +20,8 @@
include $(ERL_TOP)/make/target.mk
include ../vsn.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
+-include $(TARGET)/gen_git_version.mk
+
ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@
HIPE_ENABLED=@HIPE_ENABLED@
@@ -61,7 +63,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 +94,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
@@ -196,7 +198,7 @@ else
EMU_CC = @EMU_CC@
endif
WFLAGS = @WFLAGS@
-CFLAGS = @STATIC_CFLAGS@ $(TYPE_FLAGS) $(FLAVOR_FLAGS) $(DEFS) $(WFLAGS) $(THR_DEFS) $(ARCHCFLAGS)
+CFLAGS = @STATIC_CFLAGS@ $(TYPE_FLAGS) $(FLAVOR_FLAGS) $(DEFS) $(WFLAGS) $(THR_DEFS) $(ARCHCFLAGS) $(GIT_VSN)
HCC = @HCC@
LD = @LD@
DEXPORT = @DEXPORT@
@@ -332,15 +334,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 +401,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 +461,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 +472,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 +493,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 +527,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 +562,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 +575,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 +588,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 +613,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 +661,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
# ----------------------------------------------------------------------
@@ -709,7 +695,9 @@ EMU_OBJS = \
$(OBJDIR)/beam_emu.o $(OBJDIR)/beam_opcodes.o \
$(OBJDIR)/beam_load.o $(OBJDIR)/beam_bif_load.o \
$(OBJDIR)/beam_debug.o $(OBJDIR)/beam_bp.o \
- $(OBJDIR)/beam_catches.o
+ $(OBJDIR)/beam_catches.o \
+ $(OBJDIR)/code_ix.o \
+ $(OBJDIR)/beam_ranges.o
RUN_OBJS = \
$(OBJDIR)/erl_pbifs.o $(OBJDIR)/benchmark.o \
@@ -751,7 +739,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 = \
@@ -779,12 +768,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 \
@@ -792,9 +777,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 \
@@ -863,28 +846,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 \
@@ -927,49 +910,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
@@ -1050,6 +1008,12 @@ DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLA
SYS_SRC=$(ALL_SYS_SRC)
endif
+.PHONY: $(TARGET)/gen_git_version.mk
+$(TARGET)/gen_git_version.mk:
+# We touch beam/erl_bif.info.c if we regenerated the git version to force a
+# rebuild.
+ if $(gen_verbose)utils/gen_git_version $@; then touch beam/erl_bif_info.c; fi
+
.PHONY: depend
ifdef VOID_EMULATOR
depend:
@@ -1057,23 +1021,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..84d2d5e3ed 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -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
@@ -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();
}
@@ -132,9 +132,17 @@ atom_hash(Atom* obj)
byte* p = obj->name;
int len = obj->len;
HashValue h = 0, g;
+ byte v;
while(len--) {
- h = (h << 4) + *p++;
+ v = *p++;
+ /* latin1 clutch for r16 */
+ if ((v & 0xFE) == 0xC2 && (*p & 0xC0) == 0x80) {
+ v = (v << 6) | (*p & 0x3F);
+ p++; len--;
+ }
+ /* normal hashpjw follows for v */
+ h = (h << 4) + v;
if ((g = h & 0xf0000000)) {
h ^= (g >> 24);
h ^= g;
@@ -162,6 +170,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 +201,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 +375,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, ErtsAtomEncoding enc)
{
+ 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 (enc == ERTS_ATOM_ENC_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 +449,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..5904ae0f7e 100644
--- a/erts/emulator/beam/atom.h
+++ b/erts/emulator/beam/atom.h
@@ -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
@@ -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, ErtsAtomEncoding enc);
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 afcbd732df..ce60bb9bbc 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -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
@@ -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
@@ -261,6 +269,7 @@ atom hipe_architecture
atom http httph https http_response http_request http_header http_eoh http_error http_bin httph_bin
atom id
atom if_clause
+atom ignore
atom imports
atom in
atom in_exiting
@@ -408,6 +417,7 @@ atom overlapped_io
atom owner
atom packet
atom packet_size
+atom parallelism
atom Plus='+'
atom pause
atom pending
@@ -419,12 +429,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
@@ -434,6 +444,7 @@ atom procs
atom profile
atom protected
atom protection
+atom ptab_list_continue
atom public
atom purify
atom quantify
@@ -474,6 +485,7 @@ atom scheduler
atom scheduler_id
atom schedulers_online
atom scheme
+atom scientific
atom scope
atom sensitive
atom sequential_tracer
@@ -481,6 +493,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 02f37bf9bc..649594a334 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-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
@@ -37,25 +37,83 @@
static void set_default_trace_pattern(Eterm module);
static Eterm check_process_code(Process* rp, Module* modp);
-static void delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp);
-static void delete_export_references(Eterm module);
-static int purge_module(int module);
+static void delete_code(Module* modp);
static void decrement_refc(BeamInstr* code);
static int is_native(BeamInstr* code);
static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size);
-static void remove_from_address_table(BeamInstr* code);
-Eterm
-load_module_2(BIF_ALIST_2)
+
+
+BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
{
- Eterm reason;
- Eterm* hp;
- int sz;
- byte* code;
+ Module* modp;
Eterm res;
+ ErtsCodeIndex code_ix;
+
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ code_ix = erts_active_code_ix();
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ return am_undefined;
+ }
+ erts_rlock_old_code(code_ix);
+ res = ((modp->curr.code && is_native(modp->curr.code)) ||
+ (modp->old.code != 0 && is_native(modp->old.code))) ?
+ am_true : am_false;
+ erts_runlock_old_code(code_ix);
+ return res;
+}
+
+BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
+{
+ Module* modp;
+ Eterm res;
+
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD3(bif_export[BIF_code_make_stub_module_3],
+ BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+ }
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
+
+ if (modp && modp->curr.num_breakpoints > 0) {
+ ASSERT(modp->curr.code != NULL);
+ erts_clear_module_break(modp);
+ ASSERT(modp->curr.num_breakpoints == 0);
+ }
+
+ erts_start_staging_code_ix();
+
+ res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+
+ if (res == BIF_ARG_1) {
+ erts_end_staging_code_ix();
+ erts_commit_staging_code_ix();
+ }
+ else {
+ erts_abort_staging_code_ix();
+ }
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
+ return res;
+}
+
+BIF_RETTYPE
+prepare_loading_2(BIF_ALIST_2)
+{
byte* temp_alloc = NULL;
- struct LoaderState* stp;
+ byte* code;
+ Uint sz;
+ Binary* magic;
+ Eterm reason;
+ Eterm* hp;
+ Eterm res;
if (is_not_atom(BIF_ARG_1)) {
error:
@@ -65,108 +123,307 @@ load_module_2(BIF_ALIST_2)
if ((code = erts_get_aligned_binary_bytes(BIF_ARG_2, &temp_alloc)) == NULL) {
goto error;
}
- hp = HAlloc(BIF_P, 3);
- /*
- * Read the BEAM file and prepare the module for loading.
- */
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
sz = binary_size(BIF_ARG_2);
- reason = erts_prepare_loading(stp, BIF_P, BIF_P->group_leader,
+ reason = erts_prepare_loading(magic, BIF_P, BIF_P->group_leader,
&BIF_ARG_1, code, sz);
erts_free_aligned_binary_bytes(temp_alloc);
if (reason != NIL) {
+ hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_error, reason);
BIF_RET(res);
}
+ hp = HAlloc(BIF_P, PROC_BIN_SIZE);
+ res = erts_mk_magic_binary_term(&hp, &MSO(BIF_P), magic);
+ erts_refc_dec(&magic->refc, 1);
+ BIF_RET(res);
+}
+
+struct m {
+ Binary* code;
+ Eterm module;
+ Module* modp;
+ Uint exception;
+};
+
+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)
+{
+ Eterm* hp = HAlloc(p, 3 + 2*exceptions);
+ Eterm res = NIL;
+
+ mp += exceptions - 1;
+ while (exceptions > 0) {
+ if (mp->exception) {
+ res = CONS(hp, mp->module, res);
+ hp += 2;
+ exceptions--;
+ }
+ mp--;
+ }
+ return TUPLE2(hp, tag, res);
+}
+
+
+BIF_RETTYPE
+finish_loading_1(BIF_ALIST_1)
+{
+ int i;
+ int n;
+ struct m* p = NULL;
+ Uint exceptions;
+ Eterm res;
+ int is_blocking = 0;
+ int do_commit = 0;
+
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD1(bif_export[BIF_finish_loading_1], BIF_P, BIF_ARG_1);
+ }
/*
- * Stop all other processes and finish the loading of the module.
+ * Validate the argument before we start loading; it must be a
+ * proper list where each element is a magic binary containing
+ * prepared (not previously loaded) code.
+ *
+ * First count the number of elements and allocate an array
+ * to keep the elements in.
*/
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- reason = erts_finish_loading(stp, BIF_P, 0, &BIF_ARG_1);
- if (reason != NIL) {
- res = TUPLE2(hp, am_error, reason);
- } else {
- set_default_trace_pattern(BIF_ARG_1);
- res = TUPLE2(hp, am_module, BIF_ARG_1);
+ n = list_length(BIF_ARG_1);
+ if (n == -1) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
+ goto done;
}
+ p = erts_alloc(ERTS_ALC_T_LOADER_TMP, n*sizeof(struct m));
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- BIF_RET(res);
-}
+ /*
+ * We now know that the argument is a proper list. Validate
+ * and collect the binaries into the array.
+ */
-BIF_RETTYPE purge_module_1(BIF_ALIST_1)
-{
- int purge_res;
+ for (i = 0; i < n; i++) {
+ Eterm* cons = list_val(BIF_ARG_1);
+ Eterm term = CAR(cons);
+ ProcBin* pb;
- if (is_not_atom(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ if (!ERTS_TERM_IS_MAGIC_BINARY(term)) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
+ goto done;
+ }
+ pb = (ProcBin*) binary_val(term);
+ p[i].code = pb->val;
+ p[i].module = erts_module_for_prepared_code(p[i].code);
+ if (p[i].module == NIL) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
+ goto done;
+ }
+ BIF_ARG_1 = CDR(cons);
}
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ /*
+ * Since we cannot handle atomic loading of a group of modules
+ * if one or more of them uses on_load, we will only allow one
+ * element in the list. This limitation is intended to be
+ * lifted in the future.
+ */
- erts_export_consolidate();
- purge_res = purge_module(atom_val(BIF_ARG_1));
+ if (n > 1) {
+ ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT);
+ goto done;
+ }
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ /*
+ * All types are correct. There cannot be a BADARG from now on.
+ * Before we can start loading, we must check whether any of
+ * the modules already has old code. To avoid a race, we must
+ * not allow other process to initiate a code loading operation
+ * from now on.
+ */
- if (purge_res < 0) {
- BIF_ERROR(BIF_P, BADARG);
+ res = am_ok;
+ erts_start_staging_code_ix();
+
+ for (i = 0; i < n; i++) {
+ p[i].modp = erts_put_module(p[i].module);
+ }
+ for (i = 0; i < n; i++) {
+ if (p[i].modp->curr.num_breakpoints > 0 ||
+ p[i].modp->curr.num_traced_exports > 0 ||
+ erts_is_default_trace_enabled()) {
+ /* tracing involved, fallback with thread blocking */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ break;
+ }
}
- BIF_RET(am_true);
-}
-BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1)
-{
- Module* modp;
+ if (is_blocking) {
+ for (i = 0; i < n; i++) {
+ if (p[i].modp->curr.num_breakpoints) {
+ erts_clear_module_break(p[i].modp);
+ ASSERT(p[i].modp->curr.num_breakpoints == 0);
+ }
+ }
+ }
- if (is_not_atom(BIF_ARG_1)) {
- BIF_ERROR(BIF_P, BADARG);
+ exceptions = 0;
+ for (i = 0; i < n; i++) {
+ p[i].exception = 0;
+ if (p[i].modp->curr.code && p[i].modp->old.code) {
+ p[i].exception = 1;
+ exceptions++;
+ }
}
- if ((modp = erts_get_module(BIF_ARG_1)) == NULL) {
- return am_undefined;
+
+ if (exceptions) {
+ res = exception_list(BIF_P, am_not_purged, p, exceptions);
+ } else {
+ /*
+ * Now we can load all code. This can't fail.
+ */
+
+ exceptions = 0;
+ for (i = 0; i < n; i++) {
+ Eterm mod;
+ Eterm retval;
+
+ erts_refc_inc(&p[i].code->refc, 1);
+ retval = erts_finish_loading(p[i].code, BIF_P, 0, &mod);
+ ASSERT(retval == NIL || retval == am_on_load);
+ if (retval == am_on_load) {
+ p[i].exception = 1;
+ exceptions++;
+ }
+ }
+
+ /*
+ * Check whether any module has an on_load_handler.
+ */
+
+ if (exceptions) {
+ res = exception_list(BIF_P, am_on_load, p, exceptions);
+ }
+ do_commit = 1;
}
- return ((modp->code && is_native(modp->code)) ||
- (modp->old_code != 0 && is_native(modp->old_code))) ?
- am_true : am_false;
+
+done:
+ return staging_epilogue(BIF_P, do_commit, res, is_blocking, p, n);
}
-BIF_RETTYPE code_make_stub_module_3(BIF_ALIST_3)
-{
- Eterm res;
+static Eterm
+staging_epilogue(Process* c_p, int commit, Eterm res, int is_blocking,
+ struct m* loaded, int nloaded)
+{
+#ifdef ERTS_SMP
+ if (is_blocking || !commit)
+#endif
+ {
+ if (commit) {
+ erts_end_staging_code_ix();
+ erts_commit_staging_code_ix();
+ if (loaded) {
+ int i;
+ for (i=0; i < nloaded; i++) {
+ set_default_trace_pattern(loaded[i].module);
+ }
+ }
+ }
+ else {
+ erts_abort_staging_code_ix();
+ }
+ if (loaded) {
+ erts_free(ERTS_ALC_T_LOADER_TMP, loaded);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ erts_release_code_write_permission();
+ return res;
+ }
+#ifdef ERTS_SMP
+ else {
+ ASSERT(is_value(res));
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ if (loaded) {
+ erts_free(ERTS_ALC_T_LOADER_TMP, loaded);
+ }
+ erts_end_staging_code_ix();
+ /*
+ * Now we must wait for all schedulers to do a memory barrier before
+ * 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.
+ */
+ 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);
+ /*
+ * smp_code_ix_commiter() will do the rest "later"
+ * and resume this process to return 'res'.
+ */
+ ERTS_BIF_YIELD_RETURN(c_p, res);
+ }
+#endif
+}
- erts_export_consolidate();
- res = erts_make_stub_module(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- return res;
+#ifdef ERTS_SMP
+static void smp_code_ix_commiter(void* null)
+{
+ Process* p = commiter_state.stager;
+
+ erts_commit_staging_code_ix();
+#ifdef DEBUG
+ commiter_state.stager = NULL;
+#endif
+ 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);
}
+#endif /* ERTS_SMP */
+
+
BIF_RETTYPE
check_old_code_1(BIF_ALIST_1)
{
+ ErtsCodeIndex code_ix;
Module* modp;
+ Eterm res = am_false;
if (is_not_atom(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
- modp = erts_get_module(BIF_ARG_1);
- if (modp == NULL) { /* Doesn't exist. */
- BIF_RET(am_false);
- } else if (modp->old_code == NULL) { /* No old code. */
- BIF_RET(am_false);
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+ if (modp != NULL) {
+ erts_rlock_old_code(code_ix);
+ if (modp->old.code != NULL) {
+ res = am_true;
+ }
+ erts_runlock_old_code(code_ix);
}
- BIF_RET(am_true);
+ BIF_RET(res);
}
Eterm
@@ -180,14 +437,19 @@ check_process_code_2(BIF_ALIST_2)
}
if (is_internal_pid(BIF_ARG_1)) {
Eterm res;
- if (internal_pid_index(BIF_ARG_1) >= erts_max_processes)
- goto error;
- modp = erts_get_module(BIF_ARG_2);
+ ErtsCodeIndex code_ix;
+
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(BIF_ARG_2, code_ix);
if (modp == NULL) { /* Doesn't exist. */
return am_false;
- } else if (modp->old_code == NULL) { /* No old code. */
+ }
+ erts_rlock_old_code(code_ix);
+ if (modp->old.code == NULL) { /* No old code. */
+ erts_runlock_old_code(code_ix);
return am_false;
}
+ erts_runlock_old_code(code_ix);
#ifdef ERTS_SMP
rp = erts_pid2proc_suspend(BIF_P, ERTS_PROC_LOCK_MAIN,
@@ -202,7 +464,14 @@ check_process_code_2(BIF_ALIST_2)
ERTS_BIF_YIELD2(bif_export[BIF_check_process_code_2], BIF_P,
BIF_ARG_1, BIF_ARG_2);
}
- res = check_process_code(rp, modp);
+ erts_rlock_old_code(code_ix);
+ if (modp->old.code != NULL) { /* must check again */
+ res = check_process_code(rp, modp);
+ }
+ else {
+ res = am_false;
+ }
+ erts_runlock_old_code(code_ix);
#ifdef ERTS_SMP
if (BIF_P != rp) {
erts_resume(rp, ERTS_PROC_LOCK_MAIN);
@@ -223,56 +492,71 @@ check_process_code_2(BIF_ALIST_2)
BIF_RETTYPE delete_module_1(BIF_ALIST_1)
{
- int res;
+ ErtsCodeIndex code_ix;
+ Module* modp;
+ int is_blocking = 0;
+ int success = 0;
+ Eterm res = NIL;
- if (is_not_atom(BIF_ARG_1))
- goto badarg;
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD1(bif_export[BIF_delete_module_1], BIF_P, BIF_ARG_1);
+ }
{
- Module *modp = erts_get_module(BIF_ARG_1);
+ erts_start_staging_code_ix();
+ code_ix = erts_staging_code_ix();
+ modp = erts_get_module(BIF_ARG_1, code_ix);
if (!modp) {
res = am_undefined;
}
- else if (modp->old_code != 0) {
+ else if (modp->old.code != 0) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp, "Module %T must be purged before loading\n",
BIF_ARG_1);
erts_send_error_to_logger(BIF_P->group_leader, dsbufp);
- res = am_badarg;
+ ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG);
}
else {
- delete_export_references(BIF_ARG_1);
- delete_code(BIF_P, 0, modp);
+ if (modp->curr.num_breakpoints > 0 ||
+ modp->curr.num_traced_exports > 0) {
+ /* we have tracing, retry single threaded */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ if (modp->curr.num_breakpoints) {
+ erts_clear_module_break(modp);
+ ASSERT(modp->curr.num_breakpoints == 0);
+ }
+ }
+ delete_code(modp);
res = am_true;
+ success = 1;
}
}
-
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
-
- if (res == am_badarg) {
- badarg:
- BIF_ERROR(BIF_P, BADARG);
- }
- BIF_RET(res);
+ return staging_epilogue(BIF_P, success, res, is_blocking, NULL, 0);
}
BIF_RETTYPE module_loaded_1(BIF_ALIST_1)
{
Module* modp;
+ ErtsCodeIndex code_ix;
+ Eterm res = am_false;
if (is_not_atom(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
- if ((modp = erts_get_module(BIF_ARG_1)) == NULL ||
- modp->code == NULL ||
- modp->code[MI_ON_LOAD_FUNCTION_PTR] != 0) {
- BIF_RET(am_false);
+ code_ix = erts_active_code_ix();
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) != NULL) {
+ if (modp->curr.code != NULL
+ && modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] == 0) {
+ res = am_true;
+ }
}
- BIF_RET(am_true);
+ BIF_RET(res);
}
BIF_RETTYPE pre_loaded_0(BIF_ALIST_0)
@@ -282,27 +566,28 @@ BIF_RETTYPE pre_loaded_0(BIF_ALIST_0)
BIF_RETTYPE loaded_0(BIF_ALIST_0)
{
+ ErtsCodeIndex code_ix = erts_active_code_ix();
+ Module* modp;
Eterm previous = NIL;
Eterm* hp;
int i;
int j = 0;
-
- for (i = 0; i < module_code_size(); i++) {
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
+
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ if ((modp = module_code(i, code_ix)) != NULL &&
+ ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
j++;
}
}
if (j > 0) {
hp = HAlloc(BIF_P, j*2);
- for (i = 0; i < module_code_size(); i++) {
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
- previous = CONS(hp, make_atom(module_code(i)->module),
- previous);
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ if ((modp=module_code(i,code_ix)) != NULL &&
+ ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
+ previous = CONS(hp, make_atom(modp->module), previous);
hp += 2;
}
}
@@ -312,54 +597,65 @@ BIF_RETTYPE loaded_0(BIF_ALIST_0)
BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1)
{
- Module* modp = erts_get_module(BIF_ARG_1);
- Eterm on_load;
+ Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
- if (!modp || modp->code == 0) {
- error:
- BIF_ERROR(BIF_P, BADARG);
+ if (modp && modp->curr.code) {
+ BIF_TRAP_CODE_PTR_0(BIF_P, modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]);
}
- if ((on_load = modp->code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
- goto error;
+ else {
+ BIF_ERROR(BIF_P, BADARG);
}
- BIF_TRAP_CODE_PTR_0(BIF_P, on_load);
}
BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
{
- Module* modp = erts_get_module(BIF_ARG_1);
+ ErtsCodeIndex code_ix;
+ Module* modp;
Eterm on_load;
- if (!modp || modp->code == 0) {
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_finish_after_on_load_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
+ /* ToDo: Use code_ix staging instead of thread blocking */
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(BIF_ARG_1, code_ix);
+
+ if (!modp || modp->curr.code == 0) {
error:
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
BIF_ERROR(BIF_P, BADARG);
}
- if ((on_load = modp->code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
+ if ((on_load = modp->curr.code[MI_ON_LOAD_FUNCTION_PTR]) == 0) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
goto error;
}
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
-
if (BIF_ARG_2 == am_true) {
int i;
/*
* The on_load function succeded. Fix up export entries.
*/
- for (i = 0; i < export_list_size(); i++) {
- Export *ep = export_list(i);
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export *ep = export_list(i,code_ix);
if (ep != NULL &&
ep->code[0] == BIF_ARG_1 &&
ep->code[4] != 0) {
- ep->address = (void *) ep->code[4];
+ ep->addressv[code_ix] = (void *) ep->code[4];
ep->code[4] = 0;
}
}
- modp->code[MI_ON_LOAD_FUNCTION_PTR] = 0;
+ modp->curr.code[MI_ON_LOAD_FUNCTION_PTR] = 0;
set_default_trace_pattern(BIF_ARG_1);
} else if (BIF_ARG_2 == am_false) {
BeamInstr* code;
@@ -370,19 +666,21 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
* This is an combination of delete and purge. We purge
* the current code; the old code is not touched.
*/
- erts_total_code_size -= modp->code_length;
- code = modp->code;
- end = (BeamInstr *)((char *)code + modp->code_length);
+ erts_total_code_size -= modp->curr.code_length;
+ code = modp->curr.code;
+ end = (BeamInstr *)((char *)code + modp->curr.code_length);
erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->catches, code, modp->code_length);
+ beam_catches_delmod(modp->curr.catches, code, modp->curr.code_length,
+ erts_active_code_ix());
erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->code = NULL;
- modp->code_length = 0;
- modp->catches = BEAM_CATCHES_NIL;
- remove_from_address_table(code);
+ modp->curr.code = NULL;
+ modp->curr.code_length = 0;
+ modp->curr.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
}
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
BIF_RET(am_true);
}
@@ -403,11 +701,11 @@ set_default_trace_pattern(Eterm module)
if (trace_pattern_is_on) {
Eterm mfa[1];
mfa[0] = module;
- (void) erts_set_trace_pattern(mfa, 1,
+ (void) erts_set_trace_pattern(0, mfa, 1,
match_spec,
meta_match_spec,
1, trace_pattern_flags,
- meta_tracer_pid);
+ meta_tracer_pid, 1);
}
}
@@ -427,10 +725,10 @@ check_process_code(Process* rp, Module* modp)
/*
* Pick up limits for the module.
*/
- start = modp->old_code;
- end = (BeamInstr *)((char *)start + modp->old_code_length);
+ start = modp->old.code;
+ end = (BeamInstr *)((char *)start + modp->old.code_length);
mod_start = (char *) start;
- mod_size = modp->old_code_length;
+ mod_size = modp->old.code_length;
/*
* Check if current instruction or continuation pointer points into module.
@@ -562,10 +860,10 @@ check_process_code(Process* rp, Module* modp)
done_gc = 1;
FLAGS(rp) |= F_NEED_FULLSWEEP;
(void) erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity);
- literals = (Eterm *) modp->old_code[MI_LITERALS_START];
- lit_size = (Eterm *) modp->old_code[MI_LITERALS_END] - literals;
+ literals = (Eterm *) modp->old.code[MI_LITERALS_START];
+ lit_size = (Eterm *) modp->old.code[MI_LITERALS_END] - literals;
oh = (struct erl_off_heap_header *)
- modp->old_code[MI_LITERALS_OFF_HEAP];
+ modp->old.code[MI_LITERALS_OFF_HEAP];
erts_garbage_collect_literals(rp, literals, lit_size, oh);
}
}
@@ -624,53 +922,82 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
#undef in_area
-
-static int
-purge_module(int module)
+BIF_RETTYPE purge_module_1(BIF_ALIST_1)
{
+ ErtsCodeIndex code_ix;
BeamInstr* code;
BeamInstr* end;
Module* modp;
+ int is_blocking = 0;
+ Eterm ret;
- /*
- * Correct module?
- */
-
- if ((modp = erts_get_module(make_atom(module))) == NULL) {
- return -2;
+ if (is_not_atom(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
}
- /*
- * Any code to purge?
- */
- if (modp->old_code == 0) {
- return -1;
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD1(bif_export[BIF_purge_module_1], BIF_P, BIF_ARG_1);
}
+ code_ix = erts_active_code_ix();
+
/*
- * Unload any NIF library
+ * Correct module?
*/
- if (modp->old_nif != NULL) {
- erts_unload_nif(modp->old_nif);
- modp->old_nif = NULL;
+
+ if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) {
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
}
+ else {
+ erts_rwlock_old_code(code_ix);
- /*
- * Remove the old code.
- */
- ASSERT(erts_total_code_size >= modp->old_code_length);
- erts_total_code_size -= modp->old_code_length;
- code = modp->old_code;
- end = (BeamInstr *)((char *)code + modp->old_code_length);
- erts_cleanup_funs_on_purge(code, end);
- beam_catches_delmod(modp->old_catches, code, modp->old_code_length);
- decrement_refc(code);
- erts_free(ERTS_ALC_T_CODE, (void *) code);
- modp->old_code = NULL;
- modp->old_code_length = 0;
- modp->old_catches = BEAM_CATCHES_NIL;
- remove_from_address_table(code);
- return 0;
+ /*
+ * Any code to purge?
+ */
+ if (modp->old.code == 0) {
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ }
+ else {
+ /*
+ * Unload any NIF library
+ */
+ if (modp->old.nif != NULL) {
+ /* ToDo: Do unload nif without blocking */
+ erts_rwunlock_old_code(code_ix);
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+ is_blocking = 1;
+ erts_rwlock_old_code(code_ix);
+ erts_unload_nif(modp->old.nif);
+ modp->old.nif = NULL;
+ }
+
+ /*
+ * Remove the old code.
+ */
+ ASSERT(erts_total_code_size >= modp->old.code_length);
+ erts_total_code_size -= modp->old.code_length;
+ code = modp->old.code;
+ end = (BeamInstr *)((char *)code + modp->old.code_length);
+ erts_cleanup_funs_on_purge(code, end);
+ beam_catches_delmod(modp->old.catches, code, modp->old.code_length,
+ code_ix);
+ decrement_refc(code);
+ erts_free(ERTS_ALC_T_CODE, (void *) code);
+ modp->old.code = NULL;
+ modp->old.code_length = 0;
+ modp->old.catches = BEAM_CATCHES_NIL;
+ erts_remove_from_ranges(code);
+ ERTS_BIF_PREP_RET(ret, am_true);
+ }
+ erts_rwunlock_old_code(code_ix);
+ }
+ if (is_blocking) {
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ }
+ erts_release_code_write_permission();
+ return ret;
}
static void
@@ -690,92 +1017,48 @@ decrement_refc(BeamInstr* code)
}
}
-static void
-remove_from_address_table(BeamInstr* code)
-{
- int i;
-
- for (i = 0; i < num_loaded_modules; i++) {
- if (modules[i].start == code) {
- num_loaded_modules--;
- while (i < num_loaded_modules) {
- modules[i] = modules[i+1];
- i++;
- }
- mid_module = &modules[num_loaded_modules/2];
- return;
- }
- }
- ASSERT(0); /* Not found? */
-}
-
-
/*
- * Move code from current to old.
+ * Move code from current to old and null all export entries for the module
*/
-static void
-delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp)
-{
-#ifdef ERTS_ENABLE_LOCK_CHECK
-#ifdef ERTS_SMP
- if (c_p && c_p_locks)
- erts_proc_lc_chk_only_proc_main(c_p);
- else
-#endif
- erts_lc_check_exact(NULL, 0);
-#endif
-
- /*
- * Clear breakpoints if any
- */
- if (modp->code != NULL && modp->code[MI_NUM_BREAKPOINTS] > 0) {
- if (c_p && c_p_locks)
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
- erts_clear_module_break(modp);
- modp->code[MI_NUM_BREAKPOINTS] = 0;
- erts_smp_thr_progress_unblock();
- if (c_p && c_p_locks)
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
- }
- modp->old_code = modp->code;
- modp->old_code_length = modp->code_length;
- modp->old_catches = modp->catches;
- modp->old_nif = modp->nif;
- modp->code = NULL;
- modp->code_length = 0;
- modp->catches = BEAM_CATCHES_NIL;
- modp->nif = NULL;
-}
-
-
-/* null all references on the export table for the module called with the
- atom index below */
-
static void
-delete_export_references(Eterm module)
+delete_code(Module* modp)
{
+ ErtsCodeIndex code_ix = erts_staging_code_ix();
+ Eterm module = make_atom(modp->module);
int i;
- ASSERT(is_atom(module));
-
- for (i = 0; i < export_list_size(); i++) {
- Export *ep = export_list(i);
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->code[0] == module)) {
- if (ep->address == ep->code+3 &&
- (ep->code[3] == (BeamInstr) em_apply_bif)) {
- continue;
+ if (ep->addressv[code_ix] == ep->code+3) {
+ if (ep->code[3] == (BeamInstr) em_apply_bif) {
+ continue;
+ }
+ else if (ep->code[3] ==
+ (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
+ ASSERT(modp->curr.num_traced_exports > 0);
+ erts_clear_export_break(modp, ep->code+3);
+ }
+ else ASSERT(ep->code[3] == (BeamInstr) em_call_error_handler
+ || !erts_initialized);
}
- ep->address = ep->code+3;
+ ep->addressv[code_ix] = ep->code+3;
ep->code[3] = (BeamInstr) em_call_error_handler;
ep->code[4] = 0;
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = NULL;
}
}
+
+ ASSERT(modp->curr.num_breakpoints == 0);
+ ASSERT(modp->curr.num_traced_exports == 0);
+ modp->old = modp->curr;
+ modp->curr.code = NULL;
+ modp->curr.code_length = 0;
+ modp->curr.catches = BEAM_CATCHES_NIL;
+ modp->curr.nif = NULL;
}
-
+
Eterm
beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module)
@@ -787,11 +1070,10 @@ beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module)
* if not, delete old code; error if old code already exists.
*/
- if (modp->code != NULL && modp->old_code != NULL) {
+ if (modp->curr.code != NULL && modp->old.code != NULL) {
return am_not_purged;
- } else if (modp->old_code == NULL) { /* Make the current version old. */
- delete_code(c_p, c_p_locks, modp);
- delete_export_references(module);
+ } else if (modp->old.code == NULL) { /* Make the current version old. */
+ delete_code(modp);
}
return NIL;
}
@@ -799,7 +1081,21 @@ beam_make_current_old(Process *c_p, ErtsProcLocks c_p_locks, Eterm module)
static int
is_native(BeamInstr* code)
{
- return ((Eterm *)code[MI_FUNCTIONS])[1] != 0;
+ Uint i, num_functions = code[MI_NUM_FUNCTIONS];
+
+ /* Check NativeAdress of first real function in module
+ */
+ for (i=0; i<num_functions; i++) {
+ BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i];
+ Eterm name = (Eterm) func_info[3];
+
+ if (is_atom(name)) {
+ return func_info[1] != 0;
+ }
+ else ASSERT(is_nil(name)); /* ignore BIF stubs */
+ }
+ /* Not a single non-BIF function? */
+ return 0;
}
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index d772bea02f..9b17de34ec 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -44,67 +44,34 @@
#define ReAlloc(P, SIZ) erts_realloc(ERTS_ALC_T_BPD, (P), (SZ))
#define Free(P) erts_free(ERTS_ALC_T_BPD, (P))
-/*
-** Doubly linked ring macros
-*/
-
-#define BpInit(a,i) \
-do { \
- (a)->orig_instr = (i); \
- (a)->next = (a); \
- (a)->prev = (a); \
-} while (0)
-
-#define BpSpliceNext(a,b) \
-do { \
- register BpData *c = (a), *d = (b), *e; \
- e = c->next->prev; \
- c->next->prev = d->next->prev; \
- d->next->prev = e; \
- e = c->next; \
- c->next = d->next; \
- d->next = e; \
-} while (0)
-
-#define BpSplicePrev(a,b) \
-do { \
- register BpData *c = (a), *d = (b), *e; \
- e = c->prev->next; \
- c->prev->next = d->prev->next; \
- d->prev->next = e; \
- e = c->prev; \
- c->prev = d->prev; \
- d->prev = e; \
-} while (0)
-
-#ifdef DEBUG
-# define BpSingleton(a) ((a)->next == (a) && (a)->prev == (a))
+#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
+# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \
+ if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN)
+# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \
+ if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN)
#else
-# define BpSingleton(a) ((a)->next == (a))
+# define ERTS_SMP_REQ_PROC_MAIN_LOCK(P)
+# define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P)
#endif
-#define BpInitAndSpliceNext(a,i,b) \
-do { \
- (a)->orig_instr = (i); \
- (a)->prev = (b); \
- (b)->next->prev = (a); \
- (a)->next = (b)->next; \
- (b)->next = (a); \
-} while (0)
+#define ERTS_BPF_LOCAL_TRACE 0x01
+#define ERTS_BPF_META_TRACE 0x02
+#define ERTS_BPF_COUNT 0x04
+#define ERTS_BPF_COUNT_ACTIVE 0x08
+#define ERTS_BPF_DEBUG 0x10
+#define ERTS_BPF_TIME_TRACE 0x20
+#define ERTS_BPF_TIME_TRACE_ACTIVE 0x40
+#define ERTS_BPF_GLOBAL_TRACE 0x80
-#define BpInitAndSplicePrev(a,i,b) \
-do { \
- (a)->orig_instr = (i); \
- (a)->next = (b); \
- (b)->prev->next = (a); \
- (a)->prev = (b)->prev; \
- (b)->prev = (a); \
-} while (0)
+#define ERTS_BPF_ALL 0xFF
+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) */
-#define BREAK_IS_BIF (1)
-#define BREAK_IS_ERL (0)
-
+erts_smp_atomic32_t erts_active_bp_index;
+erts_smp_atomic32_t erts_staging_bp_index;
/* *************************************************************************
** Local prototypes
@@ -113,26 +80,30 @@ do { \
/*
** Helpers
*/
-
-static int set_break(Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid);
-static int set_module_break(Module *modp, Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid);
-static int set_function_break(Module *modp, BeamInstr *pc, int bif,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid);
-
-static int clear_break(Eterm mfa[3], int specified,
- BeamInstr break_op);
-static int clear_module_break(Module *modp, Eterm mfa[3], int specified,
- BeamInstr break_op);
-static int clear_function_break(Module *modp, BeamInstr *pc, int bif,
- BeamInstr break_op);
-
-static BpData *is_break(BeamInstr *pc, BeamInstr break_op);
-static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op);
+static Eterm do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
+ int local, Binary* ms, Eterm tracer_pid);
+static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
+ enum erts_break_op count_op, Eterm tracer_pid);
+static void set_function_break(BeamInstr *pc,
+ Binary *match_spec,
+ Uint break_flags,
+ enum erts_break_op count_op,
+ Eterm tracer_pid);
+
+static void clear_break(BpFunctions* f, Uint break_flags);
+static int clear_function_break(BeamInstr *pc, Uint break_flags);
+
+static BpDataTime* get_time_break(BeamInstr *pc);
+static GenericBpData* check_break(BeamInstr *pc, Uint break_flags);
+static void bp_time_diff(bp_data_time_item_t *item,
+ process_breakpoint_time_t *pbt,
+ Uint ms, Uint s, Uint us);
+
+static void bp_meta_unref(BpMetaPid* bmp);
+static void bp_count_unref(BpCount* bcp);
+static void bp_time_unref(BpDataTime* bdt);
+static void consolidate_bp_data(Module* modp, BeamInstr* pc, int local);
+static void uninstall_breakpoint(BeamInstr* pc);
/* bp_hash */
#define BP_TIME_ADD(pi0, pi1) \
@@ -152,240 +123,996 @@ static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_da
static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t *sitem);
static void bp_hash_delete(bp_time_hash_t *hash);
-
/* *************************************************************************
** External interfaces
*/
-erts_smp_spinlock_t erts_bp_lock;
-
void
erts_bp_init(void) {
- erts_smp_spinlock_init(&erts_bp_lock, "breakpoints");
+ erts_smp_atomic32_init_nob(&erts_active_bp_index, 0);
+ erts_smp_atomic32_init_nob(&erts_staging_bp_index, 1);
}
-int
-erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec,
- Eterm tracer_pid) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, match_spec,
- (BeamInstr) BeamOp(op_i_trace_breakpoint), 0, tracer_pid);
+
+void
+erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified)
+{
+ ErtsCodeIndex code_ix = erts_active_code_ix();
+ Uint max_funcs = 0;
+ int current;
+ int max_modules = module_code_size(code_ix);
+ int num_modules = 0;
+ Module* modp;
+ Module** module;
+ Uint i;
+
+ module = (Module **) Alloc(max_modules*sizeof(Module *));
+ num_modules = 0;
+ for (current = 0; current < max_modules; current++) {
+ modp = module_code(current, code_ix);
+ if (modp->curr.code) {
+ max_funcs += modp->curr.code[MI_NUM_FUNCTIONS];
+ module[num_modules++] = modp;
+ }
+ }
+
+ f->matching = (BpFunction *) Alloc(max_funcs*sizeof(BpFunction));
+ i = 0;
+ for (current = 0; current < num_modules; current++) {
+ BeamInstr** code_base = (BeamInstr **) module[current]->curr.code;
+ BeamInstr* code;
+ Uint num_functions = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS];
+ Uint fi;
+
+ if (specified > 0) {
+ if (mfa[0] != make_atom(module[current]->module)) {
+ /* Wrong module name */
+ continue;
+ }
+ }
+
+ for (fi = 0; fi < num_functions; fi++) {
+ BeamInstr* pc;
+ int wi;
+
+ code = code_base[MI_FUNCTIONS+fi];
+ ASSERT(code[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ pc = code+5;
+ if (erts_is_native_break(pc)) {
+ continue;
+ }
+ if (is_nil(code[3])) { /* Ignore BIF stub */
+ continue;
+ }
+ for (wi = 0;
+ wi < specified && (Eterm) code[2+wi] == mfa[wi];
+ wi++) {
+ /* Empty loop body */
+ }
+ if (wi == specified) {
+ /* Store match */
+ f->matching[i].pc = pc;
+ f->matching[i].mod = module[current];
+ i++;
+ }
+ }
+ }
+ f->matched = i;
+ Free(module);
}
-int
-erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec,
- Eterm tracer_pid) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, match_spec,
- (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid);
+void
+erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified)
+{
+ ErtsCodeIndex code_ix = erts_active_code_ix();
+ int i;
+ int num_exps = export_list_size(code_ix);
+ int ne;
+
+ f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction));
+ ne = 0;
+ for (i = 0; i < num_exps; i++) {
+ Export* ep = export_list(i, code_ix);
+ BeamInstr* pc;
+ int j;
+
+ for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) {
+ /* Empty loop body */
+ }
+ if (j < specified) {
+ continue;
+ }
+ pc = ep->code+3;
+ if (ep->addressv[code_ix] == pc) {
+ if ((*pc == (BeamInstr) em_apply_bif ||
+ *pc == (BeamInstr) em_call_error_handler)) {
+ continue;
+ }
+ ASSERT(*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ } else if (erts_is_native_break(ep->addressv[code_ix])) {
+ continue;
+ }
+
+ f->matching[ne].pc = pc;
+ f->matching[ne].mod = erts_get_module(ep->code[0], code_ix);
+ ne++;
+
+ }
+ f->matched = ne;
}
-/* set breakpoint data for on exported bif entry */
+void
+erts_bp_free_matched_functions(BpFunctions* f)
+{
+ Free(f->matching);
+}
void
-erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- set_function_break(NULL, pc, BREAK_IS_BIF, match_spec, (BeamInstr) BeamOp(op_i_mtrace_breakpoint), 0, tracer_pid);
+erts_consolidate_bp_data(BpFunctions* f, int local)
+{
+ BpFunction* fs = f->matching;
+ Uint i;
+ Uint n = f->matched;
+
+ 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);
+ }
}
-void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op) {
- set_function_break(NULL, pc, BREAK_IS_BIF, NULL, (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL);
+void
+erts_consolidate_bif_bp_data(void)
+{
+ int i;
+
+ 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);
+ }
}
-void erts_clear_time_trace_bif(BeamInstr *pc) {
- clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_time_breakpoint));
+static void
+consolidate_bp_data(Module* modp, BeamInstr* pc, int local)
+{
+ GenericBp* g = (GenericBp *) pc[-4];
+ GenericBpData* src;
+ GenericBpData* dst;
+ Uint flags;
+
+ if (g == 0) {
+ return;
+ }
+
+ src = &g->data[erts_active_bp_ix()];
+ dst = &g->data[erts_staging_bp_ix()];
+
+ /*
+ * The contents of the staging area may be out of date.
+ * Decrement all reference pointers.
+ */
+
+ flags = dst->flags;
+ if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetUnref(dst->local_ms);
+ }
+ if (flags & ERTS_BPF_META_TRACE) {
+ bp_meta_unref(dst->meta_pid);
+ MatchSetUnref(dst->meta_ms);
+ }
+ if (flags & ERTS_BPF_COUNT) {
+ bp_count_unref(dst->count);
+ }
+ if (flags & ERTS_BPF_TIME_TRACE) {
+ bp_time_unref(dst->time);
+ }
+
+ /*
+ * If all flags are zero, deallocate all breakpoint data.
+ */
+
+ flags = dst->flags = src->flags;
+ if (flags == 0) {
+ if (modp) {
+ if (local) {
+ modp->curr.num_breakpoints--;
+ } else {
+ modp->curr.num_traced_exports--;
+ }
+ ASSERT(modp->curr.num_breakpoints >= 0);
+ ASSERT(modp->curr.num_traced_exports >= 0);
+ ASSERT(*pc != (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ }
+ pc[-4] = 0;
+ Free(g);
+ return;
+ }
+
+ /*
+ * Copy the active data to the staging area (making it ready
+ * for the next time it will be used).
+ */
+
+ if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ dst->local_ms = src->local_ms;
+ MatchSetRef(dst->local_ms);
+ }
+ if (flags & ERTS_BPF_META_TRACE) {
+ dst->meta_pid = src->meta_pid;
+ erts_refc_inc(&dst->meta_pid->refc, 1);
+ dst->meta_ms = src->meta_ms;
+ MatchSetRef(dst->meta_ms);
+ }
+ if (flags & ERTS_BPF_COUNT) {
+ dst->count = src->count;
+ erts_refc_inc(&dst->count->refc, 1);
+ }
+ if (flags & ERTS_BPF_TIME_TRACE) {
+ dst->time = src->time;
+ erts_refc_inc(&dst->time->refc, 1);
+ ASSERT(dst->time->hash);
+ }
}
-int
-erts_set_debug_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, NULL,
- (BeamInstr) BeamOp(op_i_debug_breakpoint), 0, NIL);
+void
+erts_commit_staged_bp(void)
+{
+ ErtsBpIndex staging = erts_staging_bp_ix();
+ ErtsBpIndex active = erts_active_bp_ix();
+
+ erts_smp_atomic32_set_nob(&erts_active_bp_index, staging);
+ erts_smp_atomic32_set_nob(&erts_staging_bp_index, active);
}
-int
-erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op count_op) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, NULL,
- (BeamInstr) BeamOp(op_i_count_breakpoint), count_op, NIL);
+void
+erts_install_breakpoints(BpFunctions* f)
+{
+ Uint i;
+ Uint n = f->matched;
+ BeamInstr br = (BeamInstr) BeamOp(op_i_generic_breakpoint);
+
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ GenericBp* g = (GenericBp *) pc[-4];
+ if (*pc != br && g) {
+ Module* modp = f->matching[i].mod;
+
+ /*
+ * The breakpoint must be disabled in the active data
+ * (it will enabled later by switching bp indices),
+ * and enabled in the staging data.
+ */
+ ASSERT(g->data[erts_active_bp_ix()].flags == 0);
+ ASSERT(g->data[erts_staging_bp_ix()].flags != 0);
+
+ /*
+ * The following write is not protected by any lock. We
+ * assume that the hardware guarantees that a write of an
+ * aligned word-size (or half-word) writes is atomic
+ * (i.e. that other processes executing this code will not
+ * see a half pointer).
+ */
+ *pc = br;
+ modp->curr.num_breakpoints++;
+ }
+ }
}
-int
-erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op count_op) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return set_break(mfa, specified, NULL,
- (BeamInstr) BeamOp(op_i_time_breakpoint), count_op, NIL);
+void
+erts_uninstall_breakpoints(BpFunctions* f)
+{
+ Uint i;
+ Uint n = f->matched;
+
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ uninstall_breakpoint(pc);
+ }
}
-int
-erts_clear_trace_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_trace_breakpoint));
+static void
+uninstall_breakpoint(BeamInstr* pc)
+{
+ if (*pc == (BeamInstr) BeamOp(op_i_generic_breakpoint)) {
+ GenericBp* g = (GenericBp *) pc[-4];
+ if (g->data[erts_active_bp_ix()].flags == 0) {
+ /*
+ * The following write is not protected by any lock. We
+ * assume that the hardware guarantees that a write of an
+ * aligned word-size (or half-word) writes is atomic
+ * (i.e. that other processes executing this code will not
+ * see a half pointer).
+ */
+ *pc = g->orig_instr;
+ }
+ }
}
-int
-erts_clear_mtrace_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
+void
+erts_set_trace_break(BpFunctions* f, Binary *match_spec)
+{
+ set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, am_true);
}
void
-erts_clear_mtrace_bif(BeamInstr *pc) {
- clear_function_break(NULL, pc, BREAK_IS_BIF, (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
+erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, Eterm tracer_pid)
+{
+ set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid);
}
-int
-erts_clear_debug_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_debug_breakpoint));
+void
+erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local)
+{
+ Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
+
+ set_function_break(pc, match_spec, flags, 0, NIL);
}
-int
-erts_clear_count_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_count_breakpoint));
+void
+erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec, Eterm tracer_pid)
+{
+ set_function_break(pc, match_spec, ERTS_BPF_META_TRACE, 0, tracer_pid);
}
-int
-erts_clear_time_break(Eterm mfa[3], int specified) {
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified,
- (BeamInstr) BeamOp(op_i_time_breakpoint));
+void
+erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op count_op)
+{
+ set_function_break(pc, NULL,
+ ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
+ count_op, NIL);
}
-int
-erts_clear_break(Eterm mfa[3], int specified) {
+void
+erts_clear_time_trace_bif(BeamInstr *pc) {
+ clear_function_break(pc, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
+}
+
+void
+erts_set_debug_break(BpFunctions* f) {
+ set_break(f, NULL, ERTS_BPF_DEBUG, 0, NIL);
+}
+
+void
+erts_set_count_break(BpFunctions* f, enum erts_break_op count_op)
+{
+ set_break(f, 0, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE,
+ count_op, NIL);
+}
+
+void
+erts_set_time_break(BpFunctions* f, enum erts_break_op count_op)
+{
+ set_break(f, 0, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
+ count_op, NIL);
+}
+
+void
+erts_clear_trace_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_LOCAL_TRACE);
+}
+
+void
+erts_clear_call_trace_bif(BeamInstr *pc, int local)
+{
+ GenericBp* g = (GenericBp *) pc[-4];
+
+ if (g) {
+ Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
+ if (g->data[erts_staging_bp_ix()].flags & flags) {
+ clear_function_break(pc, flags);
+ }
+ }
+}
+
+void
+erts_clear_mtrace_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_META_TRACE);
+}
+
+void
+erts_clear_mtrace_bif(BeamInstr *pc)
+{
+ clear_function_break(pc, ERTS_BPF_META_TRACE);
+}
+
+void
+erts_clear_debug_break(BpFunctions* f)
+{
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- return clear_break(mfa, specified, 0);
+ clear_break(f, ERTS_BPF_DEBUG);
+}
+
+void
+erts_clear_count_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE);
+}
+
+void
+erts_clear_time_break(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
+}
+
+void
+erts_clear_all_breaks(BpFunctions* f)
+{
+ clear_break(f, ERTS_BPF_ALL);
}
int
erts_clear_module_break(Module *modp) {
+ BeamInstr** code_base;
+ Uint n;
+ Uint i;
+
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
ASSERT(modp);
- return clear_module_break(modp, NULL, 0, 0);
+ code_base = (BeamInstr **) modp->curr.code;
+ if (code_base == NULL) {
+ return 0;
+ }
+ n = (Uint)(UWord) code_base[MI_NUM_FUNCTIONS];
+ for (i = 0; i < n; ++i) {
+ BeamInstr* pc;
+
+ pc = code_base[MI_FUNCTIONS+i] + 5;
+ if (erts_is_native_break(pc)) {
+ continue;
+ }
+ clear_function_break(pc, ERTS_BPF_ALL);
+ }
+
+ erts_commit_staged_bp();
+
+ for (i = 0; i < n; ++i) {
+ BeamInstr* pc;
+
+ pc = code_base[MI_FUNCTIONS+i] + 5;
+ if (erts_is_native_break(pc)) {
+ continue;
+ }
+ uninstall_breakpoint(pc);
+ consolidate_bp_data(modp, pc, 1);
+ ASSERT(pc[-4] == 0);
+ }
+ return n;
}
-int
-erts_clear_function_break(Module *modp, BeamInstr *pc) {
+void
+erts_clear_export_break(Module* modp, BeamInstr* pc)
+{
ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
- ASSERT(modp);
- return clear_function_break(modp, pc, BREAK_IS_ERL, 0);
+
+ clear_function_break(pc, ERTS_BPF_ALL);
+ erts_commit_staged_bp();
+ *pc = (BeamInstr) 0;
+ consolidate_bp_data(modp, pc, 0);
+ ASSERT(pc[-4] == 0);
}
+BeamInstr
+erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg)
+{
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint bp_flags;
+ ErtsBpIndex ix = erts_active_bp_ix();
+
+ g = (GenericBp *) I[-4];
+ bp = &g->data[ix];
+ bp_flags = bp->flags;
+ ASSERT((bp_flags & ~ERTS_BPF_ALL) == 0);
+ if (bp_flags & (ERTS_BPF_LOCAL_TRACE|
+ ERTS_BPF_GLOBAL_TRACE|
+ ERTS_BPF_TIME_TRACE_ACTIVE) &&
+ !IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
+ bp_flags &= ~(ERTS_BPF_LOCAL_TRACE|
+ ERTS_BPF_GLOBAL_TRACE|
+ ERTS_BPF_TIME_TRACE|
+ ERTS_BPF_TIME_TRACE_ACTIVE);
+ if (bp_flags == 0) { /* Quick exit */
+ return g->orig_instr;
+ }
+ }
+
+ if (bp_flags & ERTS_BPF_LOCAL_TRACE) {
+ ASSERT((bp_flags & ERTS_BPF_GLOBAL_TRACE) == 0);
+ (void) do_call_trace(c_p, I, reg, 1, bp->local_ms, am_true);
+ } else if (bp_flags & ERTS_BPF_GLOBAL_TRACE) {
+ (void) do_call_trace(c_p, I, reg, 0, bp->local_ms, am_true);
+ }
+
+ if (bp_flags & ERTS_BPF_META_TRACE) {
+ Eterm old_pid;
+ Eterm new_pid;
+
+ old_pid = (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
+ new_pid = do_call_trace(c_p, I, reg, 1, bp->meta_ms, old_pid);
+ if (new_pid != old_pid) {
+ erts_smp_atomic_set_nob(&bp->meta_pid->pid, new_pid);
+ }
+ }
+
+ if (bp_flags & ERTS_BPF_COUNT_ACTIVE) {
+ erts_smp_atomic_inc_nob(&bp->count->acount);
+ }
+
+ if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
+ Eterm w;
+ erts_trace_time_call(c_p, I, bp->time);
+ w = (BeamInstr) *c_p->cp;
+ if (! (w == (BeamInstr) BeamOp(op_i_return_time_trace) ||
+ w == (BeamInstr) BeamOp(op_return_trace) ||
+ w == (BeamInstr) BeamOp(op_i_return_to_trace)) ) {
+ Eterm* E = c_p->stop;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ if (E - 2 < c_p->htop) {
+ (void) erts_garbage_collect(c_p, 2, reg, I[-1]);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ }
+ E = c_p->stop;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+
+ E -= 2;
+ E[0] = make_cp(I);
+ E[1] = make_cp(c_p->cp); /* original return address */
+ c_p->cp = beam_return_time_trace;
+ c_p->stop = E;
+ }
+ }
+
+ if (bp_flags & ERTS_BPF_DEBUG) {
+ return (BeamInstr) BeamOp(op_i_debug_breakpoint);
+ } else {
+ return g->orig_instr;
+ }
+}
/*
- * SMP NOTE: Process p may have become exiting on return!
+ * Entry point called by the trace wrap functions in erl_bif_wrap.c
+ *
+ * The trace wrap functions are themselves called through the export
+ * entries instead of the original BIF functions.
*/
-BeamInstr
-erts_trace_break(Process *p, BeamInstr *pc, Eterm *args,
- Uint32 *ret_flags, Eterm *tracer_pid) {
- Eterm tpid1, tpid2;
- BpData **bds = (BpData **) (pc)[-4];
- BpDataTrace *bdt = NULL;
+Eterm
+erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
+{
+ Eterm result;
+ Eterm (*func)(Process*, Eterm*, BeamInstr*);
+ Export* ep = bif_export[bif_index];
+ Uint32 flags = 0, flags_meta = 0;
+ Eterm meta_tracer_pid = NIL;
+ int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif
+ * is actually in the
+ * export entry */
+ BeamInstr *cp = p->cp;
+ GenericBp* g;
+ GenericBpData* bp = NULL;
+ Uint bp_flags = 0;
+
+ ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+
+ g = (GenericBp *) ep->fake_op_func_info_for_hipe[1];
+ if (g) {
+ bp = &g->data[erts_active_bp_ix()];
+ bp_flags = bp->flags;
+ }
- ASSERT(bds);
- ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- bdt = (BpDataTrace *) bds[bp_sched2ix_proc(p)];
- ASSERT(bdt);
- bdt = (BpDataTrace *) bdt->next;
- ASSERT(bdt);
- ASSERT(ret_flags);
- ASSERT(tracer_pid);
-
- ErtsSmpBPLock(bdt);
- tpid1 = tpid2 = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
-
- *ret_flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args,
- 1, &tpid2);
- *tracer_pid = tpid2;
- if (tpid1 != tpid2) {
- ErtsSmpBPLock(bdt);
- bdt->tracer_pid = tpid2;
- ErtsSmpBPUnlock(bdt);
- }
- bds[bp_sched2ix_proc(p)] = (BpData *) bdt;
- return bdt->orig_instr;
+ /*
+ * Make continuation pointer OK, it is not during direct BIF calls,
+ * but it is correct during apply of bif.
+ */
+ if (!applying) {
+ p->cp = I;
+ }
+ if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) &&
+ 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, &ERTS_TRACER_PROC(p));
+ }
+ if (bp_flags & ERTS_BPF_META_TRACE) {
+ Eterm tpid1, tpid2;
+
+ tpid1 = tpid2 =
+ (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
+ flags_meta = erts_call_trace(p, ep->code, bp->meta_ms, args,
+ 0, &tpid2);
+ meta_tracer_pid = tpid2;
+ if (tpid1 != tpid2) {
+ erts_smp_atomic_set_nob(&bp->meta_pid->pid, tpid2);
+ }
+ }
+ if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
+ IS_TRACED_FL(p, F_TRACE_CALLS)) {
+ BeamInstr *pc = (BeamInstr *)ep->code+3;
+ erts_trace_time_call(p, pc, bp->time);
+ }
+
+ /* Restore original continuation pointer (if changed). */
+ p->cp = cp;
+
+ func = bif_table[bif_index].f;
+
+ result = func(p, args, I);
+
+ if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
+ BeamInstr i_return_trace = beam_return_trace[0];
+ BeamInstr i_return_to_trace = beam_return_to_trace[0];
+ BeamInstr i_return_time_trace = beam_return_time_trace[0];
+ Eterm *cpp;
+ /* Maybe advance cp to skip trace stack frames */
+ for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
+ if (*cp == i_return_trace) {
+ /* Skip stack frame variables */
+ while (is_not_CP(*cpp)) cpp++;
+ cpp += 2; /* Skip return_trace parameters */
+ } else if (*cp == i_return_time_trace) {
+ /* Skip stack frame variables */
+ while (is_not_CP(*cpp)) cpp++;
+ cpp += 1; /* Skip return_time_trace parameters */
+ } else if (*cp == i_return_to_trace) {
+ /* A return_to trace message is going to be generated
+ * by normal means, so we do not have to.
+ */
+ cp = NULL;
+ break;
+ } else break;
+ }
+ }
+
+ /* Try to get these in the order
+ * they usually appear in normal code... */
+ if (is_non_value(result)) {
+ Uint reason = p->freason;
+ if (reason != TRAP) {
+ Eterm class;
+ Eterm value = p->fvalue;
+ DeclareTmpHeapNoproc(nocatch,3);
+ UseTmpHeapNoproc(3);
+ /* Expand error value like in handle_error() */
+ if (reason & EXF_ARGLIST) {
+ Eterm *tp;
+ ASSERT(is_tuple(value));
+ tp = tuple_val(value);
+ value = tp[1];
+ }
+ if ((reason & EXF_THROWN) && (p->catches <= 0)) {
+ value = TUPLE2(nocatch, am_nocatch, value);
+ reason = EXC_ERROR;
+ }
+ /* Note: expand_error_value() could theoretically
+ * allocate on the heap, but not for any error
+ * returned by a BIF, and it would do no harm,
+ * just be annoying.
+ */
+ value = expand_error_value(p, reason, value);
+ class = exception_tag[GET_EXC_CLASS(reason)];
+
+ if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
+ erts_trace_exception(p, ep->code, class, value,
+ &meta_tracer_pid);
+ }
+ if (flags & MATCH_SET_EXCEPTION_TRACE) {
+ erts_trace_exception(p, ep->code, class, value,
+ &ERTS_TRACER_PROC(p));
+ }
+ if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
+ /* can only happen if(local)*/
+ Eterm *ptr = p->stop;
+ ASSERT(is_CP(*ptr));
+ ASSERT(ptr <= STACK_START(p));
+ /* Search the nearest stack frame for a catch */
+ while (++ptr < STACK_START(p)) {
+ if (is_CP(*ptr)) break;
+ if (is_catch(*ptr)) {
+ if (applying) {
+ /* Apply of BIF, cp is in calling function */
+ if (cp) erts_trace_return_to(p, cp);
+ } else {
+ /* Direct bif call, I points into
+ * calling function */
+ erts_trace_return_to(p, I);
+ }
+ }
+ }
+ }
+ UnUseTmpHeapNoproc(3);
+ if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
+ erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ }
+ } else {
+ if (flags_meta & MATCH_SET_RX_TRACE) {
+ erts_trace_return(p, ep->code, result, &meta_tracer_pid);
+ }
+ /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
+ if (flags & MATCH_SET_RX_TRACE) {
+ erts_trace_return(p, ep->code, result, &ERTS_TRACER_PROC(p));
+ }
+ if (flags & MATCH_SET_RETURN_TO_TRACE) {
+ /* can only happen if(local)*/
+ if (applying) {
+ /* Apply of BIF, cp is in calling function */
+ if (cp) erts_trace_return_to(p, cp);
+ } else {
+ /* Direct bif call, I points into calling function */
+ erts_trace_return_to(p, I);
+ }
+ }
+ }
+ ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+ return result;
}
+static Eterm
+do_call_trace(Process* c_p, BeamInstr* I, Eterm* reg,
+ int local, Binary* ms, Eterm tracer_pid)
+{
+ Eterm* cpp;
+ int return_to_trace = 0;
+ BeamInstr w;
+ BeamInstr *cp_save;
+ Uint32 flags;
+ Uint need = 0;
+ Eterm* E = c_p->stop;
+
+ w = *c_p->cp;
+ if (w == (BeamInstr) BeamOp(op_return_trace)) {
+ cpp = &E[2];
+ } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) {
+ return_to_trace = 1;
+ cpp = &E[0];
+ } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) {
+ cpp = &E[0];
+ } else {
+ cpp = NULL;
+ }
+ if (cpp) {
+ for (;;) {
+ BeamInstr w = *cp_val(*cpp);
+ if (w == (BeamInstr) BeamOp(op_return_trace)) {
+ cpp += 3;
+ } else if (w == (BeamInstr) BeamOp(op_i_return_to_trace)) {
+ return_to_trace = 1;
+ cpp += 1;
+ } else if (w == (BeamInstr) BeamOp(op_i_return_time_trace)) {
+ cpp += 2;
+ } else {
+ break;
+ }
+ }
+ cp_save = c_p->cp;
+ c_p->cp = (BeamInstr *) cp_val(*cpp);
+ ASSERT(is_CP(*cpp));
+ }
+ ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
+ flags = erts_call_trace(c_p, I-3, ms, reg, local, &tracer_pid);
+ ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
+ if (cpp) {
+ c_p->cp = cp_save;
+ }
+
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
+ if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
+ need += 1;
+ }
+ if (flags & MATCH_SET_RX_TRACE) {
+ need += 3;
+ }
+ if (need) {
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ if (E - need < c_p->htop) {
+ (void) erts_garbage_collect(c_p, need, reg, I[-1]);
+ ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ E = c_p->stop;
+ }
+ }
+ if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) {
+ E -= 1;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ E[0] = make_cp(c_p->cp);
+ c_p->cp = beam_return_to_trace;
+ }
+ if (flags & MATCH_SET_RX_TRACE) {
+ E -= 3;
+ ASSERT(c_p->htop <= E && E <= c_p->hend);
+ ASSERT(is_CP((Eterm) (UWord) (I - 3)));
+ ASSERT(am_true == tracer_pid ||
+ is_internal_pid(tracer_pid) || is_internal_port(tracer_pid));
+ E[2] = make_cp(c_p->cp);
+ E[1] = tracer_pid;
+ E[0] = make_cp(I - 3); /* We ARE at the beginning of an
+ instruction,
+ the funcinfo is above i. */
+ 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);
+ ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
+ c_p->stop = E;
+ return tracer_pid;
+}
+void
+erts_trace_time_call(Process* c_p, BeamInstr* I, BpDataTime* bdt)
+{
+ Uint ms,s,us;
+ process_breakpoint_time_t *pbt = NULL;
+ bp_data_time_item_t sitem, *item = NULL;
+ bp_time_hash_t *h = NULL;
+ BpDataTime *pbdt = NULL;
-/*
- * SMP NOTE: Process p may have become exiting on return!
- */
-Uint32
-erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args, int local,
- Eterm *tracer_pid) {
- BpData **bds = (BpData **) (pc)[-4];
- BpDataTrace *bdt = NULL;
+ ASSERT(c_p);
+ ASSERT(erts_smp_atomic32_read_acqb(&c_p->state) & ERTS_PSFLG_RUNNING);
+ /* get previous timestamp and breakpoint
+ * from the process psd */
- ASSERT(tracer_pid);
- if (bds) {
- Eterm tpid1, tpid2;
- Uint32 flags;
- bdt = (BpDataTrace *)bds[bp_sched2ix_proc(p)];
+ pbt = ERTS_PROC_GET_CALL_TIME(c_p);
+ get_sys_now(&ms, &s, &us);
- ErtsSmpBPLock(bdt);
- tpid1 = tpid2 = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
+ /* get pbt
+ * timestamp = t0
+ * lookup bdt from code
+ * set ts0 to pbt
+ * add call count here?
+ */
+ if (pbt == 0) {
+ /* First call of process to instrumented function */
+ pbt = Alloc(sizeof(process_breakpoint_time_t));
+ (void) ERTS_PROC_SET_CALL_TIME(c_p, ERTS_PROC_LOCK_MAIN, pbt);
+ } else {
+ ASSERT(pbt->pc);
+ /* add time to previous code */
+ bp_time_diff(&sitem, pbt, ms, s, us);
+ sitem.pid = c_p->common.id;
+ sitem.count = 0;
- flags = erts_call_trace(p, pc-3/*mfa*/, bdt->match_spec, args,
- local, &tpid2);
- *tracer_pid = tpid2;
- if (tpid1 != tpid2) {
- ErtsSmpBPLock(bdt);
- bdt->tracer_pid = tpid2;
- ErtsSmpBPUnlock(bdt);
+ /* previous breakpoint */
+ pbdt = get_time_break(pbt->pc);
+
+ /* if null then the breakpoint was removed */
+ if (pbdt) {
+ h = &(pbdt->hash[bp_sched2ix_proc(c_p)]);
+
+ ASSERT(h);
+ ASSERT(h->item);
+
+ item = bp_hash_get(h, &sitem);
+ if (!item) {
+ item = bp_hash_put(h, &sitem);
+ } else {
+ BP_TIME_ADD(item, &sitem);
+ }
}
- return flags;
}
- *tracer_pid = NIL;
- return 0;
+
+ /* Add count to this code */
+ sitem.pid = c_p->common.id;
+ sitem.count = 1;
+ sitem.s_time = 0;
+ sitem.us_time = 0;
+
+ /* this breakpoint */
+ ASSERT(bdt);
+ h = &(bdt->hash[bp_sched2ix_proc(c_p)]);
+
+ ASSERT(h);
+ ASSERT(h->item);
+
+ item = bp_hash_get(h, &sitem);
+ if (!item) {
+ item = bp_hash_put(h, &sitem);
+ } else {
+ BP_TIME_ADD(item, &sitem);
+ }
+
+ pbt->pc = I;
+ pbt->ms = ms;
+ pbt->s = s;
+ pbt->us = us;
}
+void
+erts_trace_time_return(Process *p, BeamInstr *pc)
+{
+ Uint ms,s,us;
+ process_breakpoint_time_t *pbt = NULL;
+ bp_data_time_item_t sitem, *item = NULL;
+ bp_time_hash_t *h = NULL;
+ BpDataTime *pbdt = NULL;
+
+ ASSERT(p);
+ ASSERT(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_RUNNING);
+ /* get previous timestamp and breakpoint
+ * from the process psd */
+
+ pbt = ERTS_PROC_GET_CALL_TIME(p);
+ get_sys_now(&ms,&s,&us);
+
+ /* get pbt
+ * lookup bdt from code
+ * timestamp = t1
+ * get ts0 from pbt
+ * get item from bdt->hash[bp_hash(p->id)]
+ * ack diff (t1, t0) to item
+ */
+
+ if (pbt) {
+ /* might have been removed due to
+ * trace_pattern(false)
+ */
+ ASSERT(pbt->pc);
+
+ bp_time_diff(&sitem, pbt, ms, s, us);
+ sitem.pid = p->common.id;
+ sitem.count = 0;
+
+ /* previous breakpoint */
+ pbdt = get_time_break(pbt->pc);
+
+ /* beware, the trace_pattern might have been removed */
+ if (pbdt) {
+ h = &(pbdt->hash[bp_sched2ix_proc(p)]);
+
+ ASSERT(h);
+ ASSERT(h->item);
+
+ item = bp_hash_get(h, &sitem);
+ if (!item) {
+ item = bp_hash_put(h, &sitem);
+ } else {
+ BP_TIME_ADD(item, &sitem);
+ }
+ }
+
+ pbt->pc = pc;
+ pbt->ms = ms;
+ pbt->s = s;
+ pbt->us = us;
+ }
+}
int
-erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) {
- BpDataTrace *bdt =
- (BpDataTrace *) is_break(pc, (BeamInstr) BeamOp(op_i_trace_breakpoint));
-
- if (bdt) {
+erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local)
+{
+ Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
+ GenericBpData* bp = check_break(pc, flags);
+
+ if (bp) {
if (match_spec_ret) {
- *match_spec_ret = bdt->match_spec;
+ *match_spec_ret = bp->local_ms;
}
- if (tracer_pid_ret) {
- ErtsSmpBPLock(bdt);
- *tracer_pid_ret = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
- }
- return !0;
+ return 1;
}
return 0;
}
int
-erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret, Eterm *tracer_pid_ret) {
- BpDataTrace *bdt =
- (BpDataTrace *) is_break(pc, (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
+erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
+ Eterm *tracer_pid_ret)
+{
+ GenericBpData* bp = check_break(pc, ERTS_BPF_META_TRACE);
- if (bdt) {
+ if (bp) {
if (match_spec_ret) {
- *match_spec_ret = bdt->match_spec;
+ *match_spec_ret = bp->meta_ms;
}
if (tracer_pid_ret) {
- ErtsSmpBPLock(bdt);
- *tracer_pid_ret = bdt->tracer_pid;
- ErtsSmpBPUnlock(bdt);
+ *tracer_pid_ret =
+ (Eterm) erts_smp_atomic_read_nob(&bp->meta_pid->pid);
}
- return !0;
+ return 1;
}
return 0;
}
@@ -402,15 +1129,15 @@ erts_is_native_break(BeamInstr *pc) {
}
int
-erts_is_count_break(BeamInstr *pc, Sint *count_ret) {
- BpDataCount *bdc =
- (BpDataCount *) is_break(pc, (BeamInstr) BeamOp(op_i_count_breakpoint));
+erts_is_count_break(BeamInstr *pc, Uint *count_ret)
+{
+ GenericBpData* bp = check_break(pc, ERTS_BPF_COUNT);
- if (bdc) {
+ if (bp) {
if (count_ret) {
- *count_ret = (Sint) erts_smp_atomic_read_nob(&bdc->acount);
+ *count_ret = (Uint) erts_smp_atomic_read_nob(&bp->count->acount);
}
- return !0;
+ return 1;
}
return 0;
}
@@ -421,7 +1148,7 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) {
Uint size;
Eterm *hp, t;
bp_data_time_item_t *item = NULL;
- BpDataTime *bdt = (BpDataTime *) is_break(pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
+ BpDataTime *bdt = get_time_break(pc);
if (bdt) {
if (retval) {
@@ -464,7 +1191,7 @@ int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *retval) {
}
bp_hash_delete(&hash);
}
- return !0;
+ return 1;
}
return 0;
@@ -478,15 +1205,16 @@ erts_find_local_func(Eterm mfa[3]) {
BeamInstr* code_ptr;
Uint i,n;
- if ((modp = erts_get_module(mfa[0])) == NULL)
+ if ((modp = erts_get_module(mfa[0], erts_active_code_ix())) == NULL)
return NULL;
- if ((code_base = (BeamInstr **) modp->code) == NULL)
+ if ((code_base = (BeamInstr **) modp->curr.code) == NULL)
return NULL;
n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
for (i = 0; i < n; ++i) {
code_ptr = code_base[MI_FUNCTIONS+i];
ASSERT(((BeamInstr) BeamOp(op_i_func_info_IaaI)) == code_ptr[0]);
- ASSERT(mfa[0] == ((Eterm) code_ptr[2]));
+ ASSERT(mfa[0] == ((Eterm) code_ptr[2]) ||
+ is_nil((Eterm) code_ptr[2]));
if (mfa[1] == ((Eterm) code_ptr[3]) &&
((BeamInstr) mfa[2]) == code_ptr[4]) {
return code_ptr + 5;
@@ -654,11 +1382,11 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
* the previous breakpoint.
*/
- pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
+ pbdt = get_time_break(pbt->pc);
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)]);
@@ -692,669 +1420,259 @@ void erts_schedule_time_break(Process *p, Uint schedule) {
} /* pbt */
}
-/* call_time breakpoint
- * Accumulated times are added to the previous bp,
- * not the current one. The current one is saved
- * for future reference.
- * The previous breakpoint is stored in the process it self, the psd.
- * We do not need to store in a stack frame.
- * There is no need for locking, each thread has its own
- * area in each bp to save data.
- * Since we need to diffrentiate between processes for each bp,
- * every bp has a hash (per thread) to process-bp statistics.
- * - egil
- */
-
-void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type) {
- Uint ms,s,us;
- process_breakpoint_time_t *pbt = NULL;
- bp_data_time_item_t sitem, *item = NULL;
- bp_time_hash_t *h = NULL;
- BpDataTime *pbdt = NULL;
-
- ASSERT(p);
- ASSERT(p->status == P_RUNNING);
-
- /* get previous timestamp and breakpoint
- * from the process psd */
-
- pbt = ERTS_PROC_GET_CALL_TIME(p);
- get_sys_now(&ms,&s,&us);
-
- switch(type) {
- /* get pbt
- * timestamp = t0
- * lookup bdt from code
- * set ts0 to pbt
- * add call count here?
- */
- case ERTS_BP_CALL_TIME_CALL:
- case ERTS_BP_CALL_TIME_TAIL_CALL:
-
- if (pbt) {
- ASSERT(pbt->pc);
- /* add time to previous code */
- bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = p->id;
- sitem.count = 0;
-
- /* previous breakpoint */
- pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
-
- /* if null then the breakpoint was removed */
- if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
-
- ASSERT(h);
- ASSERT(h->item);
-
- item = bp_hash_get(h, &sitem);
- if (!item) {
- item = bp_hash_put(h, &sitem);
- } else {
- BP_TIME_ADD(item, &sitem);
- }
- }
-
- } else {
- /* first call of process to instrumented function */
- pbt = Alloc(sizeof(process_breakpoint_time_t));
- (void *) ERTS_PROC_SET_CALL_TIME(p, ERTS_PROC_LOCK_MAIN, pbt);
- }
- /* add count to this code */
- sitem.pid = p->id;
- sitem.count = 1;
- sitem.s_time = 0;
- sitem.us_time = 0;
-
- /* this breakpoint */
- ASSERT(bdt);
- h = &(bdt->hash[bp_sched2ix_proc(p)]);
-
- ASSERT(h);
- ASSERT(h->item);
-
- item = bp_hash_get(h, &sitem);
- if (!item) {
- item = bp_hash_put(h, &sitem);
- } else {
- BP_TIME_ADD(item, &sitem);
- }
-
- pbt->pc = pc;
- pbt->ms = ms;
- pbt->s = s;
- pbt->us = us;
- break;
-
- case ERTS_BP_CALL_TIME_RETURN:
- /* get pbt
- * lookup bdt from code
- * timestamp = t1
- * get ts0 from pbt
- * get item from bdt->hash[bp_hash(p->id)]
- * ack diff (t1, t0) to item
- */
-
- if(pbt) {
- /* might have been removed due to
- * trace_pattern(false)
- */
- ASSERT(pbt->pc);
-
- bp_time_diff(&sitem, pbt, ms, s, us);
- sitem.pid = p->id;
- sitem.count = 0;
-
- /* previous breakpoint */
- pbdt = (BpDataTime *) get_break(p, pbt->pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
-
- /* beware, the trace_pattern might have been removed */
- if (pbdt) {
- h = &(pbdt->hash[bp_sched2ix_proc(p)]);
-
- ASSERT(h);
- ASSERT(h->item);
-
- item = bp_hash_get(h, &sitem);
- if (!item) {
- item = bp_hash_put(h, &sitem);
- } else {
- BP_TIME_ADD(item, &sitem);
- }
- }
-
- pbt->pc = pc;
- pbt->ms = ms;
- pbt->s = s;
- pbt->us = us;
- }
- break;
- default :
- ASSERT(0);
- /* will never happen */
- break;
- }
-}
-
-
/* *************************************************************************
** Local helpers
*/
-static int set_break(Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid)
+static void
+set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
+ enum erts_break_op count_op, Eterm tracer_pid)
{
- Module *modp;
- int num_processed = 0;
- if (!specified) {
- /* Find and process all modules in the system... */
- int current;
- int last = module_code_size();
- for (current = 0; current < last; current++) {
- modp = module_code(current);
- ASSERT(modp != NULL);
- num_processed +=
- set_module_break(modp, mfa, specified,
- match_spec, break_op, count_op,
- tracer_pid);
- }
- } else {
- /* Process a single module */
- if ((modp = erts_get_module(mfa[0])) != NULL) {
- num_processed +=
- set_module_break(modp, mfa, specified,
- match_spec, break_op, count_op,
- tracer_pid);
- }
- }
- return num_processed;
-}
-
-static int set_module_break(Module *modp, Eterm mfa[3], int specified,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid) {
- BeamInstr** code_base;
- BeamInstr* code_ptr;
- int num_processed = 0;
- Uint i,n;
+ Uint i;
+ Uint n;
- ASSERT(break_op);
- ASSERT(modp);
- code_base = (BeamInstr **) modp->code;
- if (code_base == NULL) {
- return 0;
- }
- n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
- for (i = 0; i < n; ++i) {
- code_ptr = code_base[MI_FUNCTIONS+i];
- ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) &&
- (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) {
- BeamInstr *pc = code_ptr+5;
-
- num_processed +=
- set_function_break(modp, pc, BREAK_IS_ERL, match_spec,
- break_op, count_op, tracer_pid);
- }
+ n = f->matched;
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ set_function_break(pc, match_spec, break_flags,
+ count_op, tracer_pid);
}
- return num_processed;
}
-static int set_function_break(Module *modp, BeamInstr *pc, int bif,
- Binary *match_spec, BeamInstr break_op,
- enum erts_break_op count_op, Eterm tracer_pid) {
-
- BeamInstr **code_base = NULL;
- BpData *bd, **r, ***rs;
- size_t size;
- Uint ix = 0;
-
- if (bif == BREAK_IS_ERL) {
- code_base = (BeamInstr **)modp->code;
- ASSERT(code_base);
- ASSERT(code_base <= (BeamInstr **)pc);
- ASSERT((BeamInstr **)pc < code_base + (modp->code_length/sizeof(BeamInstr *)));
- } else {
- ASSERT(*pc == (BeamInstr) em_apply_bif);
- ASSERT(modp == NULL);
+static void
+set_function_break(BeamInstr *pc, Binary *match_spec, Uint break_flags,
+ enum erts_break_op count_op, Eterm tracer_pid)
+{
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint common;
+ ErtsBpIndex ix = erts_staging_bp_ix();
+
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
+ g = (GenericBp *) pc[-4];
+ if (g == 0) {
+ int i;
+ if (count_op == erts_break_reset || count_op == erts_break_stop) {
+ /* Do not insert a new breakpoint */
+ return;
+ }
+ g = Alloc(sizeof(GenericBp));
+ g->orig_instr = *pc;
+ for (i = 0; i < ERTS_NUM_BP_IX; i++) {
+ g->data[i].flags = 0;
+ }
+ pc[-4] = (BeamInstr) g;
}
+ bp = &g->data[ix];
/*
- * Currently no trace support for native code.
+ * If we are changing an existing breakpoint, clean up old data.
*/
- if (erts_is_native_break(pc)) {
- return 0;
- }
- /* Do not allow two breakpoints of the same kind */
- if ( (bd = is_break(pc, break_op))) {
- if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint)
- || break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
-
- BpDataTrace *bdt = (BpDataTrace *) bd;
- Binary *old_match_spec;
-
- /* Update match spec and tracer */
- MatchSetRef(match_spec);
- ErtsSmpBPLock(bdt);
- old_match_spec = bdt->match_spec;
- bdt->match_spec = match_spec;
- bdt->tracer_pid = tracer_pid;
- ErtsSmpBPUnlock(bdt);
- MatchSetUnref(old_match_spec);
- } else {
- BpDataCount *bdc = (BpDataCount *) bd;
- erts_aint_t count = 0;
- erts_aint_t res = 0;
-
- ASSERT(! match_spec);
- ASSERT(is_nil(tracer_pid));
-
- if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) {
- if (count_op == erts_break_stop) {
- count = erts_smp_atomic_read_nob(&bdc->acount);
- if (count >= 0) {
- while(1) {
- res = erts_smp_atomic_cmpxchg_nob(&bdc->acount, -count - 1, count);
- if ((res == count) || count < 0) break;
- count = res;
- }
- }
- } else {
- /* Reset call counter */
- erts_smp_atomic_set_nob(&bdc->acount, 0);
- }
-
- } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- BpDataTime *bdt = (BpDataTime *) bd;
- Uint i = 0;
-
- ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
-
- if (count_op == erts_break_stop) {
- bdt->pause = 1;
- } else {
- bdt->pause = 0;
- for (i = 0; i < bdt->n; i++) {
- bp_hash_delete(&(bdt->hash[i]));
- bp_hash_init(&(bdt->hash[i]), 32);
- }
- }
- } else {
- ASSERT (! count_op);
- }
- }
- return 1;
- }
- if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
- size = sizeof(BpDataTrace);
- } else {
- ASSERT(! match_spec);
- ASSERT(is_nil(tracer_pid));
- if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) {
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
- /* Do not insert a new breakpoint */
- return 1;
- }
- size = sizeof(BpDataCount);
- } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- if (count_op == erts_break_reset || count_op == erts_break_stop) {
- /* Do not insert a new breakpoint */
- return 1;
- }
- size = sizeof(BpDataTime);
+ common = break_flags & bp->flags;
+ if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetUnref(bp->local_ms);
+ } else if (common & ERTS_BPF_META_TRACE) {
+ MatchSetUnref(bp->meta_ms);
+ bp_meta_unref(bp->meta_pid);
+ } else if (common & ERTS_BPF_COUNT) {
+ if (count_op == erts_break_stop) {
+ bp->flags &= ~ERTS_BPF_COUNT_ACTIVE;
} else {
- ASSERT(! count_op);
- ASSERT(break_op == (BeamInstr) BeamOp(op_i_debug_breakpoint));
- size = sizeof(BpDataDebug);
+ bp->flags |= ERTS_BPF_COUNT_ACTIVE;
+ erts_smp_atomic_set_nob(&bp->count->acount, 0);
}
- }
- rs = (BpData ***) (pc-4);
- if (! *rs) {
- size_t ssize = sizeof(BeamInstr) * erts_no_schedulers;
- *rs = (BpData **) Alloc(ssize);
- sys_memzero(*rs, ssize);
- }
-
- r = &((*rs)[0]);
-
- if (! *r) {
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_trace_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_mtrace_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_debug_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_count_breakpoint));
- ASSERT(*pc != (BeamInstr) BeamOp(op_i_time_breakpoint));
- /* First breakpoint; create singleton ring */
- bd = Alloc(size);
- BpInit(bd, *pc);
- *r = bd;
- if (bif == BREAK_IS_ERL) {
- *pc = break_op;
- }
- } else {
- ASSERT(*pc == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_mtrace_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_debug_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_time_breakpoint) ||
- *pc == (BeamInstr) BeamOp(op_i_count_breakpoint) ||
- *pc == (BeamInstr) em_apply_bif);
- if (*pc == (BeamInstr) BeamOp(op_i_debug_breakpoint)) {
- /* Debug bp must be last, so if it is also first;
- * it must be singleton. */
- ASSERT(BpSingleton(*r));
- /* Insert new bp first in the ring, i.e second to last. */
- bd = Alloc(size);
- BpInitAndSpliceNext(bd, *pc, *r);
- if (bif == BREAK_IS_ERL) {
- *pc = break_op;
- }
- } else if ((*r)->prev->orig_instr
- == (BeamInstr) BeamOp(op_i_debug_breakpoint)) {
- /* Debug bp last in the ring; insert new second to last. */
- bd = Alloc(size);
- BpInitAndSplicePrev(bd, (*r)->prev->orig_instr, *r);
- (*r)->prev->orig_instr = break_op;
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ return;
+ } else if (common & ERTS_BPF_TIME_TRACE) {
+ BpDataTime* bdt = bp->time;
+ Uint i = 0;
+
+ if (count_op == erts_break_stop) {
+ bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE;
} else {
- /* Just insert last in the ring */
- bd = Alloc(size);
- BpInitAndSpliceNext(bd, (*r)->orig_instr, *r);
- (*r)->orig_instr = break_op;
- *r = bd;
+ bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE;
+ for (i = 0; i < bdt->n; i++) {
+ bp_hash_delete(&(bdt->hash[i]));
+ bp_hash_init(&(bdt->hash[i]), 32);
+ }
}
- }
- for (ix = 1; ix < erts_no_schedulers; ++ix) {
- (*rs)[ix] = (*rs)[0];
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ return;
}
- bd->this_instr = break_op;
- /* Init the bp type specific data */
- if (break_op == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- break_op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
-
- BpDataTrace *bdt = (BpDataTrace *) bd;
-
- MatchSetRef(match_spec);
- bdt->match_spec = match_spec;
- bdt->tracer_pid = tracer_pid;
- } else if (break_op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- BpDataTime *bdt = (BpDataTime *) bd;
- Uint i = 0;
-
- bdt->pause = 0;
- bdt->n = erts_no_schedulers;
- bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
+ /*
+ * Initialize the new breakpoint data.
+ */
+ if (break_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetRef(match_spec);
+ bp->local_ms = match_spec;
+ } else if (break_flags & ERTS_BPF_META_TRACE) {
+ BpMetaPid* bmp;
+ MatchSetRef(match_spec);
+ bp->meta_ms = match_spec;
+ bmp = Alloc(sizeof(BpMetaPid));
+ erts_refc_init(&bmp->refc, 1);
+ erts_smp_atomic_init_nob(&bmp->pid, tracer_pid);
+ bp->meta_pid = bmp;
+ } else if (break_flags & ERTS_BPF_COUNT) {
+ BpCount* bcp;
+
+ ASSERT((bp->flags & ERTS_BPF_COUNT) == 0);
+ bcp = Alloc(sizeof(BpCount));
+ erts_refc_init(&bcp->refc, 1);
+ erts_smp_atomic_init_nob(&bcp->acount, 0);
+ bp->count = bcp;
+ } else if (break_flags & ERTS_BPF_TIME_TRACE) {
+ BpDataTime* bdt;
+ int i;
+
+ ASSERT((bp->flags & ERTS_BPF_TIME_TRACE) == 0);
+ bdt = Alloc(sizeof(BpDataTime));
+ erts_refc_init(&bdt->refc, 1);
+ bdt->n = erts_no_schedulers;
+ bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
for (i = 0; i < bdt->n; i++) {
bp_hash_init(&(bdt->hash[i]), 32);
}
- } else if (break_op == (BeamInstr) BeamOp(op_i_count_breakpoint)) {
- BpDataCount *bdc = (BpDataCount *) bd;
- erts_smp_atomic_init_nob(&bdc->acount, 0);
+ bp->time = bdt;
}
- if (bif == BREAK_IS_ERL) {
- ++(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]);
+ bp->flags |= break_flags;
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+}
+
+static void
+clear_break(BpFunctions* f, Uint break_flags)
+{
+ Uint i;
+ Uint n;
+
+ n = f->matched;
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = f->matching[i].pc;
+ clear_function_break(pc, break_flags);
}
- return 1;
}
-static int clear_break(Eterm mfa[3], int specified, BeamInstr break_op)
+static int
+clear_function_break(BeamInstr *pc, Uint break_flags)
{
- int num_processed = 0;
- Module *modp;
+ GenericBp* g;
+ GenericBpData* bp;
+ Uint common;
+ ErtsBpIndex ix = erts_staging_bp_ix();
- if (!specified) {
- /* Iterate over all modules */
- int current;
- int last = module_code_size();
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
- for (current = 0; current < last; current++) {
- modp = module_code(current);
- ASSERT(modp != NULL);
- num_processed += clear_module_break(modp, mfa, specified, break_op);
- }
- } else {
- /* Process a single module */
- if ((modp = erts_get_module(mfa[0])) != NULL) {
- num_processed +=
- clear_module_break(modp, mfa, specified, break_op);
- }
+ if ((g = (GenericBp *) pc[-4]) == 0) {
+ return 1;
}
- return num_processed;
-}
-static int clear_module_break(Module *m, Eterm mfa[3], int specified,
- BeamInstr break_op) {
- BeamInstr** code_base;
- BeamInstr* code_ptr;
- int num_processed = 0;
- Uint i;
- BeamInstr n;
-
- ASSERT(m);
- code_base = (BeamInstr **) m->code;
- if (code_base == NULL) {
- return 0;
+ bp = &g->data[ix];
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ common = bp->flags & break_flags;
+ bp->flags &= ~break_flags;
+ if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
+ MatchSetUnref(bp->local_ms);
}
- n = (BeamInstr) code_base[MI_NUM_FUNCTIONS];
- for (i = 0; i < n; ++i) {
- code_ptr = code_base[MI_FUNCTIONS+i];
- if ((specified < 2 || mfa[1] == ((Eterm) code_ptr[3])) &&
- (specified < 3 || ((int) mfa[2]) == ((int) code_ptr[4]))) {
- BeamInstr *pc = code_ptr + 5;
-
- num_processed +=
- clear_function_break(m, pc, BREAK_IS_ERL, break_op);
- }
+ if (common & ERTS_BPF_META_TRACE) {
+ MatchSetUnref(bp->meta_ms);
+ }
+ if (common & ERTS_BPF_COUNT) {
+ ASSERT((bp->flags & ERTS_BPF_COUNT_ACTIVE) == 0);
+ bp_count_unref(bp->count);
+ }
+ if (common & ERTS_BPF_TIME_TRACE) {
+ ASSERT((bp->flags & ERTS_BPF_TIME_TRACE_ACTIVE) == 0);
+ bp_time_unref(bp->time);
}
- return num_processed;
-}
-static int clear_function_break(Module *m, BeamInstr *pc, int bif, BeamInstr break_op) {
- BpData *bd;
- Uint ix = 0;
- BeamInstr **code_base = NULL;
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ return 1;
+}
- if (bif == BREAK_IS_ERL) {
- code_base = (BeamInstr **)m->code;
- ASSERT(code_base);
- ASSERT(code_base <= (BeamInstr **)pc);
- ASSERT((BeamInstr **)pc < code_base + (m->code_length/sizeof(BeamInstr *)));
- } else {
- ASSERT(*pc == (BeamInstr) em_apply_bif);
- ASSERT(m == NULL);
+static void
+bp_meta_unref(BpMetaPid* bmp)
+{
+ if (erts_refc_dectest(&bmp->refc, 0) <= 0) {
+ Free(bmp);
}
+}
- /*
- * Currently no trace support for native code.
- */
- if (erts_is_native_break(pc)) {
- return 0;
+static void
+bp_count_unref(BpCount* bcp)
+{
+ if (erts_refc_dectest(&bcp->refc, 0) <= 0) {
+ Free(bcp);
}
+}
- while ( (bd = is_break(pc, break_op))) {
- /* Remove all breakpoints of this type.
- * There should be only one of each type,
- * but break_op may be 0 which matches any type.
+static void
+bp_time_unref(BpDataTime* bdt)
+{
+ if (erts_refc_dectest(&bdt->refc, 0) <= 0) {
+ Uint i = 0;
+ Uint j = 0;
+ Process *h_p = NULL;
+ bp_data_time_item_t* item = NULL;
+ process_breakpoint_time_t* pbt = NULL;
+
+ /* remove all psd associated with the hash
+ * and then delete the hash.
+ * ... sigh ...
*/
- BeamInstr op;
- BpData ***rs = (BpData ***) (pc - 4);
- BpData **r = NULL;
-
-#ifdef DEBUG
- for (ix = 1; ix < erts_no_schedulers; ++ix) {
- ASSERT((*rs)[ix] == (*rs)[0]);
- }
-#endif
-
- r = &((*rs)[0]);
-
- ASSERT(*r);
- /* Find opcode for this breakpoint */
- if (break_op) {
- op = break_op;
- } else {
- if (bd == (*r)->next) {
- /* First breakpoint in ring */
- op = *pc;
- } else {
- op = bd->prev->orig_instr;
- }
- }
- if (BpSingleton(bd)) {
- ASSERT(*r == bd);
- /* Only one breakpoint to remove */
- if (bif == BREAK_IS_ERL) {
- *pc = bd->orig_instr;
- }
- Free(*rs);
- *rs = NULL;
- } else {
- BpData *bd_prev = bd->prev;
-
- BpSpliceNext(bd, bd_prev);
- ASSERT(BpSingleton(bd));
- if (bd == *r) {
- /* We removed the last breakpoint in the ring */
- *r = bd_prev;
- bd_prev->orig_instr = bd->orig_instr;
- } else if (bd_prev == *r) {
- /* We removed the first breakpoint in the ring */
- if (bif == BREAK_IS_ERL) {
- *pc = bd->orig_instr;
- }
- } else {
- bd_prev->orig_instr = bd->orig_instr;
- }
- }
- if (op == (BeamInstr) BeamOp(op_i_trace_breakpoint) ||
- op == (BeamInstr) BeamOp(op_i_mtrace_breakpoint)) {
-
- BpDataTrace *bdt = (BpDataTrace *) bd;
- MatchSetUnref(bdt->match_spec);
- }
- if (op == (BeamInstr) BeamOp(op_i_time_breakpoint)) {
- BpDataTime *bdt = (BpDataTime *) bd;
- Uint i = 0;
- Uint j = 0;
- Process *h_p = NULL;
- bp_data_time_item_t *item = NULL;
- process_breakpoint_time_t *pbt = NULL;
-
- /* remove all psd associated with the hash
- * and then delete the hash.
- * ... sigh ...
- */
- for( i = 0; i < bdt->n; ++i) {
- if (bdt->hash[i].used) {
- for (j = 0; j < bdt->hash[i].n; ++j) {
- item = &(bdt->hash[i].item[j]);
- if (item->pid != NIL) {
- h_p = process_tab[internal_pid_index(item->pid)];
- if (h_p) {
- pbt = ERTS_PROC_SET_CALL_TIME(h_p, ERTS_PROC_LOCK_MAIN, NULL);
- if (pbt) {
- Free(pbt);
- }
+ for (i = 0; i < bdt->n; ++i) {
+ if (bdt->hash[i].used) {
+ for (j = 0; j < bdt->hash[i].n; ++j) {
+ item = &(bdt->hash[i].item[j]);
+ if (item->pid != NIL) {
+ h_p = erts_pid2proc(NULL, 0, item->pid,
+ ERTS_PROC_LOCK_MAIN);
+ if (h_p) {
+ pbt = ERTS_PROC_SET_CALL_TIME(h_p,
+ ERTS_PROC_LOCK_MAIN,
+ NULL);
+ if (pbt) {
+ Free(pbt);
}
+ erts_smp_proc_unlock(h_p, ERTS_PROC_LOCK_MAIN);
}
}
}
- bp_hash_delete(&(bdt->hash[i]));
}
- Free(bdt->hash);
- bdt->hash = NULL;
- bdt->n = 0;
+ bp_hash_delete(&(bdt->hash[i]));
}
- Free(bd);
- if (bif == BREAK_IS_ERL) {
- ASSERT(((BeamInstr) code_base[MI_NUM_BREAKPOINTS]) > 0);
- --(*(BeamInstr*)&code_base[MI_NUM_BREAKPOINTS]);
- }
- if (*rs) {
- for (ix = 1; ix < erts_no_schedulers; ++ix) {
- (*rs)[ix] = (*rs)[0];
- }
- }
- } /* while bd != NULL */
- return 1;
+ Free(bdt->hash);
+ Free(bdt);
+ }
}
-
-
-/*
-** Searches (linear forward) the breakpoint ring for a specified opcode
-** and returns a pointer to the breakpoint data structure or NULL if
-** not found. If the specified opcode is 0, the last breakpoint is
-** returned. The program counter must point to the first executable
-** (breakpoint) instruction of the function.
-*/
-
-BpData *erts_get_time_break(Process *p, BeamInstr *pc) {
- return get_break(p, pc, (BeamInstr) BeamOp(op_i_time_breakpoint));
+static BpDataTime*
+get_time_break(BeamInstr *pc)
+{
+ GenericBpData* bp = check_break(pc, ERTS_BPF_TIME_TRACE);
+ return bp ? bp->time : 0;
}
-static BpData *get_break(Process *p, BeamInstr *pc, BeamInstr break_op) {
- ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- if (! erts_is_native_break(pc)) {
- BpData **rs = (BpData **) pc[-4];
- BpData *bd = NULL, *ebd = NULL;
-
- if (! rs) {
- return NULL;
- }
-
- bd = ebd = rs[bp_sched2ix_proc(p)];
- ASSERT(bd);
- if (bd->this_instr == break_op) {
- return bd;
- }
-
- bd = bd->next;
- while (bd != ebd) {
- ASSERT(bd);
- if (bd->this_instr == break_op) {
- ASSERT(bd);
- return bd;
- }
- bd = bd->next;
- }
- }
- return NULL;
-}
+static GenericBpData*
+check_break(BeamInstr *pc, Uint break_flags)
+{
+ GenericBp* g = (GenericBp *) pc[-4];
-static BpData *is_break(BeamInstr *pc, BeamInstr break_op) {
- BpData **rs;
- BpData *bd = NULL, *ebd = NULL;
ASSERT(pc[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
-
if (erts_is_native_break(pc)) {
- return NULL;
- }
- rs = (BpData **) pc[-4];
- if (! rs) {
- return NULL;
- }
-
- bd = ebd = rs[erts_bp_sched2ix()];
- ASSERT(bd);
- if ( (break_op == 0) || (bd->this_instr == break_op)) {
- return bd;
+ return 0;
}
-
- bd = bd->next;
- while (bd != ebd) {
- ASSERT(bd);
- if (bd->this_instr == break_op) {
- ASSERT(bd);
- return bd;
+ if (g) {
+ GenericBpData* bp = &g->data[erts_active_bp_ix()];
+ ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
+ if (bp->flags & break_flags) {
+ return bp;
}
- bd = bd->next;
}
- return NULL;
+ return 0;
}
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index 167069552f..b061401863 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -25,77 +25,6 @@
#include "erl_vm.h"
#include "global.h"
-
-
-/* A couple of gotchas:
- *
- * The breakpoint structure from BeamInstr,
- * In beam_emu where the instruction counter pointer, I (or pc),
- * points to the *current* instruction. At that time, if the instruction
- * is a breakpoint instruction the pc looks like the following,
- *
- * I[-5] | op_i_func_info_IaaI | scheduler specific entries
- * I[-4] | BpData** bpa | --> | BpData * bdas1 | ... | BpData * bdasN |
- * I[-3] | Tagged Module | | |
- * I[-2] | Tagged Function | V V
- * I[-1] | Arity | BpData -> BpData -> BpData -> BpData
- * I[0] | The bp instruction | ^ * the bp wheel * |
- * |------------------------------
- *
- * Common struct to all bp_data_*
- *
- * 1) The type of bp_data structure in the ring is deduced from the
- * orig_instr field of the structure _before_ in the ring, except for
- * the first structure in the ring that has its instruction in
- * pc[0] of the code to execute.
- * This is valid as long as you don't search for the function while it is
- * being executed by something else. Or is in the middle of its rotation for
- * any other reason.
- * A key, the bp beam instruction, is included for this reason.
- *
- * 2) pc[-4][sched_id - 1] points to the _last_ structure in the ring before the
- * breakpoints are being executed.
- *
- * So, as an example, when a breakpointed function starts to execute,
- * the first instruction that is a breakpoint instruction at pc[0] finds
- * its data at ((BpData **) pc[-4][sched_id - 1])->next and has to cast that pointer
- * to the correct bp_data type.
-*/
-
-typedef struct bp_data {
- struct bp_data *next; /* Doubly linked ring pointers */
- struct bp_data *prev; /* -"- */
- BeamInstr orig_instr; /* The original instruction to execute */
- BeamInstr this_instr; /* key */
-} BpData;
-/*
-** All the following bp_data_.. structs must begin the same way
-*/
-
-typedef struct bp_data_trace {
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
- Binary *match_spec;
- Eterm tracer_pid;
-} BpDataTrace;
-
-typedef struct bp_data_debug {
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
-} BpDataDebug;
-
-typedef struct bp_data_count { /* Call count */
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
- erts_smp_atomic_t acount;
-} BpDataCount;
-
typedef struct {
Eterm pid;
Sint count;
@@ -110,13 +39,9 @@ typedef struct {
} bp_time_hash_t;
typedef struct bp_data_time { /* Call time */
- struct bp_data *next;
- struct bp_data *prev;
- BeamInstr orig_instr;
- BeamInstr this_instr; /* key */
- Uint pause;
- Uint n;
- bp_time_hash_t *hash;
+ Uint n;
+ bp_time_hash_t *hash;
+ erts_refc_t refc;
} BpDataTime;
typedef struct {
@@ -126,64 +51,42 @@ typedef struct {
BeamInstr *pc;
} process_breakpoint_time_t; /* used within psd */
-extern erts_smp_spinlock_t erts_bp_lock;
+typedef struct {
+ erts_smp_atomic_t acount;
+ erts_refc_t refc;
+} BpCount;
+
+typedef struct {
+ erts_smp_atomic_t pid;
+ erts_refc_t refc;
+} BpMetaPid;
+
+typedef struct generic_bp_data {
+ Uint flags;
+ Binary* local_ms; /* Match spec for local call trace */
+ Binary* meta_ms; /* Match spec for meta trace */
+ BpMetaPid* meta_pid; /* Meta trace pid */
+ BpCount* count; /* For call count */
+ BpDataTime* time; /* For time trace */
+} GenericBpData;
+
+#define ERTS_NUM_BP_IX 2
+
+typedef struct generic_bp {
+ BeamInstr orig_instr;
+ GenericBpData data[ERTS_NUM_BP_IX];
+} GenericBp;
#define ERTS_BP_CALL_TIME_SCHEDULE_IN (0)
#define ERTS_BP_CALL_TIME_SCHEDULE_OUT (1)
#define ERTS_BP_CALL_TIME_SCHEDULE_EXITING (2)
-#define ERTS_BP_CALL_TIME_CALL (0)
-#define ERTS_BP_CALL_TIME_RETURN (1)
-#define ERTS_BP_CALL_TIME_TAIL_CALL (2)
-
-#ifdef ERTS_SMP
-#define ErtsSmpBPLock(BDC) erts_smp_spin_lock(&erts_bp_lock)
-#define ErtsSmpBPUnlock(BDC) erts_smp_spin_unlock(&erts_bp_lock)
-#else
-#define ErtsSmpBPLock(BDC)
-#define ErtsSmpBPUnlock(BDC)
-#endif
-
#ifdef ERTS_SMP
#define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1)
#else
#define bp_sched2ix_proc(p) (0)
#endif
-#define ErtsCountBreak(p, pc,instr_result) \
-do { \
- BpData **bds = (BpData **) (pc)[-4]; \
- BpDataCount *bdc = NULL; \
- Uint ix = bp_sched2ix_proc( (p) ); \
- erts_aint_t count = 0; \
- \
- ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \
- ASSERT(bds); \
- bdc = (BpDataCount *) bds[ix]; \
- bdc = (BpDataCount *) bdc->next; \
- ASSERT(bdc); \
- bds[ix] = (BpData *) bdc; \
- count = erts_smp_atomic_read_nob(&bdc->acount); \
- if (count >= 0) erts_smp_atomic_inc_nob(&bdc->acount); \
- *(instr_result) = bdc->orig_instr; \
-} while (0)
-
-#define ErtsBreakSkip(p, pc,instr_result) \
-do { \
- BpData **bds = (BpData **) (pc)[-4]; \
- BpData *bd = NULL; \
- Uint ix = bp_sched2ix_proc( (p) ); \
- \
- ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \
- ASSERT(bds); \
- bd = bds[ix]; \
- ASSERT(bd); \
- bd = bd->next; \
- ASSERT(bd); \
- bds[ix] = bd; \
- *(instr_result) = bd->orig_instr; \
-} while (0)
-
enum erts_break_op{
erts_break_nop = 0, /* Must be false */
erts_break_set = !0, /* Must be true */
@@ -191,7 +94,17 @@ enum erts_break_op{
erts_break_stop
};
+typedef Uint32 ErtsBpIndex;
+typedef struct {
+ BeamInstr* pc;
+ Module* mod;
+} BpFunction;
+
+typedef struct {
+ Uint matched; /* Number matched */
+ BpFunction* matching; /* Matching functions */
+} BpFunctions;
/*
** Function interface exported from beam_bp.c
@@ -199,49 +112,66 @@ enum erts_break_op{
void erts_bp_init(void);
-int erts_set_trace_break(Eterm mfa[3], int specified, Binary *match_spec,
- Eterm tracer_pid);
-int erts_clear_trace_break(Eterm mfa[3], int specified);
-int erts_set_mtrace_break(Eterm mfa[3], int specified, Binary *match_spec,
+void erts_prepare_bp_staging(void);
+void erts_commit_staged_bp(void);
+
+ERTS_GLB_INLINE ErtsBpIndex erts_active_bp_ix(void);
+ERTS_GLB_INLINE ErtsBpIndex erts_staging_bp_ix(void);
+
+void erts_bp_match_functions(BpFunctions* f, Eterm mfa[3], int specified);
+void erts_bp_match_export(BpFunctions* f, Eterm mfa[3], int specified);
+void erts_bp_free_matched_functions(BpFunctions* f);
+
+void erts_install_breakpoints(BpFunctions* f);
+void erts_uninstall_breakpoints(BpFunctions* f);
+void erts_consolidate_bp_data(BpFunctions* f, int local);
+void erts_consolidate_bif_bp_data(void);
+
+void erts_set_trace_break(BpFunctions *f, Binary *match_spec);
+void erts_clear_trace_break(BpFunctions *f);
+
+void erts_set_call_trace_bif(BeamInstr *pc, Binary *match_spec, int local);
+void erts_clear_call_trace_bif(BeamInstr *pc, int local);
+
+void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec,
Eterm tracer_pid);
-int erts_clear_mtrace_break(Eterm mfa[3], int specified);
+void erts_clear_mtrace_break(BpFunctions *f);
void erts_set_mtrace_bif(BeamInstr *pc, Binary *match_spec,
Eterm tracer_pid);
void erts_clear_mtrace_bif(BeamInstr *pc);
-int erts_set_debug_break(Eterm mfa[3], int specified);
-int erts_clear_debug_break(Eterm mfa[3], int specified);
-int erts_set_count_break(Eterm mfa[3], int specified, enum erts_break_op);
-int erts_clear_count_break(Eterm mfa[3], int specified);
+void erts_set_debug_break(BpFunctions *f);
+void erts_clear_debug_break(BpFunctions *f);
+void erts_set_count_break(BpFunctions *f, enum erts_break_op);
+void erts_clear_count_break(BpFunctions *f);
-int erts_clear_break(Eterm mfa[3], int specified);
+
+void erts_clear_all_breaks(BpFunctions* f);
int erts_clear_module_break(Module *modp);
-int erts_clear_function_break(Module *modp, BeamInstr *pc);
+void erts_clear_export_break(Module *modp, BeamInstr* pc);
+BeamInstr erts_generic_breakpoint(Process* c_p, BeamInstr* I, Eterm* reg);
BeamInstr erts_trace_break(Process *p, BeamInstr *pc, Eterm *args,
Uint32 *ret_flags, Eterm *tracer_pid);
-Uint32 erts_bif_mtrace(Process *p, BeamInstr *pc, Eterm *args,
- int local, Eterm *tracer_pid);
-int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret,
- Eterm *tracer_pid_ret);
+int erts_is_trace_break(BeamInstr *pc, Binary **match_spec_ret, int local);
int erts_is_mtrace_break(BeamInstr *pc, Binary **match_spec_ret,
Eterm *tracer_pid_rte);
int erts_is_mtrace_bif(BeamInstr *pc, Binary **match_spec_ret,
Eterm *tracer_pid_ret);
int erts_is_native_break(BeamInstr *pc);
-int erts_is_count_break(BeamInstr *pc, Sint *count_ret);
+int erts_is_count_break(BeamInstr *pc, Uint *count_ret);
int erts_is_time_break(Process *p, BeamInstr *pc, Eterm *call_time);
-void erts_trace_time_break(Process *p, BeamInstr *pc, BpDataTime *bdt, Uint type);
+void erts_trace_time_call(Process* c_p, BeamInstr* pc, BpDataTime* bdt);
+void erts_trace_time_return(Process* c_p, BeamInstr* pc);
void erts_schedule_time_break(Process *p, Uint out);
-int erts_set_time_break(Eterm mfa[3], int specified, enum erts_break_op);
-int erts_clear_time_break(Eterm mfa[3], int specified);
+void erts_set_time_break(BpFunctions *f, enum erts_break_op);
+void erts_clear_time_break(BpFunctions *f);
int erts_is_time_trace_bif(Process *p, BeamInstr *pc, Eterm *call_time);
void erts_set_time_trace_bif(BeamInstr *pc, enum erts_break_op);
void erts_clear_time_trace_bif(BeamInstr *pc);
-BpData *erts_get_time_break(Process *p, BeamInstr *pc);
BeamInstr *erts_find_local_func(Eterm mfa[3]);
@@ -258,6 +188,19 @@ ERTS_GLB_INLINE Uint erts_bp_sched2ix(void)
return 0;
#endif
}
+
+extern erts_smp_atomic32_t erts_active_bp_index;
+extern erts_smp_atomic32_t erts_staging_bp_index;
+
+ERTS_GLB_INLINE ErtsBpIndex erts_active_bp_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&erts_active_bp_index);
+}
+
+ERTS_GLB_INLINE ErtsBpIndex erts_staging_bp_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&erts_staging_bp_index);
+}
#endif
#endif /* _BEAM_BP_H */
diff --git a/erts/emulator/beam/beam_catches.c b/erts/emulator/beam/beam_catches.c
index 406ef1db5f..d374d0469e 100644
--- a/erts/emulator/beam/beam_catches.c
+++ b/erts/emulator/beam/beam_catches.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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,78 +31,143 @@ typedef struct {
unsigned cdr;
} beam_catch_t;
-static int free_list;
-static unsigned high_mark;
-static unsigned tabsize;
-static beam_catch_t *beam_catches;
+#ifdef DEBUG
+# define IF_DEBUG(x) x
+#else
+# define IF_DEBUG(x)
+#endif
+
+struct bc_pool {
+ int free_list;
+ unsigned high_mark;
+ unsigned tabsize;
+ beam_catch_t *beam_catches;
+ /*
+ * Note that the 'beam_catches' area is shared by pools. Used slots
+ * are readonly as long as the module is not purgable. The free-list is
+ * protected by the code_ix lock.
+ */
+
+ IF_DEBUG(int is_staging;)
+};
+
+static struct bc_pool bccix[ERTS_NUM_CODE_IX];
void beam_catches_init(void)
{
- tabsize = DEFAULT_TABSIZE;
- free_list = -1;
- high_mark = 0;
+ int i;
+
+ bccix[0].tabsize = DEFAULT_TABSIZE;
+ bccix[0].free_list = -1;
+ bccix[0].high_mark = 0;
+ bccix[0].beam_catches = erts_alloc(ERTS_ALC_T_CODE,
+ sizeof(beam_catch_t)*DEFAULT_TABSIZE);
+ IF_DEBUG(bccix[0].is_staging = 0);
+ for (i=1; i<ERTS_NUM_CODE_IX; i++) {
+ bccix[i] = bccix[i-1];
+ }
+ /* For initial load: */
+ IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 1);
+}
- beam_catches = erts_alloc(ERTS_ALC_T_CODE, sizeof(beam_catch_t)*DEFAULT_TABSIZE);
+
+static void gc_old_vec(beam_catch_t* vec)
+{
+ int i;
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ if (bccix[i].beam_catches == vec) {
+ return;
+ }
+ }
+ erts_free(ERTS_ALC_T_CODE, vec);
+}
+
+
+void beam_catches_start_staging(void)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+ ErtsCodeIndex src = erts_active_code_ix();
+ beam_catch_t* prev_vec = bccix[dst].beam_catches;
+
+ ASSERT(!bccix[src].is_staging && !bccix[dst].is_staging);
+
+ bccix[dst] = bccix[src];
+ gc_old_vec(prev_vec);
+ IF_DEBUG(bccix[dst].is_staging = 1);
+}
+
+void beam_catches_end_staging(int commit)
+{
+ IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 0);
}
unsigned beam_catches_cons(BeamInstr *cp, unsigned cdr)
{
int i;
+ struct bc_pool* p = &bccix[erts_staging_code_ix()];
+ ASSERT(p->is_staging);
/*
* Allocate from free_list while it is non-empty.
* If free_list is empty, allocate at high_mark.
- *
- * This avoids the need to initialise the free list in
- * beam_catches_init(), which would cost O(TABSIZ) time.
*/
- if( free_list >= 0 ) {
- i = free_list;
- free_list = beam_catches[i].cdr;
- } else if( high_mark < tabsize ) {
- i = high_mark;
- high_mark++;
- } else {
- /* No free slots and table is full: realloc table */
- tabsize = 2*tabsize;
- beam_catches = erts_realloc(ERTS_ALC_T_CODE, beam_catches, sizeof(beam_catch_t)*tabsize);
- i = high_mark;
- high_mark++;
+ if (p->free_list >= 0) {
+ i = p->free_list;
+ p->free_list = p->beam_catches[i].cdr;
+ }
+ else {
+ if (p->high_mark >= p->tabsize) {
+ /* No free slots and table is full: realloc table */
+ beam_catch_t* prev_vec = p->beam_catches;
+ unsigned newsize = p->tabsize*2;
+
+ p->beam_catches = erts_alloc(ERTS_ALC_T_CODE,
+ newsize*sizeof(beam_catch_t));
+ sys_memcpy(p->beam_catches, prev_vec,
+ p->tabsize*sizeof(beam_catch_t));
+ gc_old_vec(prev_vec);
+ p->tabsize = newsize;
+ }
+ i = p->high_mark++;
}
- beam_catches[i].cp = cp;
- beam_catches[i].cdr = cdr;
+ p->beam_catches[i].cp = cp;
+ p->beam_catches[i].cdr = cdr;
return i;
}
BeamInstr *beam_catches_car(unsigned i)
{
- if( i >= tabsize ) {
+ struct bc_pool* p = &bccix[erts_active_code_ix()];
+
+ if (i >= p->tabsize ) {
erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i);
}
- return beam_catches[i].cp;
+ return p->beam_catches[i].cp;
}
-void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes)
+void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes,
+ ErtsCodeIndex code_ix)
{
+ struct bc_pool* p = &bccix[code_ix];
unsigned i, cdr;
+ ASSERT((code_ix == erts_active_code_ix()) != bccix[erts_staging_code_ix()].is_staging);
for(i = head; i != (unsigned)-1;) {
- if( i >= tabsize ) {
+ if (i >= p->tabsize) {
erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i);
}
- if( (char*)beam_catches[i].cp - (char*)code >= code_bytes ) {
+ if( (char*)p->beam_catches[i].cp - (char*)code >= code_bytes ) {
erl_exit(1,
- "beam_catches_delmod: item %#x has cp %#lx which is not "
- "in module's range [%#lx,%#lx[\r\n",
- i, (long)beam_catches[i].cp,
- (long)code, (long)((char*)code + code_bytes));
+ "beam_catches_delmod: item %#x has cp %p which is not "
+ "in module's range [%p,%p[\r\n",
+ i, p->beam_catches[i].cp, code, ((char*)code + code_bytes));
}
- beam_catches[i].cp = 0;
- cdr = beam_catches[i].cdr;
- beam_catches[i].cdr = free_list;
- free_list = i;
+ p->beam_catches[i].cp = 0;
+ cdr = p->beam_catches[i].cdr;
+ p->beam_catches[i].cdr = p->free_list;
+ p->free_list = i;
i = cdr;
}
}
diff --git a/erts/emulator/beam/beam_catches.h b/erts/emulator/beam/beam_catches.h
index 6223427f0d..51ef463b2f 100644
--- a/erts/emulator/beam/beam_catches.h
+++ b/erts/emulator/beam/beam_catches.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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,12 +20,21 @@
#ifndef __BEAM_CATCHES_H
#define __BEAM_CATCHES_H
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "code_ix.h"
+
#define BEAM_CATCHES_NIL (-1)
void beam_catches_init(void);
+void beam_catches_start_staging(void);
+void beam_catches_end_staging(int commit);
unsigned beam_catches_cons(BeamInstr* cp, unsigned cdr);
BeamInstr *beam_catches_car(unsigned i);
-void beam_catches_delmod(unsigned head, BeamInstr* code, unsigned code_bytes);
+void beam_catches_delmod(unsigned head, BeamInstr* code, unsigned code_bytes,
+ ErtsCodeIndex);
#define catch_pc(x) beam_catches_car(catch_val((x)))
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 8041c92162..e36ec2a93e 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -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
@@ -84,6 +84,7 @@ erts_debug_breakpoint_2(BIF_ALIST_2)
int i;
int specified = 0;
Eterm res;
+ BpFunctions f;
if (bool != am_true && bool != am_false)
goto error;
@@ -114,18 +115,30 @@ erts_debug_breakpoint_2(BIF_ALIST_2)
mfa[2] = signed_val(mfa[2]);
}
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_breakpoint_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
+ erts_bp_match_functions(&f, mfa, specified);
if (bool == am_true) {
- res = make_small(erts_set_debug_break(mfa, specified));
+ erts_set_debug_break(&f);
+ erts_install_breakpoints(&f);
+ erts_commit_staged_bp();
} else {
- res = make_small(erts_clear_debug_break(mfa, specified));
+ erts_clear_debug_break(&f);
+ erts_commit_staged_bp();
+ erts_uninstall_breakpoints(&f);
}
+ erts_consolidate_bp_data(&f, 1);
+ res = make_small(f.matched);
+ erts_bp_free_matched_functions(&f);
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
-
+ erts_release_code_write_permission();
return res;
error:
@@ -207,6 +220,7 @@ erts_debug_disassemble_1(BIF_ALIST_1)
BIF_RET(am_false);
}
} else if (is_tuple(addr)) {
+ ErtsCodeIndex code_ix;
Module* modp;
Eterm mod;
Eterm name;
@@ -225,14 +239,14 @@ erts_debug_disassemble_1(BIF_ALIST_1)
goto error;
}
arity = signed_val(tp[3]);
- modp = erts_get_module(mod);
+ code_ix = erts_active_code_ix();
+ modp = erts_get_module(mod, code_ix);
/*
* Try the export entry first to allow disassembly of special functions
* such as erts_debug:apply/4. Then search for it in the module.
*/
-
- if ((ep = erts_find_function(mod, name, arity)) != NULL) {
+ if ((ep = erts_find_function(mod, name, arity, code_ix)) != NULL) {
/* XXX: add "&& ep->address != ep->code+3" condition?
* Consider a traced function.
* Its ep will have ep->address == ep->code+3.
@@ -241,9 +255,9 @@ erts_debug_disassemble_1(BIF_ALIST_1)
* But this code_ptr will point to the start of the Export,
* not the function's func_info instruction. BOOM !?
*/
- code_ptr = ((BeamInstr *) ep->address) - 5;
+ code_ptr = ((BeamInstr *) ep->addressv[code_ix]) - 5;
funcinfo = code_ptr+2;
- } else if (modp == NULL || (code_base = modp->code) == NULL) {
+ } else if (modp == NULL || (code_base = modp->curr.code) == NULL) {
BIF_RET(am_undef);
} else {
n = code_base[MI_NUM_FUNCTIONS];
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 6d3b15cd46..944ed6da81 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -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
@@ -63,11 +63,7 @@
# define PROCESS_MAIN_CHK_LOCKS(P) \
do { \
if ((P)) { \
- erts_pix_lock_t *pix_lock__ = ERTS_PIX2PIXLOCK(internal_pid_index((P)->id));\
erts_proc_lc_chk_only_proc_main((P)); \
- erts_pix_lock(pix_lock__); \
- ASSERT(0 < (P)->lock.refc && (P)->lock.refc < erts_no_schedulers*5);\
- erts_pix_unlock(pix_lock__); \
} \
else \
erts_lc_check_exact(NULL, 0); \
@@ -221,7 +217,6 @@ BeamInstr beam_continue_exit[1];
BeamInstr* em_call_error_handler;
BeamInstr* em_apply_bif;
-BeamInstr* em_call_traced_function;
/* NOTE These should be the only variables containing trace instructions.
@@ -236,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.
*/
@@ -495,7 +485,7 @@ extern int count_instructions;
do { \
if (FCALLS > 0) { \
Eterm* dis_next; \
- SET_I(((Export *) Arg(0))->address); \
+ SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
dis_next = (Eterm *) *I; \
FCALLS--; \
CHECK_ARGS(I); \
@@ -504,7 +494,7 @@ extern int count_instructions;
&& FCALLS > neg_o_reds) { \
goto save_calls1; \
} else { \
- SET_I(((Export *) Arg(0))->address); \
+ SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
CHECK_ARGS(I); \
goto context_switch; \
} \
@@ -526,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]
@@ -965,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();
}
@@ -1088,17 +1070,6 @@ init_emulator(void)
#endif /* USE_VM_PROBES */
-#ifdef USE_VM_PROBES
-void
-dtrace_drvport_str(ErlDrvPort drvport, char *port_buf)
-{
- Port *port = erts_drvport2port(drvport);
-
- erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
- port_channel_no(port->id),
- port_number(port->id));
-}
-#endif
/*
* process_main() is called twice:
* The first call performs some initialisation, including exporting
@@ -1107,9 +1078,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
@@ -1215,7 +1184,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);
@@ -1247,7 +1216,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 {
@@ -1507,7 +1476,7 @@ void process_main(void)
*/
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]);
DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
}
#endif
@@ -1522,7 +1491,7 @@ void process_main(void)
SET_CP(c_p, I+2);
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]);
DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
}
#endif
@@ -1535,7 +1504,7 @@ void process_main(void)
OpCase(i_call_ext_only_e):
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->addressv[erts_active_code_ix()]);
DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
}
#endif
@@ -1611,6 +1580,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)) {
@@ -1826,13 +1796,12 @@ void process_main(void)
msgp = PEEK_MESSAGE(c_p);
if (msgp)
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- else {
+ else
#endif
+ {
SET_I((BeamInstr *) Arg(0));
Goto(*I); /* Jump to a wait or wait_timeout instruction */
-#ifdef ERTS_SMP
}
-#endif
}
ErtsMoveMsgAttachmentIntoProc(msgp, c_p, E, HTOP, FCALLS,
{
@@ -1887,14 +1856,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;
@@ -1919,7 +1888,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
@@ -1935,7 +1904,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
@@ -2061,11 +2030,11 @@ void process_main(void)
OpCase(wait_f):
wait2: {
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
c_p->i = (BeamInstr *) Arg(0); /* L1 */
SWAPOUT;
c_p->arity = 0;
- c_p->status = P_WAITING;
+ erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
c_p->current = NULL;
goto do_schedule;
@@ -2588,6 +2557,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);
@@ -3144,10 +3114,6 @@ void process_main(void)
c_p->arg_reg[0] = r(0);
SWAPOUT;
c_p->i = I;
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
- if (c_p->status != P_SUSPENDED)
- erts_add_to_runq(c_p);
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
goto do_schedule1;
}
@@ -3326,7 +3292,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));
{
@@ -3371,7 +3336,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;
@@ -4580,64 +4544,6 @@ void process_main(void)
* Trace and debugging support.
*/
- /*
- * At this point, I points to the code[3] in the export entry for
- * a trace-enabled function.
- *
- * code[0]: Module
- * code[1]: Function
- * code[2]: Arity
- * code[3]: &&call_traced_function
- * code[4]: Address of function.
- */
- OpCase(call_traced_function): {
- if (IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
- unsigned offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
- Export* ep = (Export *) (((char *)I)-offset);
- Uint32 flags;
-
- SWAPOUT;
- reg[0] = r(0);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- flags = erts_call_trace(c_p, ep->code, ep->match_prog_set, reg,
- 0, &c_p->tracer_proc);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- SWAPIN;
-
- if (flags & MATCH_SET_RX_TRACE) {
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- if (E - 3 < HTOP) {
- /* SWAPOUT, SWAPIN was done and r(0) was saved above */
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, 3, reg, ep->code[2]);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- SWAPIN;
- }
- E -= 3;
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- ASSERT(is_CP((BeamInstr)(ep->code)));
- ASSERT(is_internal_pid(c_p->tracer_proc) ||
- is_internal_port(c_p->tracer_proc));
- E[2] = make_cp(c_p->cp); /* Code in lower range on halfword */
- E[1] = am_true; /* Process tracer */
- E[0] = make_cp(ep->code);
- 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_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- SET_I((BeamInstr *)Arg(0));
- Dispatch();
- }
-
OpCase(return_trace): {
BeamInstr* code = (BeamInstr *) (UWord) E[0];
@@ -4652,80 +4558,22 @@ void process_main(void)
Goto(*I);
}
- OpCase(i_count_breakpoint): {
+ OpCase(i_generic_breakpoint): {
BeamInstr real_I;
-
- ErtsCountBreak(c_p, (BeamInstr *) I, &real_I);
+ ASSERT(I[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
+ SWAPOUT;
+ reg[0] = r(0);
+ real_I = erts_generic_breakpoint(c_p, I, reg);
+ r(0) = reg[0];
+ SWAPIN;
ASSERT(VALID_INSTR(real_I));
Goto(real_I);
}
- /* need to send mfa instead of bdt pointer
- * the pointer might be deallocated.
- */
-
- OpCase(i_time_breakpoint): {
- BeamInstr real_I;
- BpData **bds = (BpData **) (I)[-4];
- BpDataTime *bdt = NULL;
- Uint ix = 0;
-#ifdef ERTS_SMP
- ix = c_p->scheduler_data->no - 1;
-#else
- ix = 0;
-#endif
- bdt = (BpDataTime *)bds[ix];
-
- ASSERT((I)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI));
- ASSERT(bdt);
- bdt = (BpDataTime *) bdt->next;
- ASSERT(bdt);
- bds[ix] = (BpData *) bdt;
- real_I = bdt->orig_instr;
- ASSERT(VALID_INSTR(real_I));
-
- if (IS_TRACED_FL(c_p, F_TRACE_CALLS) && !(bdt->pause)) {
- if ( (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) ||
- (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) ||
- (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace))) {
- /* This _IS_ a tail recursive call */
- SWAPOUT;
- erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_TAIL_CALL);
- SWAPIN;
- } else {
- SWAPOUT;
- erts_trace_time_break(c_p, I, bdt, ERTS_BP_CALL_TIME_CALL);
-
- /* r register needs to be copied to the array
- * for the garbage collector
- */
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- if (E - 2 < HTOP) {
- reg[0] = r(0);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, 2, reg, I[-1]);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- }
- SWAPIN;
-
- ASSERT(c_p->htop <= E && E <= c_p->hend);
-
- E -= 2;
- E[0] = make_cp(I);
- E[1] = make_cp(c_p->cp); /* original return address */
- c_p->cp = beam_return_time_trace;
- }
- }
-
- Goto(real_I);
- }
-
OpCase(i_return_time_trace): {
BeamInstr *pc = (BeamInstr *) (UWord) E[0];
SWAPOUT;
- erts_trace_time_break(c_p, pc, NULL, ERTS_BP_CALL_TIME_RETURN);
+ erts_trace_time_return(c_p, pc);
SWAPIN;
c_p->cp = NULL;
SET_I((BeamInstr *) cp_val(E[1]));
@@ -4733,114 +4581,6 @@ void process_main(void)
Goto(*I);
}
- OpCase(i_trace_breakpoint):
- if (! IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
- BeamInstr real_I;
-
- ErtsBreakSkip(c_p, (BeamInstr *) I, &real_I);
- Goto(real_I);
- }
- /* Fall through to next case */
- OpCase(i_mtrace_breakpoint): {
- BeamInstr real_I;
- Uint32 flags;
- Eterm tracer_pid;
- Uint* cpp;
- int return_to_trace = 0, need = 0;
- flags = 0;
- SWAPOUT;
- reg[0] = r(0);
-
- if (*(c_p->cp) == (BeamInstr) OpCode(return_trace)) {
- cpp = &E[2];
- } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_to_trace)) {
- return_to_trace = !0;
- cpp = &E[0];
- } else if (*(c_p->cp) == (BeamInstr) OpCode(i_return_time_trace)) {
- return_to_trace = !0;
- cpp = &E[0];
- } else {
- cpp = NULL;
- }
- if (cpp) {
- /* This _IS_ a tail recursive call, if there are
- * return_trace and/or i_return_to_trace stackframes
- * on the stack, they are not intermixed with y registers
- */
- BeamInstr *cp_save = c_p->cp;
- for (;;) {
- ASSERT(is_CP(*cpp));
- if (*cp_val(*cpp) == (BeamInstr) OpCode(return_trace)) {
- cpp += 3;
- } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_to_trace)) {
- return_to_trace = !0;
- cpp += 1;
- } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_time_trace)) {
- cpp += 2;
- } else
- break;
- }
- c_p->cp = (BeamInstr *) cp_val(*cpp);
- ASSERT(is_CP(*cpp));
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- real_I = erts_trace_break(c_p, I, reg, &flags, &tracer_pid);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- SWAPIN; /* Needed by shared heap. */
- c_p->cp = cp_save;
- } else {
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- real_I = erts_trace_break(c_p, I, reg, &flags, &tracer_pid);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- SWAPIN; /* Needed by shared heap. */
- }
-
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
-
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
- need += 1;
- }
- if (flags & MATCH_SET_RX_TRACE) {
- need += 3;
- }
- if (need) {
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- if (E - need < HTOP) {
- /* SWAPOUT was done and r(0) was saved above */
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, need, reg, I[-1]);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- r(0) = reg[0];
- SWAPIN;
- }
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
- 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;
- }
- if (flags & MATCH_SET_RX_TRACE) {
- E -= 3;
- ASSERT(c_p->htop <= E && E <= c_p->hend);
- ASSERT(is_CP((Eterm) (UWord) (I - 3)));
- ASSERT(am_true == tracer_pid ||
- is_internal_pid(tracer_pid) || is_internal_port(tracer_pid));
- E[2] = make_cp(c_p->cp);
- E[1] = tracer_pid;
- E[0] = make_cp(I - 3); /* We ARE at the beginning of an
- instruction,
- the funcinfo is above i. */
- 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_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- Goto(real_I);
- }
-
OpCase(i_return_to_trace): {
if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) {
Uint *cpp = (Uint*) E;
@@ -5110,9 +4850,6 @@ void process_main(void)
c_p->arity = 1; /* One living register (the 'true' return value) */
SWAPOUT;
c_p->i = I + 1; /* Next instruction */
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
- erts_add_to_runq(c_p);
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
c_p->current = NULL;
goto do_schedule;
}
@@ -5193,7 +4930,6 @@ void process_main(void)
#endif /* NO_JUMP_TABLE */
em_call_error_handler = OpCode(call_error_handler);
- em_call_traced_function = OpCode(call_traced_function);
em_apply_bif = OpCode(apply_bif);
beam_apply[0] = (BeamInstr) OpCode(i_apply);
@@ -5234,7 +4970,7 @@ void process_main(void)
save_calls(c_p, (Export *) Arg(0));
- SET_I(((Export *) Arg(0))->address);
+ SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]);
dis_next = (Eterm *) *I;
FCALLS--;
@@ -5510,7 +5246,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);
@@ -5911,7 +5647,8 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func)
/*
* Search for the error_handler module.
*/
- ep = erts_find_function(erts_proc_get_error_handler(p), func, 3);
+ ep = erts_find_function(erts_proc_get_error_handler(p), func, 3,
+ erts_active_code_ix());
if (ep == NULL) { /* No error handler */
p->current = fi;
p->freason = EXC_UNDEF;
@@ -5941,7 +5678,7 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func)
reg[0] = fi[0];
reg[1] = fi[1];
reg[2] = args;
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
@@ -5955,7 +5692,7 @@ apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity,
* there is no error handler module.
*/
- if ((ep = erts_find_export_entry(erts_proc_get_error_handler(p),
+ if ((ep = erts_active_export_entry(erts_proc_get_error_handler(p),
am_undefined_function, 3)) == NULL) {
return NULL;
} else {
@@ -6062,7 +5799,7 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
* Note: All BIFs have export entries; thus, no special case is needed.
*/
- if ((ep = erts_find_export_entry(module, function, arity)) == NULL) {
+ if ((ep = erts_active_export_entry(module, function, arity)) == NULL) {
if ((ep = apply_setup_error_handler(p, module, function, arity, reg)) == NULL) goto error;
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
save_calls(p, ep);
@@ -6070,11 +5807,11 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr *fptr = (BeamInstr *) ep->address;
+ BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()];
DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]);
}
#endif
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
static BeamInstr*
@@ -6116,7 +5853,7 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
* Note: All BIFs have export entries; thus, no special case is needed.
*/
- if ((ep = erts_find_export_entry(module, function, arity)) == NULL) {
+ if ((ep = erts_active_export_entry(module, function, arity)) == NULL) {
if ((ep = apply_setup_error_handler(p, module, function, arity, reg)) == NULL)
goto error;
} else if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
@@ -6125,11 +5862,11 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
#ifdef USE_VM_CALL_PROBES
if (DTRACE_ENABLED(global_function_entry)) {
- BeamInstr *fptr = (BeamInstr *) ep->address;
+ BeamInstr *fptr = (BeamInstr *) ep->addressv[erts_active_code_ix()];
DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]);
}
#endif
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
int
@@ -6206,9 +5943,7 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re
*/
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
- if (c_p->msg.len > 0) {
- erts_add_to_runq(c_p);
- } else {
+ if (!c_p->msg.len) {
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
c_p->fvalue = NIL;
PROCESS_MAIN_CHK_LOCKS(c_p);
@@ -6216,14 +5951,12 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
#ifdef ERTS_SMP
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
- if (c_p->msg.len > 0)
- erts_add_to_runq(c_p);
- else
+ if (!c_p->msg.len)
#endif
- c_p->status = P_WAITING;
+ erts_smp_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE);
+ ASSERT(!ERTS_PROC_IS_EXITING(c_p));
}
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
c_p->current = bif_export[BIF_hibernate_3]->code;
@@ -6240,7 +5973,6 @@ call_fun(Process* p, /* Current process. */
Eterm fun = reg[arity];
Eterm hdr;
int i;
- Eterm function;
Eterm* hp;
if (!is_boxed(fun)) {
@@ -6311,7 +6043,7 @@ call_fun(Process* p, /* Current process. */
Export* ep;
Module* modp;
Eterm module;
-
+ ErtsCodeIndex code_ix = erts_active_code_ix();
/*
* No arity. There is no module loaded that defines the fun,
@@ -6319,9 +6051,9 @@ call_fun(Process* p, /* Current process. */
* representation (the module has never been loaded),
* or the module defining the fun has been unloaded.
*/
-
module = fe->module;
- if ((modp = erts_get_module(module)) != NULL && modp->code != NULL) {
+ if ((modp = erts_get_module(module, code_ix)) != NULL
+ && modp->curr.code != NULL) {
/*
* There is a module loaded, but obviously the fun is not
* defined in it. We must not call the error_handler
@@ -6336,7 +6068,7 @@ call_fun(Process* p, /* Current process. */
*/
ep = erts_find_function(erts_proc_get_error_handler(p),
- am_undefined_lambda, 3);
+ am_undefined_lambda, 3, code_ix);
if (ep == NULL) { /* No error handler */
p->current = NULL;
p->freason = EXC_UNDEF;
@@ -6346,7 +6078,7 @@ call_fun(Process* p, /* Current process. */
reg[1] = fun;
reg[2] = args;
reg[3] = NIL;
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
}
}
} else if (is_export_header(hdr)) {
@@ -6358,7 +6090,7 @@ call_fun(Process* p, /* Current process. */
if (arity == actual_arity) {
DTRACE_GLOBAL_CALL(p, ep->code[0], ep->code[1], (Uint)ep->code[2]);
- return ep->address;
+ return ep->addressv[erts_active_code_ix()];
} else {
/*
* Wrong arity. First build a list of the arguments.
@@ -6378,63 +6110,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_find_export_entry(module, function, arity)) == NULL) {
- ep = erts_find_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->address;
} else {
badfun:
p->current = NULL;
@@ -6500,7 +6175,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
@@ -6537,7 +6212,8 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
if ((ep = export_get(&e)) == NULL) {
return 0;
}
- return ep->address == ep->code+3 && (ep->code[3] == (BeamInstr) em_apply_bif);
+ return ep->addressv[erts_active_code_ix()] == ep->code+3
+ && (ep->code[3] == (BeamInstr) em_apply_bif);
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index dd788df6e4..bd4e5a52d0 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -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
@@ -205,7 +205,7 @@ typedef struct {
typedef struct {
Eterm term; /* The tagged term (in the heap). */
Uint heap_size; /* (Exact) size on the heap. */
- Uint offset; /* Offset from temporary location to final. */
+ SWord offset; /* Offset from temporary location to final. */
ErlOffHeap off_heap; /* Start of linked list of ProcBins. */
Eterm* heap; /* Heap for term. */
} Literal;
@@ -352,27 +352,6 @@ typedef struct LoaderState {
int loc_size; /* Size of location info in bytes (2/4) */
} LoaderState;
-/*
- * Layout of the line table.
- */
-
-#define MI_LINE_FNAME_PTR 0
-#define MI_LINE_LOC_TAB 1
-#define MI_LINE_LOC_SIZE 2
-#define MI_LINE_FUNC_TAB 3
-
-#define LINE_INVALID_LOCATION (0)
-
-/*
- * Macros for manipulating locations.
- */
-
-#define IS_VALID_LOCATION(File, Line) \
- ((unsigned) (File) < 255 && (unsigned) (Line) < ((1 << 24) - 1))
-#define MAKE_LOCATION(File, Line) (((File) << 24) | (Line))
-#define LOC_FILE(Loc) ((Loc) >> 24)
-#define LOC_LINE(Loc) ((Loc) & ((1 << 24)-1))
-
#define GetTagAndValue(Stp, Tag, Val) \
do { \
BeamInstr __w; \
@@ -496,7 +475,8 @@ typedef struct LoaderState {
} while (0)
-static void free_state(LoaderState* stp);
+static void free_loader_state(Binary* magic);
+static void loader_state_dtor(Binary* magic);
static Eterm insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
Eterm group_leader, Eterm module,
BeamInstr* code, Uint size);
@@ -507,6 +487,7 @@ static int verify_chunks(LoaderState* stp);
static int load_atom_table(LoaderState* stp);
static int load_import_table(LoaderState* stp);
static int read_export_table(LoaderState* stp);
+static int is_bif(Eterm mod, Eterm func, unsigned arity);
static int read_lambda_table(LoaderState* stp);
static int read_literal_table(LoaderState* stp);
static int read_line_table(LoaderState* stp);
@@ -548,22 +529,9 @@ static Eterm native_addresses(Process* p, Eterm mod);
int patch_funentries(Eterm Patchlist);
int patch(Eterm Addresses, Uint fe);
static int safe_mul(UWord a, UWord b, UWord* resp);
-static void lookup_loc(FunctionInfo* fi, BeamInstr* pc,
- BeamInstr* modp, int idx);
-
static int must_swap_floats;
-/*
- * The following variables keep a sorted list of address ranges for
- * each module. It allows us to quickly find a function given an
- * instruction pointer.
- */
-Range* modules = NULL; /* Sorted lists of module addresses. */
-int num_loaded_modules; /* Number of loaded modules. */
-int allocated_modules; /* Number of slots allocated. */
-Range* mid_module = NULL; /* Cached search start point */
-
Uint erts_total_code_size;
/**********************************************************************/
@@ -579,11 +547,7 @@ void init_load(void)
f.fd = 1.0;
must_swap_floats = (f.fw[0] == 0);
- allocated_modules = 128;
- modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS,
- allocated_modules*sizeof(Range));
- mid_module = modules;
- num_loaded_modules = 0;
+ erts_init_ranges();
}
static void
@@ -595,7 +559,7 @@ define_file(LoaderState* stp, char* name, int idx)
}
Eterm
-erts_load_module(Process *c_p,
+erts_preload_module(Process *c_p,
ErtsProcLocks c_p_locks,
Eterm group_leader, /* Group leader or NIL if none. */
Eterm* modp, /*
@@ -605,15 +569,16 @@ erts_load_module(Process *c_p,
byte* code, /* Points to the code to load */
Uint size) /* Size of code to load. */
{
- LoaderState* stp = erts_alloc_loader_state();
+ Binary* magic = erts_alloc_loader_state();
Eterm retval;
- retval = erts_prepare_loading(stp, c_p, group_leader, modp,
+ ASSERT(!erts_initialized);
+ retval = erts_prepare_loading(magic, c_p, group_leader, modp,
code, size);
if (retval != NIL) {
return retval;
}
- return erts_finish_loading(stp, c_p, c_p_locks, modp);
+ return erts_finish_loading(magic, c_p, c_p_locks, modp);
}
/* #define LOAD_MEMORY_HARD_DEBUG 1*/
@@ -629,11 +594,13 @@ extern void check_allocated_block(Uint type, void *blk);
#endif
Eterm
-erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
+erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,
Eterm* modp, byte* code, Uint unloaded_size)
{
Eterm retval = am_badfile;
+ LoaderState* stp;
+ stp = ERTS_MAGIC_BIN_DATA(magic);
stp->module = *modp;
stp->group_leader = group_leader;
@@ -666,7 +633,7 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
/*
* Initialize code area.
*/
- stp->code_buffer_size = erts_next_heap_size(2048 + stp->num_functions, 0);
+ stp->code_buffer_size = 2048 + stp->num_functions;
stp->code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE,
sizeof(BeamInstr) * stp->code_buffer_size);
@@ -679,8 +646,6 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
stp->code[MI_COMPILE_PTR] = 0;
stp->code[MI_COMPILE_SIZE] = 0;
stp->code[MI_COMPILE_SIZE_ON_HEAP] = 0;
- stp->code[MI_NUM_BREAKPOINTS] = 0;
-
/*
* Read the atom table.
@@ -774,23 +739,24 @@ erts_prepare_loading(LoaderState* stp, Process *c_p, Eterm group_leader,
load_error:
if (retval != NIL) {
- free_state(stp);
+ free_loader_state(magic);
}
return retval;
}
Eterm
-erts_finish_loading(LoaderState* stp, Process* c_p,
+erts_finish_loading(Binary* magic, Process* c_p,
ErtsProcLocks c_p_locks, Eterm* modp)
{
Eterm retval;
+ LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic);
/*
* No other process may run since we will update the export
* table which is not protected by any locks.
*/
- ERTS_SMP_LC_ASSERT(erts_initialized == 0 ||
+ ERTS_SMP_LC_ASSERT(erts_initialized == 0 || erts_has_code_write_permission() ||
erts_smp_thr_progress_is_blocking());
/*
@@ -811,7 +777,6 @@ erts_finish_loading(LoaderState* stp, Process* c_p,
* exported and imported functions. This can't fail.
*/
- erts_export_consolidate();
CHKBLK(ERTS_ALC_T_CODE,stp->code);
final_touch(stp);
@@ -837,16 +802,20 @@ erts_finish_loading(LoaderState* stp, Process* c_p,
}
load_error:
- free_state(stp);
+ free_loader_state(magic);
return retval;
}
-LoaderState*
+Binary*
erts_alloc_loader_state(void)
{
LoaderState* stp;
+ Binary* magic;
- stp = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LoaderState));
+ magic = erts_create_magic_binary(sizeof(LoaderState),
+ loader_state_dtor);
+ erts_refc_inc(&magic->refc, 1);
+ stp = ERTS_MAGIC_BIN_DATA(magic);
stp->bin = NULL;
stp->function = THE_NON_VALUE; /* Function not known yet */
stp->arity = 0;
@@ -875,76 +844,123 @@ erts_alloc_loader_state(void)
stp->line_instr = 0;
stp->func_line = 0;
stp->fname = 0;
- return stp;
+ return magic;
+}
+
+/*
+ * Return the module name (a tagged atom) for the prepared code
+ * in the magic binary, or NIL if the binary does not contain
+ * prepared code.
+ */
+Eterm
+erts_module_for_prepared_code(Binary* magic)
+{
+ LoaderState* stp;
+
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(magic) != loader_state_dtor) {
+ return NIL;
+ }
+ stp = ERTS_MAGIC_BIN_DATA(magic);
+ if (stp->code != 0) {
+ return stp->module;
+ } else {
+ return NIL;
+ }
}
static void
-free_state(LoaderState* stp)
+free_loader_state(Binary* magic)
{
+ loader_state_dtor(magic);
+ if (erts_refc_dectest(&magic->refc, 0) == 0) {
+ erts_bin_free(magic);
+ }
+}
+
+/*
+ * This destructor function can safely be called multiple times.
+ */
+static void
+loader_state_dtor(Binary* magic)
+{
+ LoaderState* stp = ERTS_MAGIC_BIN_DATA(magic);
+
if (stp->bin != 0) {
driver_free_binary(stp->bin);
+ stp->bin = 0;
}
if (stp->code != 0) {
erts_free(ERTS_ALC_T_CODE, stp->code);
+ stp->code = 0;
}
- if (stp->labels != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->labels);
+ if (stp->labels != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->labels);
+ stp->labels = 0;
}
- if (stp->atom != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->atom);
+ if (stp->atom != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->atom);
+ stp->atom = 0;
}
- if (stp->import != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->import);
+ if (stp->import != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->import);
+ stp->import = 0;
}
- if (stp->export != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->export);
+ if (stp->export != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->export);
+ stp->export = 0;
}
if (stp->lambdas != stp->def_lambdas) {
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->lambdas);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->lambdas);
+ stp->lambdas = stp->def_lambdas;
}
- if (stp->literals != NULL) {
+ if (stp->literals != 0) {
int i;
for (i = 0; i < stp->num_literals; i++) {
- if (stp->literals[i].heap != NULL) {
- erts_free(ERTS_ALC_T_LOADER_TMP,
+ if (stp->literals[i].heap != 0) {
+ erts_free(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->literals[i].heap);
+ stp->literals[i].heap = 0;
}
}
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->literals);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literals);
+ stp->literals = 0;
}
- while (stp->literal_patches != NULL) {
+ while (stp->literal_patches != 0) {
LiteralPatch* next = stp->literal_patches->next;
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->literal_patches);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->literal_patches);
stp->literal_patches = next;
}
- while (stp->string_patches != NULL) {
+ while (stp->string_patches != 0) {
StringPatch* next = stp->string_patches->next;
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->string_patches);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) stp->string_patches);
stp->string_patches = next;
}
- while (stp->genop_blocks) {
- GenOpBlock* next = stp->genop_blocks->next;
- erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->genop_blocks);
- stp->genop_blocks = next;
- }
if (stp->line_item != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->line_item);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->line_item);
+ stp->line_item = 0;
}
if (stp->line_instr != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->line_instr);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->line_instr);
+ stp->line_instr = 0;
}
if (stp->func_line != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->func_line);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->func_line);
+ stp->func_line = 0;
}
if (stp->fname != 0) {
- erts_free(ERTS_ALC_T_LOADER_TMP, stp->fname);
+ erts_free(ERTS_ALC_T_PREPARED_CODE, stp->fname);
+ stp->fname = 0;
}
- erts_free(ERTS_ALC_T_LOADER_TMP, stp);
+ /*
+ * The following data items should have been freed earlier.
+ */
+
+ ASSERT(stp->genop_blocks == 0);
}
static Eterm
@@ -954,7 +970,6 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
{
Module* modp;
Eterm retval;
- int i;
if ((retval = beam_make_current_old(c_p, c_p_locks, module)) != NIL) {
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
@@ -971,30 +986,15 @@ insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
erts_total_code_size += size;
modp = erts_put_module(module);
- modp->code = code;
- modp->code_length = size;
- modp->catches = BEAM_CATCHES_NIL; /* Will be filled in later. */
+ modp->curr.code = code;
+ modp->curr.code_length = size;
+ modp->curr.catches = BEAM_CATCHES_NIL; /* Will be filled in later. */
/*
- * Update address table (used for finding a function from a PC value).
+ * Update ranges (used for finding a function from a PC value).
*/
- if (num_loaded_modules == allocated_modules) {
- allocated_modules *= 2;
- modules = (Range *) erts_realloc(ERTS_ALC_T_MODULE_REFS,
- (void *) modules,
- allocated_modules * sizeof(Range));
- }
- for (i = num_loaded_modules; i > 0; i--) {
- if (code > modules[i-1].start) {
- break;
- }
- modules[i] = modules[i-1];
- }
- modules[i].start = code;
- modules[i].end = (BeamInstr *) (((byte *)code) + size);
- num_loaded_modules++;
- mid_module = &modules[num_loaded_modules/2];
+ erts_update_ranges(code, size);
return NIL;
}
@@ -1217,9 +1217,8 @@ load_atom_table(LoaderState* stp)
GetInt(stp, 4, stp->num_atoms);
stp->num_atoms++;
- stp->atom = erts_alloc(ERTS_ALC_T_LOADER_TMP,
- erts_next_heap_size((stp->num_atoms*sizeof(Eterm)),
- 0));
+ stp->atom = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_atoms*sizeof(Eterm));
/*
* Read all atoms.
@@ -1231,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);
}
/*
@@ -1241,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]));
@@ -1263,10 +1262,8 @@ load_import_table(LoaderState* stp)
int i;
GetInt(stp, 4, stp->num_imports);
- stp->import = erts_alloc(ERTS_ALC_T_LOADER_TMP,
- erts_next_heap_size((stp->num_imports *
- sizeof(ImportEntry)),
- 0));
+ stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_imports * sizeof(ImportEntry));
for (i = 0; i < stp->num_imports; i++) {
int n;
Eterm mod;
@@ -1296,7 +1293,7 @@ load_import_table(LoaderState* stp)
* If the export entry refers to a BIF, get the pointer to
* the BIF function.
*/
- if ((e = erts_find_export_entry(mod, func, arity)) != NULL) {
+ if ((e = erts_active_export_entry(mod, func, arity)) != NULL) {
if (e->code[3] == (BeamInstr) em_apply_bif) {
stp->import[i].bf = (BifFunction) e->code[4];
if (func == am_load_nif && mod == am_erlang && arity == 2) {
@@ -1315,16 +1312,8 @@ load_import_table(LoaderState* stp)
static int
read_export_table(LoaderState* stp)
{
- static struct {
- Eterm mod;
- Eterm func;
- int arity;
- } allow_redef[] = {
- /* The BIFs that are allowed to be redefined by Erlang code */
- {am_erlang,am_apply,2},
- {am_erlang,am_apply,3},
- };
int i;
+ BeamInstr* address;
GetInt(stp, 4, stp->num_exps);
if (stp->num_exps > stp->num_functions) {
@@ -1332,7 +1321,7 @@ read_export_table(LoaderState* stp)
stp->num_exps, stp->num_functions);
}
stp->export
- = (ExportEntry *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ = (ExportEntry *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
(stp->num_exps * sizeof(ExportEntry)));
for (i = 0; i < stp->num_exps; i++) {
@@ -1340,7 +1329,6 @@ read_export_table(LoaderState* stp)
Uint value;
Eterm func;
Uint arity;
- Export* e;
GetInt(stp, 4, n);
GetAtom(stp, n, func);
@@ -1358,29 +1346,34 @@ read_export_table(LoaderState* stp)
if (value == 0) {
LoadError2(stp, "export table entry %d: label %d not resolved", i, n);
}
- stp->export[i].address = stp->code + value;
+ stp->export[i].address = address = stp->code + value;
/*
- * Check that we are not redefining a BIF (except the ones allowed to
- * redefine).
+ * Find out if there is a BIF with the same name.
*/
- if ((e = erts_find_export_entry(stp->module, func, arity)) != NULL) {
- if (e->code[3] == (BeamInstr) em_apply_bif) {
- int j;
- for (j = 0; j < sizeof(allow_redef)/sizeof(allow_redef[0]); j++) {
- if (stp->module == allow_redef[j].mod &&
- func == allow_redef[j].func &&
- arity == allow_redef[j].arity) {
- break;
- }
- }
- if (j == sizeof(allow_redef)/sizeof(allow_redef[0])) {
- LoadError2(stp, "exported function %T/%d redefines BIF",
- func, arity);
- }
- }
+ if (!is_bif(stp->module, func, arity)) {
+ continue;
+ }
+
+ /*
+ * This is a stub for a BIF.
+ *
+ * It should not be exported, and the information in its
+ * func_info instruction should be invalidated so that it
+ * can be filtered out by module_info(functions) and by
+ * any other functions that walk through all local functions.
+ */
+
+ if (stp->labels[n].patches) {
+ LoadError3(stp, "there are local calls to the stub for "
+ "the BIF %T:%T/%d",
+ stp->module, func, arity);
}
+ stp->export[i].address = NULL;
+ address[-1] = 0;
+ address[-2] = NIL;
+ address[-3] = NIL;
}
return 1;
@@ -1388,15 +1381,39 @@ read_export_table(LoaderState* stp)
return 0;
}
+
+static int
+is_bif(Eterm mod, Eterm func, unsigned arity)
+{
+ Export* e = erts_active_export_entry(mod, func, arity);
+ if (e == NULL) {
+ return 0;
+ }
+ if (e->code[3] != (BeamInstr) em_apply_bif) {
+ return 0;
+ }
+ if (mod == am_erlang && func == am_apply && arity == 3) {
+ /*
+ * erlang:apply/3 is a special case -- it is implemented
+ * as an instruction and it is OK to redefine it.
+ */
+ return 0;
+ }
+ return 1;
+}
+
static int
read_lambda_table(LoaderState* stp)
{
int i;
GetInt(stp, 4, stp->num_lambdas);
- stp->lambdas_allocated = stp->num_lambdas;
- stp->lambdas = (Lambda *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
- stp->num_lambdas * sizeof(Lambda));
+ if (stp->num_lambdas > stp->lambdas_allocated) {
+ ASSERT(stp->lambdas == stp->def_lambdas);
+ stp->lambdas_allocated = stp->num_lambdas;
+ stp->lambdas = (Lambda *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ stp->num_lambdas * sizeof(Lambda));
+ }
for (i = 0; i < stp->num_lambdas; i++) {
Uint n;
Uint32 Index;
@@ -1446,7 +1463,7 @@ read_literal_table(LoaderState* stp)
stp->file_p = uncompressed;
stp->file_left = (unsigned) uncompressed_sz;
GetInt(stp, 4, stp->num_literals);
- stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_literals * sizeof(Literal));
stp->allocated_literals = stp->num_literals;
@@ -1466,7 +1483,7 @@ read_literal_table(LoaderState* stp)
if ((heap_size = erts_decode_ext_size(p, sz)) < 0) {
LoadError1(stp, "literal %d: bad external format", i);
}
- hp = stp->literals[i].heap = erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ hp = stp->literals[i].heap = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
heap_size*sizeof(Eterm));
stp->literals[i].off_heap.first = 0;
stp->literals[i].off_heap.overhead = 0;
@@ -1538,7 +1555,7 @@ read_line_table(LoaderState* stp)
*/
num_line_items++;
- lp = (BeamInstr *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ lp = (BeamInstr *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
num_line_items * sizeof(BeamInstr));
stp->line_item = lp;
stp->num_line_items = num_line_items;
@@ -1594,7 +1611,7 @@ read_line_table(LoaderState* stp)
*/
if (stp->num_fnames != 0) {
- stp->fname = (Eterm *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->fname = (Eterm *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_fnames *
sizeof(Eterm));
for (i = 0; i < stp->num_fnames; i++) {
@@ -1603,18 +1620,18 @@ 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);
}
}
/*
* Allocate the arrays to be filled while code is being loaded.
*/
- stp->line_instr = (LineInstr *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->line_instr = (LineInstr *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_line_instrs *
sizeof(LineInstr));
stp->current_li = 0;
- stp->func_line = (int *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_functions *
sizeof(int));
@@ -1677,7 +1694,7 @@ read_code_header(LoaderState* stp)
* Initialize label table.
*/
- stp->labels = (Label *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->labels = (Label *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_labels * sizeof(Label));
for (i = 0; i < stp->num_labels; i++) {
stp->labels[i].value = 0;
@@ -1703,9 +1720,9 @@ read_code_header(LoaderState* stp)
#define CodeNeed(w) do { \
ASSERT(ci <= code_buffer_size); \
if (code_buffer_size < ci+(w)) { \
- code_buffer_size = erts_next_heap_size(ci+(w), 0); \
- stp->code = code \
- = (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \
+ code_buffer_size = 2*ci+(w); \
+ stp->code = code = \
+ (BeamInstr *) erts_realloc(ERTS_ALC_T_CODE, \
(void *) code, \
code_buffer_size * sizeof(BeamInstr)); \
} \
@@ -1731,6 +1748,7 @@ load_code(LoaderState* stp)
GenOp* last_op = NULL;
GenOp** last_op_next = NULL;
int arity;
+ int retval = 1;
/*
* The size of the loaded func_info instruction is needed
@@ -1835,7 +1853,10 @@ load_code(LoaderState* stp)
unsigned tag;
switch (last_op->a[arg].val) {
- case 0: /* Floating point number */
+ case 0:
+ /* Floating point number.
+ * Not generated by the compiler in R16B and later.
+ */
{
Eterm* hp;
/* XXX:PaN - Halfword should use ARCH_64 variant instead */
@@ -2457,7 +2478,11 @@ load_code(LoaderState* stp)
case op_int_code_end:
stp->code_buffer_size = code_buffer_size;
stp->ci = ci;
- return 1;
+ stp->function = THE_NON_VALUE;
+ stp->genop = NULL;
+ stp->specific_op = -1;
+ retval = 1;
+ goto cleanup;
}
/*
@@ -2471,9 +2496,20 @@ load_code(LoaderState* stp)
}
}
-
load_error:
- return 0;
+ retval = 0;
+
+ cleanup:
+ /*
+ * Clean up everything that is not needed any longer.
+ */
+
+ while (stp->genop_blocks) {
+ GenOpBlock* next = stp->genop_blocks->next;
+ erts_free(ERTS_ALC_T_LOADER_TMP, (void *) stp->genop_blocks);
+ stp->genop_blocks = next;
+ }
+ return retval;
}
@@ -4009,7 +4045,7 @@ freeze_code(LoaderState* stp)
code[MI_LITERALS_END] = (BeamInstr) high;
ptr = low;
for (i = 0; i < stp->num_literals; i++) {
- Uint offset;
+ SWord offset;
struct erl_off_heap_header* t_off_heap;
sys_memcpy(ptr, stp->literals[i].heap,
@@ -4265,24 +4301,31 @@ final_touch(LoaderState* stp)
index = next;
}
modp = erts_put_module(stp->module);
- modp->catches = catches;
+ modp->curr.catches = catches;
/*
* Export functions.
*/
for (i = 0; i < stp->num_exps; i++) {
- Export* ep = erts_export_put(stp->module, stp->export[i].function,
- stp->export[i].arity);
+ Export* ep;
+ BeamInstr* address = stp->export[i].address;
+
+ if (address == NULL) {
+ /* Skip stub for a BIF */
+ continue;
+ }
+ ep = erts_export_put(stp->module, stp->export[i].function,
+ stp->export[i].arity);
if (!on_load) {
- ep->address = stp->export[i].address;
+ ep->addressv[erts_staging_code_ix()] = address;
} else {
/*
* Don't make any of the exported functions
* callable yet.
*/
- ep->address = ep->code+3;
- ep->code[4] = (BeamInstr) stp->export[i].address;
+ ep->addressv[erts_staging_code_ix()] = ep->code+3;
+ ep->code[4] = (BeamInstr) address;
}
}
@@ -4926,7 +4969,7 @@ new_label(LoaderState* stp)
int num = stp->num_labels;
stp->num_labels++;
- stp->labels = (Label *) erts_realloc(ERTS_ALC_T_LOADER_TMP,
+ stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->labels,
stp->num_labels * sizeof(Label));
stp->labels[num].value = 0;
@@ -4937,7 +4980,8 @@ new_label(LoaderState* stp)
static void
new_literal_patch(LoaderState* stp, int pos)
{
- LiteralPatch* p = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LiteralPatch));
+ LiteralPatch* p = erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ sizeof(LiteralPatch));
p->pos = pos;
p->next = stp->literal_patches;
stp->literal_patches = p;
@@ -4946,7 +4990,7 @@ new_literal_patch(LoaderState* stp, int pos)
static void
new_string_patch(LoaderState* stp, int pos)
{
- StringPatch* p = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(StringPatch));
+ StringPatch* p = erts_alloc(ERTS_ALC_T_PREPARED_CODE, sizeof(StringPatch));
p->pos = pos;
p->next = stp->string_patches;
stp->string_patches = p;
@@ -4964,14 +5008,14 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
ASSERT(stp->num_literals == 0);
stp->allocated_literals = 8;
need = stp->allocated_literals * sizeof(Literal);
- stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ stp->literals = (Literal *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
need);
} else if (stp->allocated_literals <= stp->num_literals) {
Uint need;
stp->allocated_literals *= 2;
need = stp->allocated_literals * sizeof(Literal);
- stp->literals = (Literal *) erts_realloc(ERTS_ALC_T_LOADER_TMP,
+ stp->literals = (Literal *) erts_realloc(ERTS_ALC_T_PREPARED_CODE,
(void *) stp->literals,
need);
}
@@ -4980,7 +5024,7 @@ new_literal(LoaderState* stp, Eterm** hpp, Uint heap_size)
lit = stp->literals + stp->num_literals;
lit->offset = 0;
lit->heap_size = heap_size;
- lit->heap = erts_alloc(ERTS_ALC_T_LOADER_TMP, heap_size*sizeof(Eterm));
+ lit->heap = erts_alloc(ERTS_ALC_T_PREPARED_CODE, heap_size*sizeof(Eterm));
lit->term = make_boxed(lit->heap);
lit->off_heap.first = 0;
lit->off_heap.overhead = 0;
@@ -4999,7 +5043,7 @@ erts_module_info_0(Process* p, Eterm module)
return THE_NON_VALUE;
}
- if (erts_get_module(module) == NULL) {
+ if (erts_get_module(module, erts_active_code_ix()) == NULL) {
return THE_NON_VALUE;
}
@@ -5054,32 +5098,43 @@ functions_in_module(Process* p, /* Process whose heap to use. */
BeamInstr* code;
int i;
Uint num_functions;
+ Uint need;
Eterm* hp;
+ Eterm* hp_end;
Eterm result = NIL;
if (is_not_atom(mod)) {
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
num_functions = code[MI_NUM_FUNCTIONS];
- hp = HAlloc(p, 5*num_functions);
+ need = 5*num_functions;
+ hp = HAlloc(p, need);
+ hp_end = hp + need;
for (i = num_functions-1; i >= 0 ; i--) {
BeamInstr* func_info = (BeamInstr *) code[MI_FUNCTIONS+i];
Eterm name = (Eterm) func_info[3];
int arity = (int) func_info[4];
Eterm tuple;
- ASSERT(is_atom(name));
- tuple = TUPLE2(hp, name, make_small(arity));
- hp += 3;
- result = CONS(hp, tuple, result);
- hp += 2;
+ /*
+ * If the function name is [], this entry is a stub for
+ * a BIF that should be ignored.
+ */
+ ASSERT(is_atom(name) || is_nil(name));
+ if (is_atom(name)) {
+ tuple = TUPLE2(hp, name, make_small(arity));
+ hp += 3;
+ result = CONS(hp, tuple, result);
+ hp += 2;
+ }
}
+ HRelease(p, hp_end, hp);
return result;
}
@@ -5106,12 +5161,12 @@ native_addresses(Process* p, Eterm mod)
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
num_functions = code[MI_NUM_FUNCTIONS];
need = (6+BIG_UINT_HEAP_SIZE)*num_functions;
hp = HAlloc(p, need);
@@ -5122,9 +5177,11 @@ native_addresses(Process* p, Eterm mod)
int arity = (int) func_info[4];
Eterm tuple;
- ASSERT(is_atom(name));
+ ASSERT(is_atom(name) || is_nil(name)); /* [] if BIF stub */
if (func_info[1] != 0) {
- Eterm addr = erts_bld_uint(&hp, NULL, func_info[1]);
+ Eterm addr;
+ ASSERT(is_atom(name));
+ addr = erts_bld_uint(&hp, NULL, func_info[1]);
tuple = erts_bld_tuple(&hp, NULL, 3, name, make_small(arity), addr);
result = erts_bld_cons(&hp, NULL, tuple, result);
}
@@ -5149,18 +5206,20 @@ exported_from_module(Process* p, /* Process whose heap to use. */
Eterm* hp = NULL;
Eterm* hend = NULL;
Eterm result = NIL;
+ ErtsCodeIndex code_ix;
if (is_not_atom(mod)) {
return THE_NON_VALUE;
}
- for (i = 0; i < export_list_size(); i++) {
- Export* ep = export_list(i);
+ code_ix = erts_active_code_ix();
+ for (i = 0; i < export_list_size(code_ix); i++) {
+ Export* ep = export_list(i,code_ix);
if (ep->code[0] == mod) {
Eterm tuple;
- if (ep->address == ep->code+3 &&
+ if (ep->addressv[code_ix] == ep->code+3 &&
ep->code[3] == (BeamInstr) em_call_error_handler) {
/* There is a call to the function, but it does not exist. */
continue;
@@ -5204,11 +5263,11 @@ attributes_for_module(Process* p, /* Process whose heap to use. */
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
ext = (byte *) code[MI_ATTR_PTR];
if (ext != NULL) {
hp = HAlloc(p, code[MI_ATTR_SIZE_ON_HEAP]);
@@ -5244,11 +5303,11 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */
return THE_NON_VALUE;
}
- modp = erts_get_module(mod);
+ modp = erts_get_module(mod, erts_active_code_ix());
if (modp == NULL) {
return THE_NON_VALUE;
}
- code = modp->code;
+ code = modp->curr.code;
ext = (byte *) code[MI_COMPILE_PTR];
if (ext != NULL) {
hp = HAlloc(p, code[MI_COMPILE_SIZE_ON_HEAP]);
@@ -5263,113 +5322,6 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */
}
/*
- * Find a function from the given pc and fill information in
- * the FunctionInfo struct. If the full_info is non-zero, fill
- * in all available information (including location in the
- * source code). If no function is found, the 'current' field
- * will be set to NULL.
- */
-
-void
-erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
-{
- Range* low = modules;
- Range* high = low + num_loaded_modules;
- Range* mid = mid_module;
-
- fi->current = NULL;
- fi->needed = 5;
- fi->loc = LINE_INVALID_LOCATION;
- while (low < high) {
- if (pc < mid->start) {
- high = mid;
- } else if (pc > mid->end) {
- low = mid + 1;
- } else {
- BeamInstr** low1 = (BeamInstr **) (mid->start + MI_FUNCTIONS);
- BeamInstr** high1 = low1 + mid->start[MI_NUM_FUNCTIONS];
- BeamInstr** mid1;
-
- while (low1 < high1) {
- mid1 = low1 + (high1-low1) / 2;
- if (pc < mid1[0]) {
- high1 = mid1;
- } else if (pc < mid1[1]) {
- mid_module = mid;
- fi->current = mid1[0]+2;
- if (full_info) {
- BeamInstr** fp = (BeamInstr **) (mid->start +
- MI_FUNCTIONS);
- int idx = mid1 - fp;
- lookup_loc(fi, pc, mid->start, idx);
- }
- return;
- } else {
- low1 = mid1 + 1;
- }
- }
- return;
- }
- mid = low + (high-low) / 2;
- }
-}
-
-static void
-lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx)
-{
- Eterm* line = (Eterm *) modp[MI_LINE_TABLE];
- Eterm* low;
- Eterm* high;
- Eterm* mid;
- Eterm pc;
-
- if (line == 0) {
- return;
- }
-
- pc = (Eterm) (BeamInstr) orig_pc;
- fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR];
- low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx];
- high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1];
- while (high > low) {
- mid = low + (high-low) / 2;
- if (pc < mid[0]) {
- high = mid;
- } else if (pc < mid[1]) {
- int file;
- int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB];
-
- if (line[MI_LINE_LOC_SIZE] == 2) {
- Uint16* loc_table =
- (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB];
- fi->loc = loc_table[index];
- } else {
- Uint32* loc_table =
- (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB];
- ASSERT(line[MI_LINE_LOC_SIZE] == 4);
- fi->loc = loc_table[index];
- }
- if (fi->loc == LINE_INVALID_LOCATION) {
- return;
- }
- fi->needed += 3+2+3+2;
- file = LOC_FILE(fi->loc);
- if (file == 0) {
- /* Special case: Module name with ".erl" appended */
- Atom* mod_atom = atom_tab(atom_val(fi->current[0]));
- fi->needed += 2*(mod_atom->len+4);
- } else {
- Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
- fi->needed += 2*ap->len;
- }
- return;
- } else {
- low = mid + 1;
- }
- }
-}
-
-/*
* Build a single {M,F,A,Loction} item to be part of
* a stack trace.
*/
@@ -5449,6 +5401,7 @@ code_get_chunk_2(BIF_ALIST_2)
Process* p = BIF_P;
Eterm Bin = BIF_ARG_1;
Eterm Chunk = BIF_ARG_2;
+ Binary* magic = 0;
LoaderState* stp;
Uint chunk = 0;
ErlSubBin* sb;
@@ -5461,12 +5414,13 @@ code_get_chunk_2(BIF_ALIST_2)
Eterm real_bin;
byte* temp_alloc = NULL;
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
+ stp = ERTS_MAGIC_BIN_DATA(magic);
if ((start = erts_get_aligned_binary_bytes(Bin, &temp_alloc)) == NULL) {
error:
erts_free_aligned_binary_bytes(temp_alloc);
- if (stp) {
- free_state(stp);
+ if (magic) {
+ free_loader_state(magic);
}
BIF_ERROR(p, BADARG);
}
@@ -5511,7 +5465,7 @@ code_get_chunk_2(BIF_ALIST_2)
done:
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
return res;
}
@@ -5524,14 +5478,16 @@ code_module_md5_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm Bin = BIF_ARG_1;
+ Binary* magic;
LoaderState* stp;
byte* bytes;
byte* temp_alloc = NULL;
Eterm res;
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
+ stp = ERTS_MAGIC_BIN_DATA(magic);
if ((bytes = erts_get_aligned_binary_bytes(Bin, &temp_alloc)) == NULL) {
- free_state(stp);
+ free_loader_state(magic);
BIF_ERROR(p, BADARG);
}
stp->module = THE_NON_VALUE; /* Suppress diagnostiscs */
@@ -5545,7 +5501,7 @@ code_module_md5_1(BIF_ALIST_1)
done:
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
return res;
}
@@ -5574,7 +5530,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;
@@ -5585,7 +5542,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;
}
@@ -5601,7 +5559,7 @@ stub_read_export_table(LoaderState* stp)
stp->num_exps, stp->num_functions);
}
stp->export
- = (ExportEntry *) erts_alloc(ERTS_ALC_T_LOADER_TMP,
+ = (ExportEntry *) erts_alloc(ERTS_ALC_T_PREPARED_CODE,
stp->num_exps * sizeof(ExportEntry));
for (i = 0; i < stp->num_exps; i++) {
@@ -5627,20 +5585,29 @@ stub_final_touch(LoaderState* stp, BeamInstr* fp)
{
int i;
int n = stp->num_exps;
+ Eterm mod = fp[2];
Eterm function = fp[3];
int arity = fp[4];
#ifdef HIPE
Lambda* lp;
#endif
+ if (is_bif(mod, function, arity)) {
+ fp[1] = 0;
+ fp[2] = 0;
+ fp[3] = 0;
+ fp[4] = 0;
+ return;
+ }
+
/*
* Test if the function should be exported.
*/
for (i = 0; i < n; i++) {
if (stp->export[i].function == function && stp->export[i].arity == arity) {
- Export* ep = erts_export_put(fp[2], function, arity);
- ep->address = fp+5;
+ Export* ep = erts_export_put(mod, function, arity);
+ ep->addressv[erts_staging_code_ix()] = fp+5;
return;
}
}
@@ -5819,6 +5786,7 @@ patch_funentries(Eterm Patchlist)
Eterm
erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
{
+ Binary* magic;
LoaderState* stp;
BeamInstr Funcs;
BeamInstr Patchlist;
@@ -5840,7 +5808,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Must initialize stp->lambdas here because the error handling code
* at label 'error' uses it.
*/
- stp = erts_alloc_loader_state();
+ magic = erts_alloc_loader_state();
+ stp = ERTS_MAGIC_BIN_DATA(magic);
if (is_not_atom(Mod)) {
goto error;
@@ -5920,7 +5889,6 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
code[MI_COMPILE_PTR] = 0;
code[MI_COMPILE_SIZE] = 0;
code[MI_COMPILE_SIZE_ON_HEAP] = 0;
- code[MI_NUM_BREAKPOINTS] = 0;
code[MI_LITERALS_START] = 0;
code[MI_LITERALS_END] = 0;
code[MI_LITERALS_OFF_HEAP] = 0;
@@ -5939,7 +5907,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
Eterm* tp;
Eterm func;
Eterm arity_term;
- Uint arity;
+ Sint arity;
Uint native_address;
Eterm op;
@@ -5997,12 +5965,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;
}
@@ -6028,13 +6000,13 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
if (patch_funentries(Patchlist)) {
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
return Mod;
}
error:
erts_free_aligned_binary_bytes(temp_alloc);
- free_state(stp);
+ free_loader_state(magic);
BIF_ERROR(p, BADARG);
}
@@ -6051,3 +6023,4 @@ static int safe_mul(UWord a, UWord b, UWord* resp)
return (res / b) == a;
}
}
+
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index 997ba197db..65a8f26d7c 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -1,7 +1,7 @@
/*
* %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
@@ -49,11 +49,6 @@ extern void** beam_ops;
extern BeamInstr beam_debug_apply[];
extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_apply_bif;
-extern BeamInstr* em_call_traced_function;
-typedef struct {
- BeamInstr* start; /* Pointer to start of module. */
- BeamInstr* end; /* Points one word beyond last function in module. */
-} Range;
/*
* The following variables keep a sorted list of address ranges for
@@ -61,11 +56,6 @@ typedef struct {
* instruction pointer.
*/
-extern Range* modules;
-extern int num_loaded_modules;
-extern int allocated_modules;
-extern Range* mid_module;
-
/* Total code size in bytes */
extern Uint erts_total_code_size;
/*
@@ -94,27 +84,22 @@ extern Uint erts_total_code_size;
#define MI_COMPILE_SIZE_ON_HEAP 6
/*
- * Number of breakpoints in module is stored in this word
- */
-#define MI_NUM_BREAKPOINTS 7
-
-/*
* Literal area (constant pool).
*/
-#define MI_LITERALS_START 8
-#define MI_LITERALS_END 9
-#define MI_LITERALS_OFF_HEAP 10
+#define MI_LITERALS_START 7
+#define MI_LITERALS_END 8
+#define MI_LITERALS_OFF_HEAP 9
/*
* Pointer to the on_load function (or NULL if none).
*/
-#define MI_ON_LOAD_FUNCTION_PTR 11
+#define MI_ON_LOAD_FUNCTION_PTR 10
/*
* Pointer to the line table (or NULL if none).
*/
-#define MI_LINE_TABLE 12
+#define MI_LINE_TABLE 11
/*
* Start of function pointer table. This table contains pointers to
@@ -125,5 +110,27 @@ extern Uint erts_total_code_size;
* this table.
*/
-#define MI_FUNCTIONS 13
+#define MI_FUNCTIONS 12
+
+/*
+ * Layout of the line table.
+ */
+
+#define MI_LINE_FNAME_PTR 0
+#define MI_LINE_LOC_TAB 1
+#define MI_LINE_LOC_SIZE 2
+#define MI_LINE_FUNC_TAB 3
+
+#define LINE_INVALID_LOCATION (0)
+
+/*
+ * Macros for manipulating locations.
+ */
+
+#define IS_VALID_LOCATION(File, Line) \
+ ((unsigned) (File) < 255 && (unsigned) (Line) < ((1 << 24) - 1))
+#define MAKE_LOCATION(File, Line) (((File) << 24) | (Line))
+#define LOC_FILE(Loc) ((Loc) >> 24)
+#define LOC_LINE(Loc) ((Loc) & ((1 << 24)-1))
+
#endif /* _BEAM_LOAD_H */
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
new file mode 100644
index 0000000000..0f2d5d0c2a
--- /dev/null
+++ b/erts/emulator/beam/beam_ranges.c
@@ -0,0 +1,349 @@
+/*
+ * %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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "beam_load.h"
+
+typedef struct {
+ BeamInstr* start; /* Pointer to start of module. */
+ erts_smp_atomic_t end; /* (BeamInstr*) Points one word beyond last function in module. */
+} Range;
+
+/* Range 'end' needs to be atomic as we purge module
+ by setting end=start in active code_ix */
+#define RANGE_END(R) ((BeamInstr*)erts_smp_atomic_read_nob(&(R)->end))
+
+static Range* find_range(BeamInstr* pc);
+static void lookup_loc(FunctionInfo* fi, BeamInstr* pc,
+ BeamInstr* modp, int idx);
+
+/*
+ * The following variables keep a sorted list of address ranges for
+ * each module. It allows us to quickly find a function given an
+ * instruction pointer.
+ */
+struct ranges {
+ Range* modules; /* Sorted lists of module addresses. */
+ Sint n; /* Number of range entries. */
+ Sint allocated; /* Number of allocated entries. */
+ erts_smp_atomic_t mid; /* Cached search start point */
+};
+static struct ranges r[ERTS_NUM_CODE_IX];
+static erts_smp_atomic_t mem_used;
+
+#ifdef HARD_DEBUG
+static void check_consistency(struct ranges* p)
+{
+ int i;
+
+ ASSERT(p->n <= p->allocated);
+ ASSERT((Uint)(p->mid - p->modules) < p->n ||
+ (p->mid == p->modules && p->n == 0));
+ for (i = 0; i < p->n; i++) {
+ ASSERT(p->modules[i].start <= RANGE_END(&p->modules[i]));
+ ASSERT(!i || RANGE_END(&p->modules[i-1]) < p->modules[i].start);
+ }
+}
+# define CHECK(r) check_consistency(r)
+#else
+# define CHECK(r)
+#endif /* HARD_DEBUG */
+
+
+void
+erts_init_ranges(void)
+{
+ Sint i;
+
+ erts_smp_atomic_init_nob(&mem_used, 0);
+ for (i = 0; i < ERTS_NUM_CODE_IX; i++) {
+ r[i].modules = 0;
+ r[i].n = 0;
+ r[i].allocated = 0;
+ erts_smp_atomic_init_nob(&r[i].mid, 0);
+ }
+}
+
+void
+erts_start_staging_ranges(void)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+
+ if (r[dst].modules) {
+ erts_smp_atomic_add_nob(&mem_used, -r[dst].allocated);
+ erts_free(ERTS_ALC_T_MODULE_REFS, r[dst].modules);
+ r[dst].modules = NULL;
+ }
+}
+
+void
+erts_end_staging_ranges(int commit)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+
+ if (commit && r[dst].modules == NULL) {
+ Sint i;
+ Sint n;
+
+ /* No modules added, just clone src and remove purged code. */
+ ErtsCodeIndex src = erts_active_code_ix();
+
+ erts_smp_atomic_add_nob(&mem_used, r[src].n);
+ r[dst].modules = erts_alloc(ERTS_ALC_T_MODULE_REFS,
+ r[src].n * sizeof(Range));
+ r[dst].allocated = r[src].n;
+ n = 0;
+ for (i = 0; i < r[src].n; i++) {
+ Range* rp = r[src].modules+i;
+ if (rp->start < RANGE_END(rp)) {
+ /* Only insert a module that has not been purged. */
+ r[dst].modules[n] = *rp;
+ n++;
+ }
+ }
+ r[dst].n = n;
+ erts_smp_atomic_set_nob(&r[dst].mid,
+ (erts_aint_t) (r[dst].modules + n / 2));
+ }
+}
+
+void
+erts_update_ranges(BeamInstr* code, Uint size)
+{
+ ErtsCodeIndex dst = erts_staging_code_ix();
+ ErtsCodeIndex src = erts_active_code_ix();
+ Sint i;
+ Sint n;
+ Sint need;
+
+ if (src == dst) {
+ ASSERT(!erts_initialized);
+
+ /*
+ * During start-up of system, the indices are the same.
+ * Handle this by faking a source area.
+ */
+ src = (src+1) % ERTS_NUM_CODE_IX;
+ if (r[src].modules) {
+ erts_smp_atomic_add_nob(&mem_used, -r[src].allocated);
+ erts_free(ERTS_ALC_T_MODULE_REFS, r[src].modules);
+ }
+ r[src] = r[dst];
+ r[dst].modules = 0;
+ }
+
+ CHECK(&r[src]);
+
+ ASSERT(r[dst].modules == NULL);
+ need = r[dst].allocated = r[src].n + 1;
+ erts_smp_atomic_add_nob(&mem_used, need);
+ r[dst].modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS,
+ need * sizeof(Range));
+ n = 0;
+ for (i = 0; i < r[src].n; i++) {
+ Range* rp = r[src].modules+i;
+ if (code < rp->start) {
+ r[dst].modules[n].start = code;
+ erts_smp_atomic_init_nob(&r[dst].modules[n].end,
+ (erts_aint_t)(((byte *)code) + size));
+ ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code);
+ n++;
+ break;
+ }
+ if (rp->start < RANGE_END(rp)) {
+ /* Only insert a module that has not been purged. */
+ r[dst].modules[n].start = rp->start;
+ erts_smp_atomic_init_nob(&r[dst].modules[n].end,
+ (erts_aint_t)(RANGE_END(rp)));
+ ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start);
+ n++;
+ }
+ }
+
+ while (i < r[src].n) {
+ Range* rp = r[src].modules+i;
+ if (rp->start < RANGE_END(rp)) {
+ /* Only insert a module that has not been purged. */
+ r[dst].modules[n].start = rp->start;
+ erts_smp_atomic_init_nob(&r[dst].modules[n].end,
+ (erts_aint_t)(RANGE_END(rp)));
+ ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < rp->start);
+ n++;
+ }
+ i++;
+ }
+
+ if (n == 0 || code > r[dst].modules[n-1].start) {
+ r[dst].modules[n].start = code;
+ erts_smp_atomic_init_nob(&r[dst].modules[n].end,
+ (erts_aint_t)(((byte *)code) + size));
+ ASSERT(!n || RANGE_END(&r[dst].modules[n-1]) < code);
+ n++;
+ }
+
+ ASSERT(n <= r[src].n+1);
+ r[dst].n = n;
+ erts_smp_atomic_set_nob(&r[dst].mid,
+ (erts_aint_t) (r[dst].modules + n / 2));
+
+ CHECK(&r[dst]);
+ CHECK(&r[src]);
+}
+
+void
+erts_remove_from_ranges(BeamInstr* code)
+{
+ Range* rp = find_range(code);
+ erts_smp_atomic_set_nob(&rp->end, (erts_aint_t)rp->start);
+}
+
+UWord
+erts_ranges_sz(void)
+{
+ return erts_smp_atomic_read_nob(&mem_used) * sizeof(Range);
+}
+
+/*
+ * Find a function from the given pc and fill information in
+ * the FunctionInfo struct. If the full_info is non-zero, fill
+ * in all available information (including location in the
+ * source code). If no function is found, the 'current' field
+ * will be set to NULL.
+ */
+
+void
+erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info)
+{
+ BeamInstr** low;
+ BeamInstr** high;
+ BeamInstr** mid;
+ Range* rp;
+
+ fi->current = NULL;
+ fi->needed = 5;
+ fi->loc = LINE_INVALID_LOCATION;
+ rp = find_range(pc);
+ if (rp == 0) {
+ return;
+ }
+
+ low = (BeamInstr **) (rp->start + MI_FUNCTIONS);
+ high = low + rp->start[MI_NUM_FUNCTIONS];
+ while (low < high) {
+ mid = low + (high-low) / 2;
+ if (pc < mid[0]) {
+ high = mid;
+ } else if (pc < mid[1]) {
+ fi->current = mid[0]+2;
+ if (full_info) {
+ BeamInstr** fp = (BeamInstr **) (rp->start +
+ MI_FUNCTIONS);
+ int idx = mid - fp;
+ lookup_loc(fi, pc, rp->start, idx);
+ }
+ return;
+ } else {
+ low = mid + 1;
+ }
+ }
+}
+
+static Range*
+find_range(BeamInstr* pc)
+{
+ ErtsCodeIndex active = erts_active_code_ix();
+ Range* low = r[active].modules;
+ Range* high = low + r[active].n;
+ Range* mid = (Range *) erts_smp_atomic_read_nob(&r[active].mid);
+
+ CHECK(&r[active]);
+ while (low < high) {
+ if (pc < mid->start) {
+ high = mid;
+ } else if (pc > RANGE_END(mid)) {
+ low = mid + 1;
+ } else {
+ erts_smp_atomic_set_nob(&r[active].mid, (erts_aint_t) mid);
+ return mid;
+ }
+ mid = low + (high-low) / 2;
+ }
+ return 0;
+}
+
+static void
+lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx)
+{
+ Eterm* line = (Eterm *) modp[MI_LINE_TABLE];
+ Eterm* low;
+ Eterm* high;
+ Eterm* mid;
+ Eterm pc;
+
+ if (line == 0) {
+ return;
+ }
+
+ pc = (Eterm) (BeamInstr) orig_pc;
+ fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR];
+ low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx];
+ high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1];
+ while (high > low) {
+ mid = low + (high-low) / 2;
+ if (pc < mid[0]) {
+ high = mid;
+ } else if (pc < mid[1]) {
+ int file;
+ int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB];
+
+ if (line[MI_LINE_LOC_SIZE] == 2) {
+ Uint16* loc_table =
+ (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB];
+ fi->loc = loc_table[index];
+ } else {
+ Uint32* loc_table =
+ (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB];
+ ASSERT(line[MI_LINE_LOC_SIZE] == 4);
+ fi->loc = loc_table[index];
+ }
+ if (fi->loc == LINE_INVALID_LOCATION) {
+ return;
+ }
+ fi->needed += 3+2+3+2;
+ file = LOC_FILE(fi->loc);
+ if (file == 0) {
+ /* Special case: Module name with ".erl" appended */
+ Atom* mod_atom = atom_tab(atom_val(fi->current[0]));
+ fi->needed += 2*(mod_atom->len+4);
+ } else {
+ Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1]));
+ fi->needed += 2*ap->len;
+ }
+ return;
+ } else {
+ low = mid + 1;
+ }
+ }
+}
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index fc00b42454..9c438679ea 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -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
@@ -37,10 +37,14 @@
#include "erl_db_util.h"
#include "register.h"
#include "erl_thr_progress.h"
+#define ERTS_PTAB_WANT_BIF_IMPL__
+#include "erl_ptab.h"
+#include "erl_bits.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 +87,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 +106,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 +150,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 +159,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 +202,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 +229,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 +240,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);
@@ -233,15 +253,17 @@ BIF_RETTYPE link_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
- res_no_proc:
- if (BIF_P->flags & F_TRAPEXIT) {
- ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
- erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
- erts_smp_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
- BIF_RET(am_true);
+res_no_proc: {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&BIF_P->state);
+ if (state & ERTS_PSFLG_TRAP_EXIT) {
+ ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN;
+ erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL);
+ erts_smp_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks);
+ BIF_RET(am_true);
+ }
+ else
+ BIF_ERROR(BIF_P, EXC_NOPROC);
}
- else
- BIF_ERROR(BIF_P, EXC_NOPROC);
}
#define ERTS_DEMONITOR_FALSE 2
@@ -287,7 +309,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;
@@ -296,7 +318,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);
@@ -323,7 +345,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),
@@ -338,8 +360,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to)
break;
default:
ASSERT(! "Invalid dsig prepare result");
- res = ERTS_DEMONITOR_INTERNAL_ERROR;
- break;
+ return ERTS_DEMONITOR_INTERNAL_ERROR;
}
#ifndef ERTS_SMP
@@ -385,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;
@@ -424,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
@@ -438,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)
@@ -580,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;
}
@@ -597,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);
}
@@ -633,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);
}
@@ -687,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
@@ -939,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)
@@ -991,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);
@@ -1020,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);
@@ -1035,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 */
@@ -1057,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);
@@ -1065,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)
@@ -1103,8 +1123,9 @@ BIF_RETTYPE hibernate_3(BIF_ALIST_3)
if (erts_hibernate(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, reg)) {
/*
- * If hibernate succeeded, TRAP. The process will be suspended
- * if status is P_WAITING or continue (if any message was in the queue).
+ * If hibernate succeeded, TRAP. The process will be wait in a
+ * hibernated state if its state is inactive (!ERTS_PSFLG_ACTIVE);
+ * otherwise, continue executing (if any message was in the queue).
*/
BIF_TRAP_CODE_PTR_(BIF_P, BIF_P->i);
}
@@ -1342,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)
@@ -1376,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);
@@ -1394,18 +1428,15 @@ 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);
}
else {
rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- rp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, rp_locks);
if (!rp) {
BIF_RET(am_true);
}
@@ -1415,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,
@@ -1427,8 +1458,6 @@ BIF_RETTYPE exit_2(BIF_ALIST_2)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
- if (rp != BIF_P)
- erts_smp_proc_dec_refc(rp);
#endif
/*
* We may have exited ourselves and may have to take action.
@@ -1502,14 +1531,13 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_priority) {
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
old_value = erts_set_process_priority(BIF_P, BIF_ARG_2);
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
if (old_value == THE_NON_VALUE)
goto error;
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_trap_exit) {
+ erts_aint32_t state;
Uint trap_exit;
if (BIF_ARG_2 == am_true) {
trap_exit = 1;
@@ -1520,63 +1548,58 @@ 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_BIF_CHK_PENDING_EXIT(BIF_P,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- old_value = ERTS_PROC_IS_TRAPPING_EXITS(BIF_P) ? am_true : am_false;
- if (trap_exit) {
- ERTS_PROC_SET_TRAP_EXIT(BIF_P);
- } else {
- ERTS_PROC_UNSET_TRAP_EXIT(BIF_P);
+ if (trap_exit)
+ state = erts_smp_atomic32_read_bor_mb(&BIF_P->state,
+ ERTS_PSFLG_TRAP_EXIT);
+ else
+ 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);
}
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
+#endif
+
+ old_value = (state & ERTS_PSFLG_TRAP_EXIT) ? am_true : am_false;
BIF_RET(old_value);
}
else if (BIF_ARG_1 == am_scheduler) {
- int yield;
- ErtsRunQueue *old;
- ErtsRunQueue *new;
+ ErtsRunQueue *old, *new, *curr;
Sint sched;
+ erts_aint32_t state;
+
if (!is_small(BIF_ARG_2))
goto error;
sched = signed_val(BIF_ARG_2);
if (sched < 0 || erts_no_schedulers < sched)
goto error;
- erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS);
- old = BIF_P->bound_runq;
-#ifdef ERTS_SMP
- ASSERT(!old || old == BIF_P->run_queue);
-#endif
- new = !sched ? NULL : erts_schedid2runq(sched);
-#ifndef ERTS_SMP
- yield = 0;
-#else
- if (new == old)
- yield = 0;
+
+ if (sched == 0) {
+ new = NULL;
+ state = erts_smp_atomic32_read_band_mb(&BIF_P->state,
+ ~ERTS_PSFLG_BOUND);
+ }
else {
- ErtsRunQueue *curr = BIF_P->run_queue;
- if (!new)
- erts_smp_runq_lock(curr);
- else
- erts_smp_runqs_lock(curr, new);
- yield = new && BIF_P->run_queue != new;
-#endif
- BIF_P->bound_runq = new;
+ new = erts_schedid2runq(sched);
#ifdef ERTS_SMP
- if (new)
- BIF_P->run_queue = new;
- if (!new)
- erts_smp_runq_unlock(curr);
- else
- erts_smp_runqs_unlock(curr, new);
- }
+ erts_atomic_set_nob(&BIF_P->run_queue, (erts_aint_t) new);
#endif
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS);
+ state = erts_smp_atomic32_read_bor_mb(&BIF_P->state,
+ ERTS_PSFLG_BOUND);
+ }
+
+ curr = ERTS_GET_SCHEDULER_DATA_FROM_PROC(BIF_P)->run_queue;
+ old = (ERTS_PSFLG_BOUND & state) ? curr : NULL;
+
+ ASSERT(!old || old == curr);
+
old_value = old ? make_small(old->ix+1) : make_small(0);
- if (yield)
+ if (new && new != curr)
ERTS_BIF_YIELD_RETURN_X(BIF_P, old_value, am_scheduler);
else
BIF_RET(old_value);
@@ -1625,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);
@@ -1755,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)
@@ -1810,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;
@@ -1822,17 +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_pid2proc_opt(p, ERTS_PROC_LOCK_MAIN,
- to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
-
- 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) {
@@ -1841,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);
@@ -1850,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, ERTS_P2P_FLG_SMP_INC_REFC,
- &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)) {
@@ -1897,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 !! */
@@ -1958,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);
@@ -1997,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, ERTS_P2P_FLG_SMP_INC_REFC,
- &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);
@@ -2060,23 +2022,15 @@ 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)
: rp_locks);
- erts_smp_proc_dec_refc(rp);
return res;
}
}
@@ -2084,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;
@@ -2107,13 +2062,24 @@ 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);
+ if (ERTS_IS_PROC_OUT_OF_REDS(p))
+ goto yield_return;
BIF_RET(am_ok);
- } else switch (result) {
+ }
+
+ switch (result) {
case 0:
+ /* May need to yield even though we do not bump reds here... */
+ if (ERTS_IS_PROC_OUT_OF_REDS(p))
+ goto yield_return;
BIF_RET(am_ok);
break;
case SEND_TRAP:
@@ -2131,10 +2097,13 @@ BIF_RETTYPE send_3(BIF_ALIST_3)
}
break;
case SEND_YIELD_RETURN:
- if (suspend)
- ERTS_BIF_YIELD_RETURN(p, am_ok);
- else
+ if (!suspend)
BIF_RET(am_nosuspend);
+ yield_return:
+ ERTS_BIF_YIELD_RETURN(p, am_ok);
+ 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;
@@ -2159,13 +2128,27 @@ 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);
+ if (ERTS_IS_PROC_OUT_OF_REDS(p))
+ goto yield_return;
BIF_RET(msg);
- } else switch (result) {
+ }
+
+ switch (result) {
case 0:
+ /* May need to yield even though we do not bump reds here... */
+ if (ERTS_IS_PROC_OUT_OF_REDS(p))
+ goto yield_return;
BIF_RET(msg);
break;
case SEND_TRAP:
@@ -2175,7 +2158,11 @@ Eterm erl_send(Process *p, Eterm to, Eterm msg)
ERTS_BIF_YIELD2(bif_export[BIF_send_2], p, to, msg);
break;
case SEND_YIELD_RETURN:
+ 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;
@@ -2445,9 +2432,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));
}
@@ -2460,7 +2445,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);
@@ -2481,7 +2466,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);
}
@@ -2533,11 +2518,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--) {
@@ -2547,15 +2537,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);
@@ -2564,9 +2630,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);
}
/**********************************************************************/
@@ -2576,18 +2651,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);
}
@@ -2597,16 +2673,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, ERTS_ATOM_ENC_LATIN1)) {
erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(a);
} else {
@@ -2831,42 +2907,168 @@ BIF_RETTYPE string_to_integer_1(BIF_ALIST_1)
BIF_RET(TUPLE2(hp, res, tail));
}
}
-
BIF_RETTYPE list_to_integer_1(BIF_ALIST_1)
-{
+ {
+ /* Using do_list_to_integer is about twice as fast as using
+ erts_chars_to_integer because we do not have to copy the
+ entire list */
Eterm res;
Eterm dummy;
/* must be a list */
-
if (do_list_to_integer(BIF_P,BIF_ARG_1,&res,&dummy) != LTI_ALL_INTEGER) {
BIF_ERROR(BIF_P,BADARG);
}
BIF_RET(res);
}
+BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
+{
+
+ /* Bif implementation is about 50% faster than pure erlang,
+ and since we have erts_chars_to_integer now it is simpler
+ as well. This could be optmized further if we did not have to
+ copy the list to buf. */
+ int i;
+ Eterm res;
+ char *buf = NULL;
+ int base;
+
+ i = list_length(BIF_ARG_1);
+ if (i < 0)
+ BIF_ERROR(BIF_P, BADARG);
+
+ base = signed_val(BIF_ARG_2);
+
+ if (base < 2 || base > 36)
+ BIF_ERROR(BIF_P, BADARG);
+
+ /* Take fast path if base it 10 */
+ if (base == 10)
+ return list_to_integer_1(BIF_P,&BIF_ARG_1);
+
+ buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1);
+
+ if (intlist_to_buf(BIF_ARG_1, buf, i) < 0)
+ goto list_to_integer_1_error;
+ buf[i] = '\0'; /* null terminal */
+
+ if ((res = erts_chars_to_integer(BIF_P,buf,i,base)) == THE_NON_VALUE)
+ goto list_to_integer_1_error;
+
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_RET(res);
+
+ list_to_integer_1_error:
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_ERROR(BIF_P, BADARG);
+
+ }
+
/**********************************************************************/
+static int do_float_to_charbuf(Process *p, Eterm efloat, Eterm list,
+ char *fbuf, int sizeof_fbuf) {
+
+ 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 arg;
+ FloatDef f;
+
+ /* check the arguments */
+ if (is_not_float(efloat))
+ 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]);
+ 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(efloat, f);
+
+ if (fmt_type == FMT_FIXED) {
+ return sys_double_to_chars_fast(f.fd, fbuf, sizeof_fbuf,
+ decimals, compact);
+ } else {
+ return sys_double_to_chars_ext(f.fd, fbuf, sizeof_fbuf, decimals);
+ }
+
+badarg:
+ return -1;
+}
+
/* convert a float to a list of ascii characters */
+static BIF_RETTYPE do_float_to_list(Process *BIF_P, Eterm arg, Eterm opts) {
+ int used;
+ Eterm* hp;
+ char fbuf[256];
+
+ if ((used = do_float_to_charbuf(BIF_P,arg,opts,fbuf,sizeof(fbuf))) <= 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ hp = HAlloc(BIF_P, (Uint)used*2);
+ BIF_RET(buf_to_intlist(&hp, fbuf, (Uint)used, NIL));
+}
+
+
BIF_RETTYPE float_to_list_1(BIF_ALIST_1)
{
- int i;
- Uint need;
- Eterm* hp;
- FloatDef f;
- char fbuf[30];
-
- /* check the arguments */
- 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)
- BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
- need = i*2;
- hp = HAlloc(BIF_P, need);
- BIF_RET(buf_to_intlist(&hp, fbuf, i, NIL));
- }
+ return do_float_to_list(BIF_P,BIF_ARG_1,NIL);
+}
+
+BIF_RETTYPE float_to_list_2(BIF_ALIST_2)
+{
+ return do_float_to_list(BIF_P,BIF_ARG_1,BIF_ARG_2);
+}
+
+/* convert a float to a binary of ascii characters */
+
+static BIF_RETTYPE do_float_to_binary(Process *BIF_P, Eterm arg, Eterm opts) {
+ int used;
+ char fbuf[256];
+
+ if ((used = do_float_to_charbuf(BIF_P,arg,opts,fbuf,sizeof(fbuf))) <= 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(new_binary(BIF_P, (byte*)fbuf, (Uint)used));
+}
+
+BIF_RETTYPE float_to_binary_1(BIF_ALIST_1)
+{
+ return do_float_to_binary(BIF_P,BIF_ARG_1,NIL);
+}
+
+BIF_RETTYPE float_to_binary_2(BIF_ALIST_2)
+{
+ return do_float_to_binary(BIF_P,BIF_ARG_1,BIF_ARG_2);
+}
/**********************************************************************/
@@ -3050,36 +3252,101 @@ BIF_RETTYPE string_to_float_1(BIF_ALIST_1)
BIF_RET(tup);
}
+static BIF_RETTYPE do_charbuf_to_float(Process *BIF_P,char *buf) {
+ FloatDef f;
+ Eterm res;
+ Eterm* hp;
+
+ if (sys_chars_to_double(buf, &f.fd) != 0)
+ BIF_ERROR(BIF_P, BADARG);
+
+ hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT);
+ res = make_float(hp);
+ PUT_DOUBLE(f, hp);
+ BIF_RET(res);
+
+}
BIF_RETTYPE list_to_float_1(BIF_ALIST_1)
{
int i;
- FloatDef f;
Eterm res;
- Eterm* hp;
char *buf = NULL;
i = list_length(BIF_ARG_1);
- if (i < 0) {
- badarg:
- if (buf)
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- BIF_ERROR(BIF_P, BADARG);
- }
-
+ if (i < 0)
+ BIF_ERROR(BIF_P, BADARG);
+
buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1);
if (intlist_to_buf(BIF_ARG_1, buf, i) < 0)
- goto badarg;
+ goto list_to_float_1_error;
buf[i] = '\0'; /* null terminal */
+
+ if ((res = do_charbuf_to_float(BIF_P,buf)) == THE_NON_VALUE)
+ goto list_to_float_1_error;
+
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_RET(res);
+
+ list_to_float_1_error:
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_ERROR(BIF_P, BADARG);
+
+}
+
+BIF_RETTYPE binary_to_float_1(BIF_ALIST_1)
+{
+ Eterm res;
+ Eterm binary = BIF_ARG_1;
+ Sint size;
+ byte* bytes, *buf;
+ Eterm* real_bin;
+ Uint offs = 0;
+ Uint bit_offs = 0;
+
+ if (is_not_binary(binary) || (size = binary_size(binary)) == 0)
+ BIF_ERROR(BIF_P, BADARG);
+
+ /*
+ * Unfortunately we have to copy the binary because we have to insert
+ * the '\0' at the end of the binary for strtod to work
+ * (there is no nstrtod :( )
+ */
+
+ buf = erts_alloc(ERTS_ALC_T_TMP, size + 1);
+
+ real_bin = binary_val(binary);
+ if (*real_bin == HEADER_SUB_BIN) {
+ ErlSubBin* sb = (ErlSubBin *) real_bin;
+ if (sb->bitsize) {
+ goto binary_to_float_1_error;
+ }
+ offs = sb->offs;
+ bit_offs = sb->bitoffs;
+ real_bin = binary_val(sb->orig);
+ }
+ if (*real_bin == HEADER_PROC_BIN) {
+ bytes = ((ProcBin *) real_bin)->bytes + offs;
+ } else {
+ bytes = (byte *)(&(((ErlHeapBin *) real_bin)->data)) + offs;
+ }
+ if (bit_offs)
+ erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, size*8);
+ else
+ memcpy(buf, bytes, size);
+
+ buf[size] = '\0';
+
+ if ((res = do_charbuf_to_float(BIF_P,(char*)buf)) == THE_NON_VALUE)
+ goto binary_to_float_1_error;
- if (sys_chars_to_double(buf, &f.fd) != 0)
- goto badarg;
- hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT);
- res = make_float(hp);
- PUT_DOUBLE(f, hp);
erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(res);
+
+ binary_to_float_1_error:
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_ERROR(BIF_P, BADARG);
}
/**********************************************************************/
@@ -3121,7 +3388,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);
}
@@ -3143,7 +3410,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);
}
/**********************************************************************/
@@ -3180,11 +3447,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++;
@@ -3196,24 +3461,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);
+}
- write_ref_thing(hp, ref0, ref1, ref2);
+Eterm erts_make_ref_in_buffer(Eterm buffer[REF_THING_SIZE])
+{
+ Eterm* hp = buffer;
+ Uint32 ref[ERTS_MAX_REF_NUMBERS];
+
+ 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)
@@ -3477,7 +3754,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
@@ -3486,7 +3763,7 @@ BIF_RETTYPE garbage_collect_1(BIF_ALIST_1)
if (rp == ERTS_PROC_LOCK_BUSY)
ERTS_BIF_YIELD1(bif_export[BIF_garbage_collect_1], BIF_P, BIF_ARG_1);
#else
- rp = erts_pid2proc(BIF_P, 0, BIF_ARG_1, 0);
+ rp = erts_proc_lookup(BIF_ARG_1);
#endif
if (!rp)
BIF_RET(am_false);
@@ -3517,71 +3794,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);
}
/**********************************************************************/
@@ -3780,7 +4009,8 @@ BIF_RETTYPE function_exported_3(BIF_ALIST_3)
is_not_small(BIF_ARG_3)) {
BIF_ERROR(BIF_P, BADARG);
}
- if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3)) == NULL) {
+ if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3),
+ erts_active_code_ix()) == NULL) {
BIF_RET(am_false);
}
BIF_RET(am_true);
@@ -4210,14 +4440,15 @@ 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++) {
- if (process_tab[i] != (Process*) 0) {
- Process* p = process_tab[i];
+ max = erts_ptab_max(&erts_proc);
+ for (i = 0; i < max; i++) {
+ Process *p = erts_pix2proc(i);
+ if (p) {
#ifdef USE_VM_PROBES
p->seq_trace_token = (p->dt_utag != NIL) ? am_have_dt_utag : NIL;
#else
@@ -4513,6 +4744,21 @@ erts_bif_prep_await_proc_exit_apply_trap(Process *c_p,
Export bif_return_trap_export;
+void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
+ Eterm (*bif)(BIF_ALIST_0))
+{
+ int i;
+ sys_memset((void *) ep, 0, sizeof(Export));
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ ep->addressv[i] = &ep->code[3];
+ }
+ ep->code[0] = m;
+ ep->code[1] = f;
+ ep->code[2] = a;
+ ep->code[3] = (BeamInstr) em_apply_bif;
+ ep->code[4] = (BeamInstr) bif;
+}
+
void erts_init_bif(void)
{
reference0 = 0;
@@ -4528,17 +4774,13 @@ void erts_init_bif(void)
* yield the calling process traps to. The only thing it does:
* return the value passed as argument.
*/
- sys_memset((void *) &bif_return_trap_export, 0, sizeof(Export));
- bif_return_trap_export.address = &bif_return_trap_export.code[3];
- bif_return_trap_export.code[0] = am_erlang;
- bif_return_trap_export.code[1] = am_bif_return_trap;
+ erts_init_trap_export(&bif_return_trap_export, am_erlang, am_bif_return_trap,
#ifdef DEBUG
- bif_return_trap_export.code[2] = 2;
+ 2
#else
- bif_return_trap_export.code[2] = 1;
+ 1
#endif
- bif_return_trap_export.code[3] = (BeamInstr) em_apply_bif;
- bif_return_trap_export.code[4] = (BeamInstr) &bif_return_trap;
+ , &bif_return_trap);
flush_monitor_message_trap = erts_export_put(am_erlang,
am_flush_monitor_message,
@@ -4551,6 +4793,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);
@@ -4566,19 +4810,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
@@ -4586,7 +4829,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) {
@@ -4680,7 +4923,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;
@@ -4697,6 +4939,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);
@@ -4707,7 +4950,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;
@@ -4724,6 +4966,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);
@@ -4747,14 +4990,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
}
}
@@ -4780,7 +5023,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;
@@ -4797,12 +5040,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 d20089a9fb..51b77a95ed 100644
--- a/erts/emulator/beam/bif.h
+++ b/erts/emulator/beam/bif.h
@@ -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
@@ -35,6 +35,13 @@ extern Export* erts_format_cpu_topology_trap;
#define BIF_ARG_2 (BIF__ARGS[1])
#define BIF_ARG_3 (BIF__ARGS[2])
+#define ERTS_IS_PROC_OUT_OF_REDS(p) \
+ ((p)->fcalls > 0 \
+ ? 0 \
+ : (!ERTS_PROC_GET_SAVED_CALLS_BUF((p)) \
+ ? (p)->fcalls == 0 \
+ : ((p)->fcalls == -CONTEXT_REDS)))
+
#define BUMP_ALL_REDS(p) do { \
if (!ERTS_PROC_GET_SAVED_CALLS_BUF((p))) \
(p)->fcalls = 0; \
@@ -59,6 +66,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))) \
@@ -125,7 +134,7 @@ do { \
#define ERTS_BIF_PREP_TRAP0(Ret, Trap, Proc) \
do { \
(Proc)->arity = 0; \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -135,7 +144,7 @@ do { \
Eterm* reg = ERTS_PROC_GET_SCHDATA((Proc))->x_reg_array; \
(Proc)->arity = 1; \
reg[0] = (Eterm) (A0); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -146,7 +155,7 @@ do { \
(Proc)->arity = 2; \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -158,7 +167,7 @@ do { \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
reg[2] = (Eterm) (A2); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
(Ret) = THE_NON_VALUE; \
} while (0)
@@ -170,13 +179,13 @@ do { \
reg[0] = (Eterm) (A0); \
reg[1] = (Eterm) (A1); \
reg[2] = (Eterm) (A2); \
- (Proc)->i = (BeamInstr*) ((Trap)->address); \
+ (Proc)->i = (BeamInstr*) ((Trap)->addressv[erts_active_code_ix()]); \
(Proc)->freason = TRAP; \
} while (0)
#define BIF_TRAP0(p, Trap_) do { \
(p)->arity = 0; \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -185,7 +194,7 @@ do { \
Eterm* reg = ERTS_PROC_GET_SCHDATA((p))->x_reg_array; \
(p)->arity = 1; \
reg[0] = (A0); \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -195,7 +204,7 @@ do { \
(p)->arity = 2; \
reg[0] = (A0); \
reg[1] = (A1); \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -206,7 +215,7 @@ do { \
reg[0] = (A0); \
reg[1] = (A1); \
reg[2] = (A2); \
- (p)->i = (BeamInstr*) ((Trap_)->address); \
+ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \
(p)->freason = TRAP; \
return THE_NON_VALUE; \
} while(0)
@@ -322,27 +331,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 797bce43ab..8bc994c8c3 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -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
@@ -31,434 +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:load_module/2
-bif 'erl.system.code':load/2 ebif_load_module_2
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
@@ -466,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
@@ -513,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)
@@ -629,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
#
@@ -643,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
#
@@ -677,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.
@@ -799,6 +522,7 @@ bif erlang:nif_error/2
bif prim_file:internal_name2native/1
bif prim_file:internal_native2name/1
bif prim_file:internal_normalize_utf8/1
+bif prim_file:is_translatable/1
bif file:native_name_encoding/0
#
@@ -829,6 +553,24 @@ bif erlang:dt_restore_tag/1
bif erlang:dt_prepend_vm_tag_data/1
bif erlang:dt_append_vm_tag_data/1
+
+#
+# New in R16B.
+#
+bif erlang:prepare_loading/2
+bif erlang:finish_loading/1
+bif erlang:insert_element/3
+bif erlang:delete_element/2
+bif erlang:binary_to_integer/1
+bif erlang:binary_to_integer/2
+bif erlang:integer_to_binary/1
+bif erlang:list_to_integer/2
+bif erlang:float_to_binary/1
+bif erlang:float_to_binary/2
+bif erlang:binary_to_float/1
+
+bif io:printable_range/0
+
#
# Obsolete
#
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 5a5b162b9c..acfcc845e4 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -1674,26 +1674,26 @@ int big_decimal_estimate(Wterm x)
** Convert a bignum into a string of decimal numbers
*/
-static void write_big(Wterm x, void (*write_func)(void *, char), void *arg)
+static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg)
{
Eterm* xp = big_val(x);
ErtsDigit* dx = BIG_V(xp);
dsize_t xl = BIG_SIZE(xp);
short sign = BIG_SIGN(xp);
ErtsDigit rem;
+ Uint n = 0;
if (xl == 1 && *dx < D_DECIMAL_BASE) {
rem = *dx;
- if (rem == 0)
- (*write_func)(arg, '0');
- else {
+ if (rem == 0) {
+ (*write_func)(arg, '0'); n++;
+ } else {
while(rem) {
- (*write_func)(arg, (rem % 10) + '0');
+ (*write_func)(arg, (rem % 10) + '0'); n++;
rem /= 10;
}
}
- }
- else {
+ } else {
ErtsDigit* tmp = (ErtsDigit*) erts_alloc(ERTS_ALC_T_TMP,
sizeof(ErtsDigit)*xl);
dsize_t tmpl = xl;
@@ -1704,15 +1704,14 @@ static void write_big(Wterm x, void (*write_func)(void *, char), void *arg)
tmpl = D_div(tmp, tmpl, D_DECIMAL_BASE, tmp, &rem);
if (tmpl == 1 && *tmp == 0) {
while(rem) {
- (*write_func)(arg, (rem % 10)+'0');
+ (*write_func)(arg, (rem % 10)+'0'); n++;
rem /= 10;
}
break;
- }
- else {
+ } else {
int i = D_DECIMAL_EXP;
while(i--) {
- (*write_func)(arg, (rem % 10)+'0');
+ (*write_func)(arg, (rem % 10)+'0'); n++;
rem /= 10;
}
}
@@ -1720,8 +1719,10 @@ static void write_big(Wterm x, void (*write_func)(void *, char), void *arg)
erts_free(ERTS_ALC_T_TMP, (void *) tmp);
}
- if (sign)
- (*write_func)(arg, '-');
+ if (sign) {
+ (*write_func)(arg, '-'); n++;
+ }
+ return n;
}
struct big_list__ {
@@ -1762,6 +1763,20 @@ char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz)
return big_str;
}
+/* Bignum to binary bytes
+ * e.g. 1 bsl 64 -> "18446744073709551616"
+ */
+
+Uint erts_big_to_binary_bytes(Eterm x, char *buf, Uint buf_sz)
+{
+ char *big_str = buf + buf_sz;
+ Uint n;
+ n = write_big(x, write_string, (void *) &big_str);
+ ASSERT(buf <= big_str && big_str <= buf + buf_sz);
+ return n;
+}
+
+
/*
** Normalize a bignum given thing pointer length in digits and a sign
** patch zero if odd length
@@ -2467,3 +2482,209 @@ int term_equals_2pow32(Eterm x)
return 0;
}
}
+
+
+#define IS_VALID_CHARACTER(CHAR,BASE) \
+ (CHAR < '0' \
+ || (CHAR > ('0' + BASE - 1) \
+ && !(BASE > 10 \
+ && ((CHAR >= 'a' && CHAR < ('a' + BASE - 10)) \
+ || (CHAR >= 'A' && CHAR < ('A' + BASE - 10))))))
+#define CHARACTER_FROM_BASE(CHAR) \
+ ((CHAR <= '9') ? CHAR - '0' : 10 + ((CHAR <= 'Z') ? CHAR - 'A' : CHAR - 'a'))
+#define D_BASE_EXP(BASE) (d_base_exp_lookup[BASE-2])
+#define D_BASE_BASE(BASE) (d_base_base_lookup[BASE-2])
+#define LG2_LOOKUP(BASE) (lg2_lookup[base-2])
+
+/*
+ * for i in 2..64 do
+ * lg2_lookup[i-2] = log2(i)
+ * end
+ * How many bits are needed to store string of size n
+ */
+const double lg2_lookup[] = { 1.0, 1.58496, 2, 2.32193, 2.58496, 2.80735, 3.0,
+ 3.16993, 3.32193, 3.45943, 3.58496, 3.70044, 3.80735, 3.90689, 4.0,
+ 4.08746, 4.16993, 4.24793, 4.32193, 4.39232, 4.45943, 4.52356, 4.58496,
+ 4.64386, 4.70044, 4.75489, 4.80735, 4.85798, 4.90689, 4.9542, 5.0,
+ 5.04439, 5.08746, 5.12928, 5.16993, 5.20945, 5.24793, 5.2854, 5.32193,
+ 5.35755, 5.39232, 5.42626, 5.45943, 5.49185, 5.52356, 5.55459, 5.58496,
+ 5.61471, 5.64386, 5.67243, 5.70044, 5.72792, 5.75489, 5.78136, 5.80735,
+ 5.83289, 5.85798, 5.88264, 5.90689, 5.93074, 5.9542, 5.97728, 6.0 };
+
+/*
+ * for i in 2..64 do
+ * d_base_exp_lookup[i-2] = 31 / lg2_lookup[i-2];
+ * end
+ * How many characters can fit in 31 bits
+ */
+const byte d_base_exp_lookup[] = { 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8,
+ 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5 };
+
+/*
+ * for i in 2..64 do
+ * d_base_base_lookup[i-2] = pow(i,d_base_exp_lookup[i-2]);
+ * end
+ * How much can the characters which fit in 31 bit represent
+ */
+const Uint d_base_base_lookup[] = { 2147483648, 1162261467, 1073741824,
+ 1220703125, 362797056, 1977326743, 1073741824, 387420489, 1000000000,
+ 214358881, 429981696, 815730721, 1475789056, 170859375, 268435456,
+ 410338673, 612220032, 893871739, 1280000000, 1801088541, 113379904,
+ 148035889, 191102976, 244140625, 308915776, 387420489, 481890304,
+ 594823321, 729000000, 887503681, 1073741824, 1291467969, 1544804416,
+ 1838265625, 60466176, 69343957, 79235168, 90224199, 102400000,
+ 115856201, 130691232, 147008443, 164916224, 184528125, 205962976,
+ 229345007, 254803968, 282475249, 312500000, 345025251, 380204032,
+ 418195493, 459165024, 503284375, 550731776, 601692057, 656356768,
+ 714924299, 777600000, 844596301, 916132832, 992436543, 1073741824 };
+
+Eterm erts_chars_to_integer(Process *BIF_P, char *bytes,
+ Uint size, const int base) {
+ Eterm res;
+ Sint i = 0;
+ int n = 0;
+ int neg = 0;
+ byte b;
+ Eterm *hp, *hp_end;
+ int m;
+ int lg2;
+
+ if (size == 0)
+ goto bytebuf_to_integer_1_error;
+
+ if (bytes[0] == '-') {
+ neg = 1;
+ bytes++;
+ size--;
+
+ } else if (bytes[0] == '+') {
+ bytes++;
+ size--;
+ }
+
+ if (size < SMALL_DIGITS && base <= 10) {
+ /* *
+ * Take shortcut if we know that all chars are '0' < b < '9' and
+ * fit in a small. This improves speed by about 10% over the generic
+ * small case.
+ * */
+ while (size--) {
+ b = *bytes++;
+
+ if (b < '0' || b > ('0'+base-1))
+ goto bytebuf_to_integer_1_error;
+
+ i = i * base + b - '0';
+ }
+
+ if (neg)
+ i = -i;
+ res = make_small(i);
+ goto bytebuf_to_integer_1_done;
+ }
+
+ /*
+ * Calculate the maximum number of bits which will
+ * be needed to represent the binary
+ */
+ lg2 = ((size+2)*LG2_LOOKUP(base)+1);
+
+ if (lg2 < SMALL_BITS) {
+ /* Take shortcut if we know it will fit in a small.
+ * This improves speed by about 30%.
+ */
+ while (size) {
+ b = *bytes++;
+ size--;
+
+ if (IS_VALID_CHARACTER(b,base))
+ goto bytebuf_to_integer_1_error;
+
+ i = i * base + CHARACTER_FROM_BASE(b);
+
+ }
+
+ if (neg)
+ i = -i;
+ res = make_small(i);
+ goto bytebuf_to_integer_1_done;
+
+ }
+
+ /* Start calculating bignum */
+ m = (lg2 + D_EXP-1)/D_EXP;
+ m = BIG_NEED_SIZE(m);
+
+ hp = HAlloc(BIF_P, m);
+ hp_end = hp + m;
+
+ if ((i = (size % D_BASE_EXP(base))) == 0)
+ i = D_BASE_EXP(base);
+
+ n = size - i;
+ m = 0;
+
+ while (i--) {
+ b = *bytes++;
+
+ if (IS_VALID_CHARACTER(b,base)) {
+ HRelease(BIF_P, hp_end, hp);
+ goto bytebuf_to_integer_1_error;
+ }
+
+ m = base * m + CHARACTER_FROM_BASE(b);
+ }
+
+ res = small_to_big(m, hp);
+
+ while (n) {
+ i = D_BASE_EXP(base);
+ n -= D_BASE_EXP(base);
+ m = 0;
+ while (i--) {
+ b = *bytes++;
+
+ if (IS_VALID_CHARACTER(b,base)) {
+ HRelease(BIF_P, hp_end, hp);
+ goto bytebuf_to_integer_1_error;
+ }
+
+ m = base * m + CHARACTER_FROM_BASE(b);
+ }
+ if (is_small(res)) {
+ res = small_to_big(signed_val(res), hp);
+ }
+ res = big_times_small(res, D_BASE_BASE(base), hp);
+ if (is_small(res)) {
+ res = small_to_big(signed_val(res), hp);
+ }
+ res = big_plus_small(res, m, hp);
+ }
+
+ if (is_big(res)) /* check if small */
+ res = big_plus_small(res, 0, hp); /* includes conversion to small */
+
+ if (neg) {
+ if (is_small(res))
+ res = make_small(-signed_val(res));
+ else {
+ Uint *big = big_val(res); /* point to thing */
+ *big = bignum_header_neg(*big);
+ }
+ }
+
+ if (is_big(res)) {
+ hp += (big_arity(res) + 1);
+ }
+ HRelease(BIF_P, hp_end, hp);
+ goto bytebuf_to_integer_1_done;
+
+bytebuf_to_integer_1_error:
+ return THE_NON_VALUE;
+
+bytebuf_to_integer_1_done:
+ return res;
+
+}
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 7eb1e5afe2..1a7b14170f 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -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
@@ -117,6 +117,7 @@ typedef Uint dsize_t; /* Vector size type */
int big_decimal_estimate(Wterm);
Eterm erts_big_to_list(Eterm, Eterm**);
char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz);
+Uint erts_big_to_binary_bytes(Eterm x, char *buf, Uint buf_sz);
Eterm small_times(Sint, Sint, Eterm*);
@@ -165,5 +166,7 @@ int term_equals_2pow32(Eterm);
Eterm erts_uint64_to_big(Uint64, Eterm **);
Eterm erts_sint64_to_big(Sint64, Eterm **);
+Eterm erts_chars_to_integer(Process *, char*, Uint, const int);
+
#endif
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index 3d2725e239..33abac2f3d 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -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
@@ -240,6 +240,98 @@ erts_bin_bytes_to_list(Eterm previous, Eterm* hp, byte* bytes, Uint size, Uint b
return previous;
}
+BIF_RETTYPE binary_to_integer_1(BIF_ALIST_1)
+{
+ byte *temp_alloc = NULL;
+ char *bytes;
+ Uint size;
+ Eterm res;
+
+ if ((bytes = (char*)erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc))
+ == NULL )
+ goto binary_to_integer_1_error;
+
+ size = binary_size(BIF_ARG_1);
+
+ if ((res = erts_chars_to_integer(BIF_P,bytes,size,10)) != THE_NON_VALUE) {
+ erts_free_aligned_binary_bytes(temp_alloc);
+ return res;
+ }
+
+ binary_to_integer_1_error:
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+BIF_RETTYPE binary_to_integer_2(BIF_ALIST_2)
+{
+ byte *temp_alloc = NULL;
+ char *bytes;
+ Uint size;
+ int base;
+ Eterm res;
+
+ if (!is_small(BIF_ARG_2))
+ BIF_ERROR(BIF_P, BADARG);
+
+ base = signed_val(BIF_ARG_2);
+
+ if (base < 2 || base > 36)
+ BIF_ERROR(BIF_P, BADARG);
+
+ if ((bytes = (char*)erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc))
+ == NULL )
+ goto binary_to_integer_2_error;
+
+ size = binary_size(BIF_ARG_1);
+
+ if ((res = erts_chars_to_integer(BIF_P,bytes,size,base)) != THE_NON_VALUE) {
+ erts_free_aligned_binary_bytes(temp_alloc);
+ return res;
+ }
+
+ binary_to_integer_2_error:
+
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_ERROR(BIF_P, BADARG);
+
+}
+
+BIF_RETTYPE integer_to_binary_1(BIF_ALIST_1)
+{
+ Uint size;
+ Eterm res;
+
+ if (is_not_integer(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (is_small(BIF_ARG_1)) {
+ char *c;
+ struct Sint_buf ibuf;
+
+ /* Enhancement: If we can calculate the buffer size exactly
+ * we could avoid an unnecessary copy of buffers.
+ * Useful if size determination is faster than a copy.
+ */
+ c = Sint_to_buf(signed_val(BIF_ARG_1), &ibuf);
+ size = sys_strlen(c);
+ res = new_binary(BIF_P, (byte *)c, size);
+ } else {
+ byte* bytes;
+ Uint n = 0;
+
+ /* Here we also have multiple copies of buffers
+ * due to new_binary interface
+ */
+ size = big_decimal_estimate(BIF_ARG_1) - 1; /* remove null */
+ bytes = (byte*) erts_alloc(ERTS_ALC_T_TMP, sizeof(byte)*size);
+ n = erts_big_to_binary_bytes(BIF_ARG_1, (char *)bytes, size);
+ res = new_binary(BIF_P, bytes + size - n, n);
+ erts_free(ERTS_ALC_T_TMP, (void *) bytes);
+ }
+ BIF_RET(res);
+}
BIF_RETTYPE binary_to_list_1(BIF_ALIST_1)
{
@@ -355,10 +447,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 +469,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 f7e9f15655..9aa1e5f30d 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -61,19 +61,23 @@ 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++) {
- if ((process_tab[i] != NULL) && (process_tab[i]->i != ENULL)) {
- if (process_tab[i]->status != P_EXITING)
- print_process_info(to, to_arg, process_tab[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))
+ print_process_info(to, to_arg, p);
}
}
@@ -83,13 +87,14 @@ 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--) {
- if (((rp = process_tab[i]) != NULL) && rp->i != ENULL) {
+ for (i = max-1; i >= 0; i--) {
+ rp = erts_pix2proc(i);
+ if (rp && rp->i != ENULL) {
int br;
print_process_info(ERTS_PRINT_STDOUT, NULL, rp);
erts_printf("(k)ill (n)ext (r)eturn:\n");
@@ -97,11 +102,20 @@ process_killer(void)
if ((j = sys_get_key(0)) <= 0)
erl_exit(0, "");
switch(j) {
- case 'k':
- if (rp->status == P_WAITING) {
- ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- erts_smp_proc_inc_refc(rp);
- erts_smp_proc_lock(rp, rp_locks);
+ case 'k': {
+ ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
+ erts_aint32_t state;
+ erts_smp_proc_inc_refc(rp);
+ erts_smp_proc_lock(rp, rp_locks);
+ state = erts_smp_atomic32_read_acqb(&rp->state);
+ if (state & (ERTS_PSFLG_FREE
+ | ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_IN_RUNQ
+ | ERTS_PSFLG_RUNNING)) {
+ erts_printf("Can only kill WAITING processes this way\n");
+ }
+ else {
(void) erts_send_exit_signal(NULL,
NIL,
rp,
@@ -110,12 +124,10 @@ process_killer(void)
NIL,
NULL,
0);
- erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
- else
- erts_printf("Can only kill WAITING processes this way\n");
-
+ erts_smp_proc_unlock(rp, rp_locks);
+ erts_smp_proc_dec_refc(rp);
+ }
case 'n': br = 1; break;
case 'r': return;
default: return;
@@ -180,49 +192,45 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
void
print_process_info(int to, void *to_arg, Process *p)
{
+ time_t approx_started;
int garbing = 0;
int running = 0;
- time_t tmp_t;
struct saved_calls *scb;
+ 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: ");
- switch (p->status) {
- case P_FREE:
+
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (state & ERTS_PSFLG_FREE)
erts_print(to, to_arg, "Non Existing\n"); /* Should never happen */
- break;
- case P_RUNABLE:
- erts_print(to, to_arg, "Scheduled\n");
- break;
- case P_WAITING:
- erts_print(to, to_arg, "Waiting\n");
- break;
- case P_SUSPENDED:
- erts_print(to, to_arg, "Suspended\n");
- break;
- case P_RUNNING:
- erts_print(to, to_arg, "Running\n");
- running = 1;
- break;
- case P_EXITING:
+ else if (state & ERTS_PSFLG_EXITING)
erts_print(to, to_arg, "Exiting\n");
- break;
- case P_GARBING:
- erts_print(to, to_arg, "Garbing\n");
+ else if (state & ERTS_PSFLG_GC) {
garbing = 1;
running = 1;
- break;
+ erts_print(to, to_arg, "Garbing\n");
+ }
+ else if (state & ERTS_PSFLG_SUSPENDED)
+ erts_print(to, to_arg, "Suspended\n");
+ else if (state & ERTS_PSFLG_RUNNING) {
+ running = 1;
+ erts_print(to, to_arg, "Running\n");
}
+ else if (state & ERTS_PSFLG_ACTIVE)
+ erts_print(to, to_arg, "Scheduled\n");
+ else
+ erts_print(to, to_arg, "Waiting\n");
/*
* 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
@@ -245,8 +253,8 @@ print_process_info(int to, void *to_arg, Process *p)
}
erts_print(to, to_arg, "Spawned by: %T\n", p->parent);
- tmp_t = p->started.tv_sec;
- erts_print(to, to_arg, "Started: %s", ctime(&tmp_t));
+ approx_started = (time_t) p->approx_started;
+ erts_print(to, to_arg, "Started: %s", ctime(&approx_started));
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
erts_print(to, to_arg, "Message queue length: %d\n", p->msg.len);
@@ -296,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");
}
@@ -377,17 +385,22 @@ loaded(int to, void *to_arg)
int old = 0;
int cur = 0;
BeamInstr* code;
+ Module* modp;
+ ErtsCodeIndex code_ix;
+
+ code_ix = erts_active_code_ix();
+ erts_rlock_old_code(code_ix);
/*
* Calculate and print totals.
*/
- for (i = 0; i < module_code_size(); i++) {
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
- cur += module_code(i)->code_length;
- if (module_code(i)->old_code_length != 0) {
- old += module_code(i)->old_code_length;
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ if ((modp = module_code(i, code_ix)) != NULL &&
+ ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
+ cur += modp->curr.code_length;
+ if (modp->old.code_length != 0) {
+ old += modp->old.code_length;
}
}
}
@@ -398,21 +411,22 @@ loaded(int to, void *to_arg)
* Print one line per module.
*/
- for (i = 0; i < module_code_size(); i++) {
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ modp = module_code(i, code_ix);
if (!ERTS_IS_CRASH_DUMPING) {
/*
* Interactive dump; keep it brief.
*/
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
- erts_print(to, to_arg, "%T", make_atom(module_code(i)->module));
- cur += module_code(i)->code_length;
- erts_print(to, to_arg, " %d", module_code(i)->code_length );
- if (module_code(i)->old_code_length != 0) {
+ if (modp != NULL &&
+ ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
+ erts_print(to, to_arg, "%T", make_atom(modp->module));
+ cur += modp->curr.code_length;
+ erts_print(to, to_arg, " %d", modp->curr.code_length );
+ if (modp->old.code_length != 0) {
erts_print(to, to_arg, " (%d old)",
- module_code(i)->old_code_length );
- old += module_code(i)->old_code_length;
+ modp->old.code_length );
+ old += modp->old.code_length;
}
erts_print(to, to_arg, "\n");
}
@@ -420,15 +434,15 @@ loaded(int to, void *to_arg)
/*
* To crash dump; make it parseable.
*/
- if (module_code(i) != NULL &&
- ((module_code(i)->code_length != 0) ||
- (module_code(i)->old_code_length != 0))) {
+ if (modp != NULL &&
+ ((modp->curr.code_length != 0) ||
+ (modp->old.code_length != 0))) {
erts_print(to, to_arg, "=mod:");
- erts_print(to, to_arg, "%T", make_atom(module_code(i)->module));
+ erts_print(to, to_arg, "%T", make_atom(modp->module));
erts_print(to, to_arg, "\n");
erts_print(to, to_arg, "Current size: %d\n",
- module_code(i)->code_length);
- code = module_code(i)->code;
+ modp->curr.code_length);
+ code = modp->curr.code;
if (code != NULL && code[MI_ATTR_PTR]) {
erts_print(to, to_arg, "Current attributes: ");
dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR],
@@ -440,9 +454,9 @@ loaded(int to, void *to_arg)
code[MI_COMPILE_SIZE]);
}
- if (module_code(i)->old_code_length != 0) {
- erts_print(to, to_arg, "Old size: %d\n", module_code(i)->old_code_length);
- code = module_code(i)->old_code;
+ if (modp->old.code_length != 0) {
+ erts_print(to, to_arg, "Old size: %d\n", modp->old.code_length);
+ code = modp->old.code;
if (code[MI_ATTR_PTR]) {
erts_print(to, to_arg, "Old attributes: ");
dump_attributes(to, to_arg, (byte *) code[MI_ATTR_PTR],
@@ -457,6 +471,7 @@ loaded(int to, void *to_arg)
}
}
}
+ erts_runlock_old_code(code_ix);
}
@@ -613,16 +628,17 @@ 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++) {
- if ((rp = process_tab[i]) == NULL)
+ for (i=0; i < max; i++) {
+ rp = erts_pix2proc(i);
+ if (!rp)
continue;
for (hdr = rp->off_heap.first; hdr; hdr = hdr->next) {
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",
@@ -753,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 (process_tab != NULL) /* XXX true at init */
+ 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
new file mode 100644
index 0000000000..c66d5a2f05
--- /dev/null
+++ b/erts/emulator/beam/code_ix.c
@@ -0,0 +1,168 @@
+/*
+ * %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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "code_ix.h"
+#include "global.h"
+#include "beam_catches.h"
+
+
+
+#if 0
+# define CIX_TRACE(text) erts_fprintf(stderr, "CIX_TRACE: " text " act=%u load=%u\r\n", erts_active_code_ix(), erts_staging_code_ix())
+#else
+# define CIX_TRACE(text)
+#endif
+
+erts_smp_atomic32_t the_active_code_index;
+erts_smp_atomic32_t the_staging_code_index;
+
+static Process* code_writing_process = NULL;
+struct code_write_queue_item {
+ Process *p;
+ struct code_write_queue_item* next;
+};
+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;
+#endif
+
+void erts_code_ix_init(void)
+{
+ /* We start emulator by initializing preloaded modules
+ * single threaded with active and staging set both to zero.
+ * Preloading is finished by a commit that will set things straight.
+ */
+ erts_smp_atomic32_init_nob(&the_active_code_index, 0);
+ erts_smp_atomic32_init_nob(&the_staging_code_index, 0);
+ 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
+ CIX_TRACE("init");
+}
+
+void erts_start_staging_code_ix(void)
+{
+ beam_catches_start_staging();
+ export_start_staging();
+ module_start_staging();
+ erts_start_staging_ranges();
+ CIX_TRACE("start");
+}
+
+
+void erts_end_staging_code_ix(void)
+{
+ beam_catches_end_staging(1);
+ export_end_staging(1);
+ module_end_staging(1);
+ erts_end_staging_ranges(1);
+ CIX_TRACE("end");
+}
+
+void erts_commit_staging_code_ix(void)
+{
+ ErtsCodeIndex ix;
+ /* We need to this lock as we are now making the staging export table active */
+ export_staging_lock();
+ ix = erts_staging_code_ix();
+ erts_smp_atomic32_set_nob(&the_active_code_index, ix);
+ ix = (ix + 1) % ERTS_NUM_CODE_IX;
+ erts_smp_atomic32_set_nob(&the_staging_code_index, ix);
+ export_staging_unlock();
+ CIX_TRACE("activate");
+}
+
+void erts_abort_staging_code_ix(void)
+{
+ beam_catches_end_staging(0);
+ export_end_staging(0);
+ module_end_staging(0);
+ erts_end_staging_ranges(0);
+ CIX_TRACE("abort");
+}
+
+
+/*
+ * Calller _must_ yield if we return 0
+ */
+int erts_try_seize_code_write_permission(Process* c_p)
+{
+ int success;
+#ifdef ERTS_SMP
+ ASSERT(!erts_smp_thr_progress_is_blocking()); /* to avoid deadlock */
+#endif
+ ASSERT(c_p != NULL);
+
+ erts_smp_mtx_lock(&code_write_permission_mtx);
+ success = (code_writing_process == NULL);
+ if (success) {
+ 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_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 = code_write_queue;
+ code_write_queue = qitem;
+ erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+ }
+ erts_smp_mtx_unlock(&code_write_permission_mtx);
+ return success;
+}
+
+void erts_release_code_write_permission(void)
+{
+ 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);
+ code_write_queue = qitem->next;
+ erts_smp_proc_dec_refc(qitem->p);
+ erts_free(ERTS_ALC_T_CODE_IX_LOCK_Q, qitem);
+ }
+ code_writing_process = NULL;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_tsd_set(has_code_write_permission, (void *) 0);
+#endif
+ erts_smp_mtx_unlock(&code_write_permission_mtx);
+}
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+int erts_has_code_write_permission(void)
+{
+ 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
new file mode 100644
index 0000000000..16ad900228
--- /dev/null
+++ b/erts/emulator/beam/code_ix.h
@@ -0,0 +1,142 @@
+/*
+ * %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/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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:
+ * 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.
+ *
+ * The basic idea is to maintain several "logical copies" of the code. These
+ * copies are identified by a global 'code index', an integer of 0, 1 or 2.
+ * The code index is used as argument to code access structures like
+ * export, module, beam_catches, beam_ranges.
+ *
+ * The current 'active' code index is used to access the current running
+ * code. The 'staging' code index is used by the process that performs
+ * a code change operation. When a code change operation completes
+ * succesfully, the staging code index becomes the new active code index.
+ *
+ * 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 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.
+ *
+ * Note that the 'code index' is very loosely coupled to the concept of
+ * 'current' and 'old' module versions. You can almost say that they are
+ * orthogonal to each other. Code index is an emulator global concept while
+ * 'current' and 'old' is specific for each module.
+ */
+
+#ifndef __CODE_IX_H__
+#define __CODE_IX_H__
+
+#ifndef __SYS_H__
+# ifdef HAVE_CONFIG_H
+# include "config.h"
+# endif
+# include "sys.h"
+#endif
+struct process;
+
+
+#define ERTS_NUM_CODE_IX 3
+typedef unsigned ErtsCodeIndex;
+
+
+/* Called once at emulator initialization.
+ */
+void erts_code_ix_init(void);
+
+/* Return active code index.
+ * Is guaranteed to be valid until the calling BIF returns.
+ * To get a consistent view of the code, only one call to erts_active_code_ix()
+ * should be made and the returned ix reused within the same BIF call.
+ */
+ERTS_GLB_INLINE
+ErtsCodeIndex erts_active_code_ix(void);
+
+/* Return staging code ix.
+ * Only used by a process performing code loading/upgrading/deleting/purging.
+ * Code write permission must be seized.
+ */
+ERTS_GLB_INLINE
+ErtsCodeIndex erts_staging_code_ix(void);
+
+/* Try seize exclusive code write permission. Needed for code staging.
+ * Main process lock (only) must be held.
+ * System thread progress must not be blocked.
+ * Caller must not already hold the code write permission.
+ * Caller is suspended and *must* yield if 0 is returned.
+ */
+int erts_try_seize_code_write_permission(struct process* c_p);
+
+/* Release code write permission.
+ * Will resume any suspended waiters.
+ */
+void erts_release_code_write_permission(void);
+
+/* Prepare the "staging area" to be a complete copy of the active code.
+ * Code write permission must have been seized.
+ * Must be followed by calls to either "end" and "commit" or "abort" before
+ * code write permission can be released.
+ */
+void erts_start_staging_code_ix(void);
+
+/* End the staging.
+ * Preceded by "start" and must be followed by "commit".
+ */
+void erts_end_staging_code_ix(void);
+
+/* Set staging code index as new active code index.
+ * Preceded by "end".
+ */
+void erts_commit_staging_code_ix(void);
+
+/* Abort the staging.
+ * Preceded by "start".
+ */
+void erts_abort_staging_code_ix(void);
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+int erts_has_code_write_permission(void);
+#endif
+
+
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+extern erts_smp_atomic32_t the_active_code_index;
+extern erts_smp_atomic32_t the_staging_code_index;
+
+ERTS_GLB_INLINE ErtsCodeIndex erts_active_code_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&the_active_code_index);
+}
+ERTS_GLB_INLINE ErtsCodeIndex erts_staging_code_ix(void)
+{
+ return erts_smp_atomic32_read_nob(&the_staging_code_index);
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* !__CODE_IX_H__ */
+
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 025258e8de..0781665f05 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -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
@@ -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);
@@ -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) {
@@ -1312,7 +1395,7 @@ int erts_net_message(Port *prt,
if (is_not_pid(from) || is_not_atom(to)){
goto invalid_message;
}
- rp = erts_whereis_process(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_whereis_process(NULL, 0, to, 0, 0);
if (rp) {
Uint xsize = (type == DOP_REG_SEND
? 0
@@ -1338,7 +1421,6 @@ int erts_net_message(Port *prt,
erts_queue_dist_message(rp, &locks, ede_copy, token);
if (locks)
erts_smp_proc_unlock(rp, locks);
- erts_smp_proc_dec_refc(rp);
}
break;
@@ -1364,7 +1446,7 @@ int erts_net_message(Port *prt,
if (is_not_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_proc_lookup(to);
if (rp) {
Uint xsize = type == DOP_SEND ? 0 : ERTS_HEAP_FRAG_SIZE(token_size);
ErtsProcLocks locks = 0;
@@ -1388,7 +1470,6 @@ int erts_net_message(Port *prt,
erts_queue_dist_message(rp, &locks, ede_copy, token);
if (locks)
erts_smp_proc_unlock(rp, locks);
- erts_smp_proc_dec_refc(rp);
}
break;
@@ -1434,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);
@@ -1485,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.
@@ -1544,8 +1625,7 @@ int erts_net_message(Port *prt,
if (is_not_pid(from) || is_not_internal_pid(to)) {
goto invalid_message;
}
- rp = erts_pid2proc_opt(NULL, 0, to, rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_pid2proc(NULL, 0, to, rp_locks);
if (rp) {
(void) erts_send_exit_signal(NULL,
from,
@@ -1556,7 +1636,6 @@ int erts_net_message(Port *prt,
NULL,
0);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
break;
}
@@ -1601,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;
}
@@ -1650,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;
@@ -1697,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);
@@ -1730,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);
}
}
@@ -1783,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);
}
@@ -1816,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,
@@ -1870,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,
@@ -1907,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));
@@ -1929,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;
}
@@ -1956,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;
@@ -1973,15 +2047,15 @@ erts_dist_command(Port *prt, int reds_limit)
bw(foq.first->extp, size);
#endif
reds += ERTS_PORT_REDS_DIST_CMD_DATA(size);
+ erts_smp_atomic_add_nob(&erts_bytes_out, size);
fob = foq.first;
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;
@@ -1989,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;
@@ -1999,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 */
@@ -2043,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'
@@ -2056,16 +2132,15 @@ erts_dist_command(Port *prt, int reds_limit)
bw(oq.first->extp, size);
#endif
reds += ERTS_PORT_REDS_DIST_CMD_DATA(size);
+ erts_smp_atomic_add_nob(&erts_bytes_out, size);
fob = oq.first;
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);
@@ -2093,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;
@@ -2139,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
@@ -2201,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,
@@ -2242,8 +2321,8 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp)
void *arg = ((struct print_to_data *) vptdp)->arg;
Process *rp;
ErtsMonitor *rmon;
- rp = erts_pid2proc_unlocked(mon->pid);
- if (!rp || (rmon = erts_lookup_monitor(rp->monitors, mon->ref)) == NULL) {
+ rp = erts_proc_lookup(mon->pid);
+ 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 */
@@ -2281,7 +2360,7 @@ static void doit_print_link_info2(ErtsLink *lnk, void *vpplc)
static void doit_print_link_info(ErtsLink *lnk, void *vptdp)
{
- if (is_internal_pid(lnk->pid) && erts_pid2proc_unlocked(lnk->pid)) {
+ if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid)) {
PrintLinkContext plc = {(struct print_to_data *) vptdp, lnk->pid};
erts_doforall_links(ERTS_LINK_ROOT(lnk), &doit_print_link_info2, &plc);
}
@@ -2303,7 +2382,7 @@ static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext)
{
PrintNodeLinkContext *pcontext = vpcontext;
- if (is_internal_pid(lnk->pid) && erts_pid2proc_unlocked(lnk->pid))
+ if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid))
erts_print(pcontext->ptd.to, pcontext->ptd.arg,
"Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname);
}
@@ -2451,15 +2530,15 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
goto error;
/* Check that all trap functions are defined !! */
- if (dsend2_trap->address == NULL ||
- dsend3_trap->address == NULL ||
+ if (dsend2_trap->addressv[0] == NULL ||
+ dsend3_trap->addressv[0] == NULL ||
/* dsend_nosuspend_trap->address == NULL ||*/
- dlink_trap->address == NULL ||
- dunlink_trap->address == NULL ||
- dmonitor_node_trap->address == NULL ||
- dgroup_leader_trap->address == NULL ||
- dmonitor_p_trap->address == NULL ||
- dexit_trap->address == NULL) {
+ dlink_trap->addressv[0] == NULL ||
+ dunlink_trap->addressv[0] == NULL ||
+ dmonitor_node_trap->addressv[0] == NULL ||
+ dgroup_leader_trap->addressv[0] == NULL ||
+ dmonitor_p_trap->addressv[0] == NULL ||
+ dexit_trap->addressv[0] == NULL) {
goto error;
}
@@ -2471,9 +2550,9 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
/* By setting dist_entry==erts_this_dist_entry and DISTRIBUTION on
net_kernel do_net_exist will be called when net_kernel
is terminated !! */
- (void *) ERTS_PROC_SET_DIST_ENTRY(net_kernel,
- ERTS_PROC_LOCK_MAIN,
- erts_this_dist_entry);
+ (void) ERTS_PROC_SET_DIST_ENTRY(net_kernel,
+ ERTS_PROC_LOCK_MAIN,
+ erts_this_dist_entry);
erts_refc_inc(&erts_this_dist_entry->refc, 2);
net_kernel->flags |= F_DISTRIBUTION;
@@ -2488,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);
@@ -2556,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",
@@ -2578,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)
@@ -2596,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;
}
@@ -2610,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(ERTS_Port2ErlDrvPort(pp), &disable, NULL);
+ }
pp->dist_entry = dep;
@@ -2642,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,
@@ -2655,7 +2746,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3)
}
if (pp)
- erts_smp_port_unlock(pp);
+ erts_port_release(pp);
return ret;
@@ -2699,16 +2790,15 @@ 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);
}
else {
lp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- lp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
- local, lp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ lp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ local, lp_locks);
if (!lp) {
BIF_RET(am_true); /* ignore */
}
@@ -2727,14 +2817,18 @@ BIF_RETTYPE dist_exit_3(BIF_ALIST_3)
lp_locks &= ~ERTS_PROC_LOCK_MAIN;
#endif
erts_smp_proc_unlock(lp, lp_locks);
- if (lp != BIF_P)
- erts_smp_proc_dec_refc(lp);
- else {
+ 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)
@@ -2932,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));
}
}
@@ -3510,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..ff8f5e106f 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -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
@@ -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/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index 570cc59be2..5850f80843 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -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..cf7b99c463 100644
--- a/erts/emulator/beam/erl_afit_alloc.h
+++ b/erts/emulator/beam/erl_afit_alloc.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2009. 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
@@ -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 6fce032f9d..d748f86d75 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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
@@ -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();
@@ -2182,9 +2197,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
if (want.code) {
size.code = module_table_sz();
size.code += export_table_sz();
- size.code += export_list_size() * sizeof(Export);
+ size.code += export_entries_sz();
size.code += erts_fun_table_sz();
- size.code += allocated_modules*sizeof(Range);
+ size.code += erts_ranges_sz();
size.code += erts_total_code_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);
@@ -2332,7 +2345,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].arity = 2;
values[i].name = "export_list";
- values[i].ui[0] = export_list_size() * sizeof(Export);
+ values[i].ui[0] = export_entries_sz();
i++;
values[i].arity = 2;
@@ -2347,7 +2360,7 @@ erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc)
values[i].arity = 2;
values[i].name = "module_refs";
- values[i].ui[0] = allocated_modules*sizeof(Range);
+ values[i].ui[0] = erts_ranges_sz();
i++;
values[i].arity = 2;
@@ -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 4aa8fa82fb..5e3615ccc2 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -1,7 +1,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
@@ -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
@@ -164,6 +167,7 @@ type MSG_REF FIXED_SIZE PROCESSES msg_ref
type MSG_ROOTS TEMPORARY PROCESSES msg_roots
type ROOTSET TEMPORARY PROCESSES root_set
type LOADER_TMP TEMPORARY CODE loader_tmp
+type PREPARED_CODE SHORT_LIVED CODE prepared_code
type BIF_TIMER_TABLE LONG_LIVED SYSTEM bif_timer_table
type SL_BIF_TIMER SHORT_LIVED PROCESSES bif_timer_sl
type LL_BIF_TIMER STANDARD PROCESSES bif_timer_ll
@@ -188,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
@@ -196,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
@@ -233,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
@@ -263,6 +268,10 @@ type ZLIB STANDARD SYSTEM zlib
type CPU_GRPS_MAP LONG_LIVED SYSTEM cpu_groups_map
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;
@@ -321,6 +330,8 @@ type SL_PTIMER SHORT_LIVED SYSTEM ptimer_sl
type LL_PTIMER STANDARD SYSTEM ptimer_ll
type SYS_MSG_Q SHORT_LIVED PROCESSES system_messages_queue
type FP_EXCEPTION LONG_LIVED SYSTEM fp_exception
+type LL_MPATHS LONG_LIVED SYSTEM ll_migration_paths
+type SL_MPATHS SHORT_LIVED SYSTEM sl_migration_paths
+endif
+if hipe
@@ -414,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 97ba306a79..ac7f420708 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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,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), \
- (*((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) & ~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), \
+ (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,15 +637,15 @@ 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;
}
static ERTS_INLINE void
-alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size)
+alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
{
- erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, &allctr->mseg_opt);
+ erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, flags, &allctr->mseg_opt);
INC_CC(allctr->calls.mseg_dealloc);
}
@@ -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
@@ -1209,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. */
@@ -1242,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)
@@ -1262,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);
@@ -1291,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);
@@ -1348,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);
@@ -1370,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;
@@ -1381,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);
@@ -1400,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)
@@ -1416,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
@@ -1472,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);
@@ -1497,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;
@@ -1516,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,
@@ -1541,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);
@@ -1594,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;
@@ -1610,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... */
@@ -1624,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);
@@ -1633,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);
@@ -1657,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);
@@ -1664,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));
@@ -1696,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);
}
}
@@ -1743,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);
@@ -1754,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;
@@ -1774,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);
}
}
@@ -1790,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);
@@ -1800,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);
@@ -1815,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)
@@ -1865,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);
+ SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC, allctr);
-#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
- crr_sz -= sizeof(UWord);
-#endif
+ blk = MBC_TO_FIRST_BLK(allctr, crr);
- blk_sz = UNIT_FLOOR(crr_sz - allctr->mbc_header_size);
+ blk_sz = UNIT_FLOOR(crr_sz - MBC_HEADER_SIZE(allctr));
- 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);
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);
@@ -1909,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
@@ -1952,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
@@ -1974,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))
@@ -2008,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
@@ -2057,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
@@ -2066,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);
@@ -2075,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);
-
-#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 - 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);
if (flags & CFLG_MAIN_CARRIER) {
ASSERT(!allctr->main_carrier);
@@ -2105,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);
@@ -2142,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));
@@ -2157,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,
@@ -2166,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,
@@ -2174,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 */
}
@@ -2184,7 +2276,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
(void *) BLK2UMEM(old_blk),
MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ);
unlink_carrier(&allctr->sbc_list, old_crr);
- alcu_mseg_dealloc(allctr, old_crr, old_crr_sz);
+ alcu_mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE);
}
else {
/* Old carrier unchanged; restore stat */
@@ -2196,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));
@@ -2208,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);
@@ -2218,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,
@@ -2260,13 +2352,12 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
Carrier_t *crr;
#if HAVE_ERTS_MSEG
Uint is_mseg = 0;
+ Uint mseg_flags = ERTS_MSEG_FLG_NONE;
#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));
@@ -2276,7 +2367,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
@@ -2287,7 +2378,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
@@ -2305,8 +2397,9 @@ 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);
+ mseg_flags = ERTS_MSEG_FLG_2POW;
}
else
#endif
@@ -2320,7 +2413,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
#if HAVE_ERTS_MSEG
if (is_mseg) {
- alcu_mseg_dealloc(allctr, crr, crr_sz);
+ alcu_mseg_dealloc(allctr, crr, crr_sz, mseg_flags);
}
else
#endif
@@ -2815,10 +2908,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);
@@ -3430,12 +3523,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
@@ -3506,24 +3594,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);
@@ -3644,21 +3728,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,
+ 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);
}
@@ -3739,13 +3822,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
@@ -3775,7 +3858,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);
}
}
@@ -3798,16 +3881,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
@@ -3966,16 +4045,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);
@@ -3985,56 +4063,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)
+ if (used_allctr != pref_allctr) {
enqueue_dealloc_other_instance(type,
used_allctr,
- ptr,
+ 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);
@@ -4111,7 +4166,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;
@@ -4120,20 +4175,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);
@@ -4143,6 +4207,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;
@@ -4158,7 +4239,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;
}
@@ -4203,59 +4284,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;
@@ -4303,9 +4351,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)
@@ -4319,17 +4367,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 */
@@ -4372,11 +4412,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;
@@ -4388,7 +4427,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);
}
@@ -4432,6 +4471,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
@@ -4441,34 +4487,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);
@@ -4481,18 +4530,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);
@@ -4500,44 +4547,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;
}
@@ -4559,4 +4602,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..6ce209085c 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -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 f308039baf..054d1a48f6 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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,7 +400,11 @@ 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);
@@ -601,7 +613,7 @@ long driver_async(ErlDrvPort ix, unsigned int* key,
#endif
prt = erts_drvport2port(ix);
- if (!prt)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
@@ -612,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..5625622e3f 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -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_binary.c b/erts/emulator/beam/erl_bif_binary.c
index cc4f2be8eb..0db19a1ee6 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -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
@@ -72,52 +72,29 @@ binary_matches(Process *p, Eterm arg1, Eterm arg2, Eterm arg3);
void erts_init_bif_binary(void)
{
- sys_memset((void *) &binary_match_trap_export, 0, sizeof(Export));
- binary_match_trap_export.address = &binary_match_trap_export.code[3];
- binary_match_trap_export.code[0] = am_erlang;
- binary_match_trap_export.code[1] = am_binary_match_trap;
- binary_match_trap_export.code[2] = 3;
- binary_match_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_match_trap_export.code[4] = (BeamInstr) &binary_match_trap;
-
- sys_memset((void *) &binary_matches_trap_export, 0, sizeof(Export));
- binary_matches_trap_export.address = &binary_matches_trap_export.code[3];
- binary_matches_trap_export.code[0] = am_erlang;
- binary_matches_trap_export.code[1] = am_binary_matches_trap;
- binary_matches_trap_export.code[2] = 3;
- binary_matches_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_matches_trap_export.code[4] = (BeamInstr) &binary_matches_trap;
-
- sys_memset((void *) &binary_longest_prefix_trap_export, 0, sizeof(Export));
- binary_longest_prefix_trap_export.address = &binary_longest_prefix_trap_export.code[3];
- binary_longest_prefix_trap_export.code[0] = am_erlang;
- binary_longest_prefix_trap_export.code[1] = am_binary_longest_prefix_trap;
- binary_longest_prefix_trap_export.code[2] = 3;
- binary_longest_prefix_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_longest_prefix_trap_export.code[4] = (BeamInstr) &binary_longest_prefix_trap;
-
- sys_memset((void *) &binary_longest_suffix_trap_export, 0, sizeof(Export));
- binary_longest_suffix_trap_export.address = &binary_longest_suffix_trap_export.code[3];
- binary_longest_suffix_trap_export.code[0] = am_erlang;
- binary_longest_suffix_trap_export.code[1] = am_binary_longest_suffix_trap;
- binary_longest_suffix_trap_export.code[2] = 3;
- binary_longest_suffix_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_longest_suffix_trap_export.code[4] = (BeamInstr) &binary_longest_suffix_trap;
-
- sys_memset((void *) &binary_bin_to_list_trap_export, 0, sizeof(Export));
- binary_bin_to_list_trap_export.address = &binary_bin_to_list_trap_export.code[3];
- binary_bin_to_list_trap_export.code[0] = am_erlang;
- binary_bin_to_list_trap_export.code[1] = am_binary_bin_to_list_trap;
- binary_bin_to_list_trap_export.code[2] = 3;
- binary_bin_to_list_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_bin_to_list_trap_export.code[4] = (BeamInstr) &binary_bin_to_list_trap;
- sys_memset((void *) &binary_copy_trap_export, 0, sizeof(Export));
- binary_copy_trap_export.address = &binary_copy_trap_export.code[3];
- binary_copy_trap_export.code[0] = am_erlang;
- binary_copy_trap_export.code[1] = am_binary_copy_trap;
- binary_copy_trap_export.code[2] = 2;
- binary_copy_trap_export.code[3] = (BeamInstr) em_apply_bif;
- binary_copy_trap_export.code[4] = (BeamInstr) &binary_copy_trap;
+ erts_init_trap_export(&binary_match_trap_export,
+ am_erlang, am_binary_match_trap, 3,
+ &binary_match_trap);
+
+ erts_init_trap_export(&binary_matches_trap_export,
+ am_erlang, am_binary_matches_trap, 3,
+ &binary_matches_trap);
+
+ erts_init_trap_export(&binary_longest_prefix_trap_export,
+ am_erlang, am_binary_longest_prefix_trap, 3,
+ &binary_longest_prefix_trap);
+
+ erts_init_trap_export(&binary_longest_suffix_trap_export,
+ am_erlang, am_binary_longest_suffix_trap, 3,
+ &binary_longest_suffix_trap);
+
+ erts_init_trap_export(&binary_bin_to_list_trap_export,
+ am_erlang, am_binary_bin_to_list_trap, 3,
+ &binary_bin_to_list_trap);
+
+ erts_init_trap_export(&binary_copy_trap_export,
+ am_erlang, am_binary_copy_trap, 2,
+ &binary_copy_trap);
max_loop_limit = 0;
return;
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index 06b7ffdf32..4302fe8f79 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -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
@@ -42,16 +42,9 @@ static Export chksum_md5_2_exp;
void erts_init_bif_chksum(void)
{
/* Non visual BIF to trap to. */
- memset(&chksum_md5_2_exp, 0, sizeof(Export));
- chksum_md5_2_exp.address =
- &chksum_md5_2_exp.code[3];
- chksum_md5_2_exp.code[0] = am_erlang;
- chksum_md5_2_exp.code[1] = am_atom_put("md5_trap",8);
- chksum_md5_2_exp.code[2] = 2;
- chksum_md5_2_exp.code[3] =
- (BeamInstr) em_apply_bif;
- chksum_md5_2_exp.code[4] =
- (BeamInstr) &md5_2;
+ erts_init_trap_export(&chksum_md5_2_exp,
+ am_erlang, am_atom_put("md5_trap",8), 2,
+ &md5_2);
}
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index c338ee1c4b..1c3e955f47 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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,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(ERTS_Port2ErlDrvPort(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,42 +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];
-#ifdef DDLL_SMP
- erts_smp_port_state_lock(prt);
-#endif
- if (!(prt->status & FREE_PORT_FLAGS) &&
- prt->drv_ptr->handle == dh) {
-#if DDLL_SMP
- erts_smp_atomic_inc_nob(&prt->refc);
- /* 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();
@@ -581,47 +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];
-#if DDLL_SMP
- erts_smp_port_state_lock(prt);
-#endif
- if (!(prt->status & FREE_PORT_FLAGS)
- && prt->drv_ptr->handle == dh) {
-#if DDLL_SMP
- erts_smp_atomic_inc_nob(&prt->refc);
- /* 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
@@ -791,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:
@@ -1049,40 +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];
-#if DDLL_SMP
- erts_smp_port_state_lock(prt);
-#endif
- if (!(prt->status & FREE_PORT_FLAGS) &&
- prt->drv_ptr->handle == dh) {
-#if DDLL_SMP
- erts_smp_atomic_inc_nob(&prt->refc);
- 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
@@ -1104,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,
@@ -1127,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);
}
@@ -1167,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));
@@ -1180,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) {
@@ -1408,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) {
@@ -1429,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) {
@@ -1456,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) {
@@ -1471,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;
@@ -1484,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;
@@ -1496,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;
@@ -1516,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;
@@ -1530,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;
@@ -1541,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;
@@ -1562,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;
@@ -1600,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;
@@ -1626,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) {
@@ -1666,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;
@@ -1704,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;
@@ -1742,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;
@@ -1775,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) {
@@ -1875,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) {
@@ -1897,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';
@@ -1918,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 45dc5fb11c..8582a8954b 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -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
@@ -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
@@ -67,7 +69,11 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE
" [no-c-stack-objects]"
#endif
#ifndef OTP_RELEASE
+#ifdef ERLANG_GIT_VERSION
+ " [source-" ERLANG_GIT_VERSION "]"
+#else
" [source]"
+#endif
#endif
#ifdef ARCH_64
#if HALFWORD_HEAP
@@ -128,8 +134,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 +877,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 +912,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 +1004,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 +1052,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 +1211,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 +1229,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 +1266,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 +1332,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);
@@ -1338,13 +1340,15 @@ process_info_aux(Process *BIF_P,
hp = HAlloc(BIF_P, 3);
break;
- case am_trap_exit:
+ case am_trap_exit: {
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
hp = HAlloc(BIF_P, 3);
- if (rp->flags & F_TRAPEXIT)
+ if (state & ERTS_PSFLG_TRAP_EXIT)
res = am_true;
else
res = am_false;
break;
+ }
case am_error_handler:
hp = HAlloc(BIF_P, 3);
@@ -1424,8 +1428,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);
@@ -1498,7 +1502,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: {
@@ -1603,7 +1607,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;
/*
@@ -1835,17 +1839,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';
@@ -2157,9 +2161,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
@@ -2294,8 +2302,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);
@@ -2532,6 +2542,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;
@@ -2699,66 +2712,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:
@@ -2771,38 +2724,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;
@@ -2811,17 +2746,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;
@@ -2830,79 +2774,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);
@@ -2916,51 +2877,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)
{
@@ -3092,21 +3074,16 @@ 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_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
- BIF_ARG_1, ERTS_PROC_LOCK_STATUS);
+ rp = erts_proc_lookup(BIF_ARG_1);
if (!rp) {
BIF_RET(am_false);
}
else {
- int have_pending_exit = ERTS_PROC_PENDING_EXIT(rp);
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- if (have_pending_exit)
+ if (erts_smp_atomic32_read_acqb(&rp->state)
+ & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING))
ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_false);
else
BIF_RET(am_true);
@@ -3315,12 +3292,11 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
else if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1)
|| ERTS_IS_ATOM_STR("next_port", BIF_ARG_1)) {
/* Used by node_container_SUITE (emulator) */
- Eterm res;
+ Sint 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 +3332,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 +3397,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 +3442,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 +3585,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,
@@ -3671,13 +3650,12 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
Uint next;
if (term_to_Uint(BIF_ARG_2, &next) != 0) {
- Eterm res;
+ Sint 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));
@@ -3710,9 +3688,8 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
&& is_internal_pid(tp[2])) {
int xres;
ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
- Process *rp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
- tp[2], rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
+ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ tp[2], rp_locks);
if (!rp) {
DECL_AM(dead);
BIF_RET(AM_dead);
@@ -3738,7 +3715,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
#endif
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
if (xres > 1) {
DECL_AM(message);
BIF_RET(AM_message);
@@ -3903,7 +3879,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);
@@ -3940,18 +3916,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_pid2proc_unlocked(lock->id);
- if (proc && proc->reg) {
- id = proc->reg->name;
+ proc = erts_proc_lookup(lock->id);
+ if (proc && proc->common.u.alive.reg) {
+ id = proc->common.u.alive.reg->name;
} else {
/* otherwise use process id */
id = lock->id;
@@ -3986,12 +3962,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);
@@ -4127,14 +4103,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..adac0052d6 100644
--- a/erts/emulator/beam/erl_bif_op.c
+++ b/erts/emulator/beam/erl_bif_op.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2010. 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
@@ -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 831e05493a..1062d4379b 100644
--- a/erts/emulator/beam/erl_bif_os.c
+++ b/erts/emulator/beam/erl_bif_os.c
@@ -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));
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index f9009166c0..44fa41c7b6 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,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)
-{
- 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);
- }
- if (erts_system_profile_flags.runnable_procs && erts_system_profile_flags.exclusive) {
- profile_runnable_proc(BIF_P, am_inactive);
- }
+/*
+ * erts_internal:port_connect/2 is used by the
+ * erlang:port_connect/2 BIF.
+ */
+BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2)
+{
+ Eterm ref;
+ Port* prt;
- 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);
- }
+ prt = lookup_port(BIF_P, BIF_ARG_1);
+ if (!prt)
+ BIF_RET(am_badarg);
- /* 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);
- }
+#ifdef DEBUG
+ ref = NIL;
+#endif
- 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);
+ 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 (erts_system_profile_flags.runnable_ports && !erts_port_is_scheduled(p)) {
- profile_runnable_port(p, am_inactive);
- }
+BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1)
+{
+ Eterm retval;
+ Port* prt;
- 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 (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);
}
-
- 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;
@@ -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) {
@@ -902,9 +767,9 @@ 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;
@@ -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 */
diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c
index 6b843d2e08..3d34c2a77f 100644
--- a/erts/emulator/beam/erl_bif_re.c
+++ b/erts/emulator/beam/erl_bif_re.c
@@ -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
@@ -71,14 +71,9 @@ void erts_init_bif_re(void)
erts_pcre_stack_free = &erts_erts_pcre_stack_free;
default_table = NULL; /* ISO8859-1 default, forced into pcre */
max_loop_limit = CONTEXT_REDS * LOOP_FACTOR;
-
- sys_memset((void *) &re_exec_trap_export, 0, sizeof(Export));
- re_exec_trap_export.address = &re_exec_trap_export.code[3];
- re_exec_trap_export.code[0] = am_erlang;
- re_exec_trap_export.code[1] = am_re_run_trap;
- re_exec_trap_export.code[2] = 3;
- re_exec_trap_export.code[3] = (BeamInstr) em_apply_bif;
- re_exec_trap_export.code[4] = (BeamInstr) &re_exec_trap;
+
+ erts_init_trap_export(&re_exec_trap_export, am_erlang, am_re_run_trap, 3,
+ &re_exec_trap);
grun_trap_exportp = erts_export_put(am_re,am_grun,3);
urun_trap_exportp = erts_export_put(am_re,am_urun,3);
@@ -189,6 +184,7 @@ static Eterm make_signed_integer(int x, Process *p)
#define CAPSPEC_VALUES 0
#define CAPSPEC_TYPE 1
#define CAPSPEC_SIZE 2
+#define CAPSPEC_INIT {0,0}
static int /* 0 == ok, < 0 == error */
parse_options(Eterm listp, /* in */
@@ -418,7 +414,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;
@@ -449,7 +445,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);
}
@@ -802,7 +798,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;
}
@@ -814,7 +810,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';
@@ -858,7 +854,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;
@@ -869,7 +865,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3)
size_t code_size;
Uint loop_limit_tmp;
unsigned long loop_count;
- Eterm capture[CAPSPEC_SIZE];
+ Eterm capture[CAPSPEC_SIZE] = CAPSPEC_INIT;
int is_list_cap;
if (parse_options(arg3,&comp_options,&options,&pflags,&startoffset,capture)
@@ -882,7 +878,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;
@@ -901,7 +897,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);
}
@@ -1044,7 +1040,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 d806be0704..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;
}
@@ -324,10 +324,9 @@ bif_timer_timeout(ErtsBifTimer* btm)
ASSERT(!erts_get_current_process());
if (btm->flags & BTM_FLG_BYNAME)
- rp = erts_whereis_process(NULL,0,btm->receiver.name,0,ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = erts_whereis_process(NULL, 0, btm->receiver.name, 0, 0);
else {
rp = btm->receiver.proc.ess;
- erts_smp_proc_inc_refc(rp);
unlink_proc(btm);
}
@@ -379,7 +378,6 @@ bif_timer_timeout(ErtsBifTimer* btm)
#endif
);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
}
}
@@ -615,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",
@@ -639,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));
@@ -649,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();
}
@@ -698,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 80f774523c..41c98bbffb 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -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
@@ -42,14 +42,33 @@
#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
const struct trace_pattern_flags erts_trace_pattern_flags_off = {0, 0, 0, 0, 0};
+
+/*
+ * The following variables are protected by code write permission.
+ */
static int erts_default_trace_pattern_is_on;
static Binary *erts_default_match_spec;
static Binary *erts_default_meta_match_spec;
static struct trace_pattern_flags erts_default_trace_pattern_flags;
static Eterm erts_default_meta_tracer_pid;
+static struct { /* Protected by code write permission */
+ int current;
+ int install;
+ 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);
@@ -60,12 +79,11 @@ static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
-static int setup_func_trace(Export* ep, void* match_prog);
-static int reset_func_trace(Export* ep);
-static void reset_bif_trace(int bif_index);
-static void setup_bif_trace(int bif_index);
-static void set_trace_bif(int bif_index, void* match_prog);
-static void clear_trace_bif(int bif_index);
+static void reset_bif_trace(void);
+static void setup_bif_trace(void);
+static void install_exp_breakpoints(BpFunctions* f);
+static void uninstall_exp_breakpoints(BpFunctions* f);
+static void clean_export_entries(BpFunctions* f);
void
erts_bif_trace_init(void)
@@ -98,7 +116,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
{
DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */
int i;
- int matches = 0;
+ int matches = -1;
int specified = 0;
enum erts_break_op on;
Binary* match_prog_set;
@@ -106,10 +124,12 @@ 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;
+ Eterm meta_tracer_pid = p->common.id;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
- erts_smp_thr_progress_block();
+ if (!erts_try_seize_code_write_permission(p)) {
+ ERTS_BIF_YIELD3(bif_export[BIF_trace_pattern_3], p, MFA, Pattern, flaglist);
+ }
+ finish_bp.current = -1;
UseTmpHeap(3,p);
/*
@@ -151,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;
}
@@ -234,14 +252,13 @@ 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);
erts_default_meta_match_spec = NULL;
erts_default_meta_tracer_pid = NIL;
}
- MatchSetUnref(match_prog_set);
if (erts_default_trace_pattern_flags.breakpoint &&
flags.breakpoint) {
/* Breakpoint trace -> breakpoint trace */
@@ -297,8 +314,7 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
erts_default_trace_pattern_is_on = !!flags.breakpoint;
}
}
-
- goto done;
+ matches = 0;
} else if (is_tuple(MFA)) {
Eterm *tp = tuple_val(MFA);
if (tp[0] != make_arityval(3)) {
@@ -322,36 +338,64 @@ trace_pattern(Process* p, Eterm MFA, Eterm Pattern, Eterm flaglist)
if (is_small(mfa[2])) {
mfa[2] = signed_val(mfa[2]);
}
- } else {
- goto error;
- }
- if (meta_tracer_proc) {
- meta_tracer_proc->trace_flags |= F_TRACER;
- }
+ if (meta_tracer_proc) {
+ ERTS_TRACE_FLAGS(meta_tracer_proc) |= F_TRACER;
+ }
+ matches = erts_set_trace_pattern(p, mfa, specified,
+ match_prog_set, match_prog_set,
+ on, flags, meta_tracer_pid, 0);
+ }
- matches = erts_set_trace_pattern(mfa, specified,
- match_prog_set, match_prog_set,
- on, flags, meta_tracer_pid);
+ error:
MatchSetUnref(match_prog_set);
-
- done:
UnUseTmpHeap(3,p);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- return make_small(matches);
+#ifdef ERTS_SMP
+ if (finish_bp.current >= 0) {
+ ASSERT(matches >= 0);
+ 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));
+ }
+#endif
- error:
+ erts_release_code_write_permission();
- MatchSetUnref(match_prog_set);
+ if (matches >= 0) {
+ return make_small(matches);
+ }
+ else {
+ BIF_ERROR(p, BADARG);
+ }
+}
- UnUseTmpHeap(3,p);
- erts_smp_thr_progress_unblock();
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
- BIF_ERROR(p, BADARG);
+#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_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);
+ }
}
+#endif /* ERTS_SMP */
void
erts_get_default_trace_pattern(int *trace_pattern_is_on,
@@ -360,6 +404,8 @@ 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_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;
if (match_spec)
@@ -372,7 +418,12 @@ erts_get_default_trace_pattern(int *trace_pattern_is_on,
*meta_tracer_pid = erts_default_meta_tracer_pid;
}
-
+int erts_is_default_trace_enabled(void)
+{
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission() ||
+ erts_smp_thr_progress_is_blocking());
+ return erts_default_trace_pattern_is_on;
+}
Uint
erts_trace_flag2bit(Eterm flag)
@@ -466,27 +517,31 @@ Eterm trace_3(BIF_ALIST_3)
BIF_ERROR(p, BADARG);
}
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ ERTS_BIF_YIELD3(bif_export[BIF_trace_3], BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_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;
@@ -497,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;
@@ -519,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)) {
@@ -570,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
@@ -651,48 +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++) {
- Process* tracee_p = process_tab[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. */
@@ -730,6 +796,7 @@ Eterm trace_3(BIF_ALIST_3)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
}
#endif
+ erts_release_code_write_permission();
BIF_RET(make_small(matches));
@@ -745,6 +812,7 @@ Eterm trace_3(BIF_ALIST_3)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
}
#endif
+ erts_release_code_write_permission();
BIF_ERROR(p, BADARG);
}
@@ -759,21 +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_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
- tracee_port->tracer_proc, 0);
+ 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. */
@@ -783,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;
@@ -800,21 +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_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
- tracee_p->tracer_proc, 0);
+ 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. */
@@ -824,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;
@@ -841,6 +909,11 @@ Eterm trace_info_2(BIF_ALIST_2)
Eterm What = BIF_ARG_1;
Eterm Key = BIF_ARG_2;
Eterm res;
+
+ if (!erts_try_seize_code_write_permission(p)) {
+ ERTS_BIF_YIELD2(bif_export[BIF_trace_info_2], p, What, Key);
+ }
+
if (What == am_on_load) {
res = trace_info_on_load(p, Key);
} else if (is_atom(What) || is_pid(What)) {
@@ -848,8 +921,10 @@ Eterm trace_info_2(BIF_ALIST_2)
} else if (is_tuple(What)) {
res = trace_info_func(p, What, Key);
} else {
+ erts_release_code_write_permission();
BIF_ERROR(p, BADARG);
}
+ erts_release_code_write_permission();
BIF_RET(res);
}
@@ -862,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);
@@ -871,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_pid2proc(p, ERTS_PROC_LOCK_MAIN, tracer, 0)) {
+ 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)) {
@@ -977,64 +1051,54 @@ static int function_is_traced(Process *p,
Binary **ms, /* out */
Binary **ms_meta, /* out */
Eterm *tracer_pid_meta, /* out */
- Sint *count, /* out */
+ Uint *count, /* out */
Eterm *call_time) /* out */
{
Export e;
Export* ep;
- int i;
- BeamInstr *code;
+ BeamInstr* pc;
/* First look for an export entry */
e.code[0] = mfa[0];
e.code[1] = mfa[1];
e.code[2] = mfa[2];
if ((ep = export_get(&e)) != NULL) {
- if (ep->address == ep->code+3 &&
- ep->code[3] != (BeamInstr) em_call_error_handler) {
- if (ep->code[3] == (BeamInstr) em_call_traced_function) {
- *ms = ep->match_prog_set;
+ pc = ep->code+3;
+ if (ep->addressv[erts_active_code_ix()] == pc &&
+ *pc != (BeamInstr) em_call_error_handler) {
+
+ int r = 0;
+
+ ASSERT(*pc == (BeamInstr) em_apply_bif ||
+ *pc == (BeamInstr) BeamOp(op_i_generic_breakpoint));
+
+ if (erts_is_trace_break(pc, ms, 0)) {
return FUNC_TRACE_GLOBAL_TRACE;
}
- if (ep->code[3] == (BeamInstr) em_apply_bif) {
- for (i = 0; i < BIF_SIZE; ++i) {
- if (bif_export[i] == ep) {
- int r = 0;
-
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_GLOBAL) {
- *ms = ep->match_prog_set;
- return FUNC_TRACE_GLOBAL_TRACE;
- } else {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_LOCAL) {
- r |= FUNC_TRACE_LOCAL_TRACE;
- *ms = ep->match_prog_set;
- }
- if (erts_is_mtrace_break(ep->code+3, ms_meta,
- tracer_pid_meta)) {
- r |= FUNC_TRACE_META_TRACE;
- }
- if (erts_is_time_break(p, ep->code+3, call_time)) {
- r |= FUNC_TRACE_TIME_TRACE;
- }
- }
- return r ? r : FUNC_TRACE_UNTRACED;
- }
- }
- erl_exit(1,"Impossible ghost bif encountered in trace_info.");
+
+ if (erts_is_trace_break(pc, ms, 1)) {
+ r |= FUNC_TRACE_LOCAL_TRACE;
+ }
+ if (erts_is_mtrace_break(pc, ms_meta, tracer_pid_meta)) {
+ r |= FUNC_TRACE_META_TRACE;
}
+ if (erts_is_time_break(p, pc, call_time)) {
+ r |= FUNC_TRACE_TIME_TRACE;
+ }
+ return r ? r : FUNC_TRACE_UNTRACED;
}
}
/* OK, now look for breakpoint tracing */
- if ((code = erts_find_local_func(mfa)) != NULL) {
+ if ((pc = erts_find_local_func(mfa)) != NULL) {
int r =
- (erts_is_trace_break(code, ms, NULL)
+ (erts_is_trace_break(pc, ms, 1)
? FUNC_TRACE_LOCAL_TRACE : 0)
- | (erts_is_mtrace_break(code, ms_meta, tracer_pid_meta)
+ | (erts_is_mtrace_break(pc, ms_meta, tracer_pid_meta)
? FUNC_TRACE_META_TRACE : 0)
- | (erts_is_count_break(code, count)
+ | (erts_is_count_break(pc, count)
? FUNC_TRACE_COUNT_TRACE : 0)
- | (erts_is_time_break(p, code, call_time)
+ | (erts_is_time_break(p, pc, call_time)
? FUNC_TRACE_TIME_TRACE : 0);
return r ? r : FUNC_TRACE_UNTRACED;
@@ -1049,7 +1113,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
Eterm* hp;
DeclareTmpHeap(mfa,3,p); /* Not really heap here, but might be when setting pattern */
Binary *ms = NULL, *ms_meta = NULL;
- Sint count = 0;
+ Uint count = 0;
Eterm traced = am_false;
Eterm match_spec = am_false;
Eterm retval = am_false;
@@ -1137,9 +1201,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key)
break;
case am_call_count:
if (r & FUNC_TRACE_COUNT_TRACE) {
- retval = count < 0 ?
- erts_make_integer(-count-1, p) :
- erts_make_integer(count, p);
+ retval = erts_make_integer(count, p);
}
break;
case am_call_time:
@@ -1328,38 +1390,53 @@ trace_info_on_load(Process* p, Eterm key)
#undef FUNC_TRACE_LOCAL_TRACE
int
-erts_set_trace_pattern(Eterm* mfa, int specified,
+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 flags,
- Eterm meta_tracer_pid)
+ Eterm meta_tracer_pid, int is_blocking)
{
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
int matches = 0;
int i;
+ int n;
+ BpFunction* fp;
/*
* First work on normal functions (not real BIFs).
*/
-
- for (i = 0; i < export_list_size(); i++) {
- Export* ep = export_list(i);
- int j;
-
- if (ExportIsBuiltIn(ep)) {
- continue;
- }
-
- for (j = 0; j < specified && mfa[j] == ep->code[j]; j++) {
- /* Empty loop body */
- }
- if (j == specified) {
- if (on) {
- if (! flags.breakpoint)
- matches += setup_func_trace(ep, match_prog_set);
- else
- reset_func_trace(ep);
- } else if (! flags.breakpoint) {
- matches += reset_func_trace(ep);
+ erts_bp_match_export(&finish_bp.e, mfa, specified);
+ fp = finish_bp.e.matching;
+ n = finish_bp.e.matched;
+
+ for (i = 0; i < n; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *)(((char *)(pc-3)) - offsetof(Export, code));
+
+ if (on && !flags.breakpoint) {
+ /* Turn on global call tracing */
+ if (ep->addressv[code_ix] != pc) {
+ fp[i].mod->curr.num_traced_exports++;
+#ifdef DEBUG
+ pc[-5] = (BeamInstr) BeamOp(op_i_func_info_IaaI);
+#endif
+ pc[0] = (BeamInstr) BeamOp(op_jump_f);
+ pc[1] = (BeamInstr) ep->addressv[code_ix];
+ }
+ erts_set_call_trace_bif(pc, match_prog_set, 0);
+ 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);
}
}
}
@@ -1384,26 +1461,15 @@ erts_set_trace_pattern(Eterm* mfa, int specified,
/* Empty loop body */
}
if (j == specified) {
+ BeamInstr* pc = (BeamInstr *)bif_export[i]->code + 3;
+
if (! flags.breakpoint) { /* Export entry call trace */
if (on) {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_META) {
- ASSERT(ExportIsBuiltIn(bif_export[i]));
- erts_clear_mtrace_bif
- ((BeamInstr *)bif_export[i]->code + 3);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_META;
- }
- set_trace_bif(i, match_prog_set);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_LOCAL;
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_GLOBAL;
- setup_bif_trace(i);
+ erts_clear_call_trace_bif(pc, 1);
+ erts_clear_mtrace_bif(pc);
+ erts_set_call_trace_bif(pc, match_prog_set, 0);
} else { /* off */
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_GLOBAL) {
- clear_trace_bif(i);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL;
- }
- if (! erts_bif_trace_flags[i]) {
- reset_bif_trace(i);
- }
+ erts_clear_call_trace_bif(pc, 0);
}
matches++;
} else { /* Breakpoint call trace */
@@ -1411,52 +1477,33 @@ erts_set_trace_pattern(Eterm* mfa, int specified,
if (on) {
if (flags.local) {
- set_trace_bif(i, match_prog_set);
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_LOCAL;
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL;
+ erts_clear_call_trace_bif(pc, 0);
+ erts_set_call_trace_bif(pc, match_prog_set, 1);
m = 1;
}
if (flags.meta) {
- erts_set_mtrace_bif
- ((BeamInstr *)bif_export[i]->code + 3,
- meta_match_prog_set, meta_tracer_pid);
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_META;
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_GLOBAL;
+ erts_set_mtrace_bif(pc, meta_match_prog_set,
+ meta_tracer_pid);
m = 1;
}
if (flags.call_time) {
- erts_set_time_trace_bif(bif_export[i]->code + 3, on);
+ erts_set_time_trace_bif(pc, on);
/* I don't want to remove any other tracers */
- erts_bif_trace_flags[i] |= BIF_TRACE_AS_CALL_TIME;
m = 1;
}
- if (erts_bif_trace_flags[i]) {
- setup_bif_trace(i);
- }
} else { /* off */
if (flags.local) {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_LOCAL) {
- clear_trace_bif(i);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_LOCAL;
- }
+ erts_clear_call_trace_bif(pc, 1);
m = 1;
}
if (flags.meta) {
- if (erts_bif_trace_flags[i] & BIF_TRACE_AS_META) {
- erts_clear_mtrace_bif
- ((BeamInstr *)bif_export[i]->code + 3);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_META;
- }
+ erts_clear_mtrace_bif(pc);
m = 1;
}
if (flags.call_time) {
- erts_clear_time_trace_bif(bif_export[i]->code + 3);
- erts_bif_trace_flags[i] &= ~BIF_TRACE_AS_CALL_TIME;
+ erts_clear_time_trace_bif(pc);
m = 1;
}
- if (! erts_bif_trace_flags[i]) {
- reset_bif_trace(i);
- }
}
matches += m;
}
@@ -1466,176 +1513,241 @@ erts_set_trace_pattern(Eterm* mfa, int specified,
/*
** So, now for breakpoint tracing
*/
+ erts_bp_match_functions(&finish_bp.f, mfa, specified);
if (on) {
if (! flags.breakpoint) {
- erts_clear_trace_break(mfa, specified);
- erts_clear_mtrace_break(mfa, specified);
- erts_clear_count_break(mfa, specified);
- erts_clear_time_break(mfa, specified);
+ erts_clear_all_breaks(&finish_bp.f);
} else {
- int m = 0;
if (flags.local) {
- m = erts_set_trace_break(mfa, specified, match_prog_set,
- am_true);
+ erts_set_trace_break(&finish_bp.f, match_prog_set);
}
if (flags.meta) {
- m = erts_set_mtrace_break(mfa, specified, meta_match_prog_set,
- meta_tracer_pid);
+ erts_set_mtrace_break(&finish_bp.f, meta_match_prog_set,
+ meta_tracer_pid);
}
if (flags.call_count) {
- m = erts_set_count_break(mfa, specified, on);
+ erts_set_count_break(&finish_bp.f, on);
}
if (flags.call_time) {
- m = erts_set_time_break(mfa, specified, on);
+ erts_set_time_break(&finish_bp.f, on);
}
- /* All assignments to 'm' above should give the same value,
- * so just use the last */
- matches += m;
}
} else {
- int m = 0;
if (flags.local) {
- m = erts_clear_trace_break(mfa, specified);
+ erts_clear_trace_break(&finish_bp.f);
}
if (flags.meta) {
- m = erts_clear_mtrace_break(mfa, specified);
+ erts_clear_mtrace_break(&finish_bp.f);
}
if (flags.call_count) {
- m = erts_clear_count_break(mfa, specified);
+ erts_clear_count_break(&finish_bp.f);
}
if (flags.call_time) {
- m = erts_clear_time_break(mfa, specified);
+ erts_clear_time_break(&finish_bp.f);
+ }
+ }
+
+ finish_bp.current = 0;
+ finish_bp.install = on;
+ finish_bp.local = flags.breakpoint;
+
+#ifdef ERTS_SMP
+ if (is_blocking) {
+ ERTS_SMP_LC_ASSERT(erts_smp_thr_progress_is_blocking());
+#endif
+ while (erts_finish_breakpointing()) {
+ /* Empty loop body */
}
- /* All assignments to 'm' above should give the same value,
- * so just use the last */
- matches += m;
+#ifdef ERTS_SMP
+ finish_bp.current = -1;
}
+#endif
+ if (flags.breakpoint) {
+ matches += finish_bp.f.matched;
+ } else {
+ matches += finish_bp.e.matched;
+ }
return matches;
}
-/*
- * Setup function tracing for the given exported function.
- *
- * Return Value: 1 if entry refers to a BIF or loaded function,
- * 0 if the entry refers to a function not loaded.
- */
-
-static int
-setup_func_trace(Export* ep, void* match_prog)
+int
+erts_finish_breakpointing(void)
{
- if (ep->address == ep->code+3) {
- if (ep->code[3] == (BeamInstr) em_call_error_handler) {
- return 0;
- } else if (ep->code[3] == (BeamInstr) em_call_traced_function) {
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = match_prog;
- MatchSetRef(ep->match_prog_set);
- return 1;
- } else {
- /*
- * We ignore apply/3 and anything else.
- */
- return 0;
- }
- }
-
+ ERTS_SMP_LC_ASSERT(erts_has_code_write_permission());
+
/*
- * Currently no trace support for native code.
+ * Memory barriers will be issued for all processes *before*
+ * each of the stages below. (Unless the other schedulers
+ * are blocked, in which case memory barriers will be issued
+ * when they are awaken.)
*/
- if (erts_is_native_break(ep->address)) {
+ switch (finish_bp.current++) {
+ case 0:
+ /*
+ * At this point, in all functions that are to be breakpointed,
+ * a pointer to a GenericBp struct has already been added,
+ *
+ * Insert the new breakpoints (if any) into the
+ * code. Different schedulers may see breakpoint instruction
+ * at different times, but it does not matter since the newly
+ * added breakpoints are disabled.
+ */
+ if (finish_bp.install) {
+ if (finish_bp.local) {
+ erts_install_breakpoints(&finish_bp.f);
+ } else {
+ install_exp_breakpoints(&finish_bp.e);
+ }
+ }
+ setup_bif_trace();
+ return 1;
+ case 1:
+ /*
+ * Switch index for the breakpoint data, activating the staged
+ * data. (Depending on the changes in the breakpoint data,
+ * that could either activate breakpoints or disable
+ * breakpoints.)
+ */
+ erts_commit_staged_bp();
+ return 1;
+ case 2:
+ /*
+ * Remove breakpoints instructions for disabled breakpoints
+ * (if any).
+ */
+ if (finish_bp.install) {
+ if (finish_bp.local) {
+ uninstall_exp_breakpoints(&finish_bp.e);
+ } else {
+ erts_uninstall_breakpoints(&finish_bp.f);
+ }
+ } else {
+ if (finish_bp.local) {
+ erts_uninstall_breakpoints(&finish_bp.f);
+ } else {
+ uninstall_exp_breakpoints(&finish_bp.e);
+ }
+ }
+ reset_bif_trace();
+ return 1;
+ case 3:
+ /*
+ * Now all breakpoints have either been inserted or removed.
+ * For all updated breakpoints, copy the active breakpoint
+ * data to the staged breakpoint data to make them equal
+ * (simplifying for the next time breakpoints are to be
+ * updated). If any breakpoints have been totally disabled,
+ * deallocate the GenericBp structs for them.
+ */
+ erts_consolidate_bif_bp_data();
+ clean_export_entries(&finish_bp.e);
+ erts_consolidate_bp_data(&finish_bp.e, 0);
+ erts_consolidate_bp_data(&finish_bp.f, 1);
+ erts_bp_free_matched_functions(&finish_bp.e);
+ erts_bp_free_matched_functions(&finish_bp.f);
return 0;
+ default:
+ ASSERT(0);
}
-
- ep->code[3] = (BeamInstr) em_call_traced_function;
- ep->code[4] = (BeamInstr) ep->address;
- ep->address = ep->code+3;
- ep->match_prog_set = match_prog;
- MatchSetRef(ep->match_prog_set);
- return 1;
+ return 0;
}
-static void setup_bif_trace(int bif_index) {
- Export *ep = bif_export[bif_index];
-
- ASSERT(ExportIsBuiltIn(ep));
- ASSERT(ep->code[4]);
- ep->code[4] = (BeamInstr) bif_table[bif_index].traced;
-}
+static void
+install_exp_breakpoints(BpFunctions* f)
+{
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
+ BpFunction* fp = f->matching;
+ Uint ne = f->matched;
+ Uint i;
+ Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
-static void set_trace_bif(int bif_index, void* match_prog) {
- Export *ep = bif_export[bif_index];
-
-#ifdef HARDDEBUG
- erts_fprintf(stderr, "set_trace_bif: %T:%T/%bpu\n",
- ep->code[0], ep->code[1], ep->code[2]);
-#endif
- ASSERT(ExportIsBuiltIn(ep));
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = match_prog;
- MatchSetRef(ep->match_prog_set);
-}
+ for (i = 0; i < ne; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *) (((char *)pc)-offset);
-/*
- * Reset function tracing for the given exported function.
- *
- * Return Value: 1 if entry refers to a BIF or loaded function,
- * 0 if the entry refers to a function not loaded.
- */
+ ep->addressv[code_ix] = pc;
+ }
+}
-static int
-reset_func_trace(Export* ep)
+static void
+uninstall_exp_breakpoints(BpFunctions* f)
{
- if (ep->address == ep->code+3) {
- if (ep->code[3] == (BeamInstr) em_call_error_handler) {
- return 0;
- } else if (ep->code[3] == (BeamInstr) em_call_traced_function) {
- ep->address = (Uint *) ep->code[4];
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = NULL;
- return 1;
- } else {
- /*
- * We ignore apply/3 and anything else.
- */
- return 0;
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
+ BpFunction* fp = f->matching;
+ Uint ne = f->matched;
+ Uint i;
+ Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
+
+ for (i = 0; i < ne; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *) (((char *)pc)-offset);
+
+ if (ep->addressv[code_ix] != pc) {
+ continue;
}
+ ASSERT(*pc == (BeamInstr) BeamOp(op_jump_f));
+ ep->addressv[code_ix] = (BeamInstr *) ep->code[4];
}
-
- /*
- * Currently no trace support for native code.
- */
- if (erts_is_native_break(ep->address)) {
- return 0;
- }
-
- /*
- * Nothing to do, but the export entry matches.
- */
+}
- return 1;
+static void
+clean_export_entries(BpFunctions* f)
+{
+ const ErtsCodeIndex code_ix = erts_active_code_ix();
+ BpFunction* fp = f->matching;
+ Uint ne = f->matched;
+ Uint i;
+ Uint offset = offsetof(Export, code) + 3*sizeof(BeamInstr);
+
+ for (i = 0; i < ne; i++) {
+ BeamInstr* pc = fp[i].pc;
+ Export* ep = (Export *) (((char *)pc)-offset);
+
+ if (ep->addressv[code_ix] == pc) {
+ continue;
+ }
+ if (*pc == (BeamInstr) BeamOp(op_jump_f)) {
+ ep->code[3] = (BeamInstr) 0;
+ ep->code[4] = (BeamInstr) 0;
+ }
+ }
}
-static void reset_bif_trace(int bif_index) {
- Export *ep = bif_export[bif_index];
-
- ASSERT(ExportIsBuiltIn(ep));
- ASSERT(ep->code[4]);
- ASSERT(! ep->match_prog_set);
- ASSERT(! erts_is_mtrace_break((BeamInstr *)ep->code+3, NULL, NULL));
- ep->code[4] = (BeamInstr) bif_table[bif_index].f;
+static void
+setup_bif_trace(void)
+{
+ int i;
+
+ for (i = 0; i < BIF_SIZE; ++i) {
+ Export *ep = bif_export[i];
+ GenericBp* g = (GenericBp *) ep->fake_op_func_info_for_hipe[1];
+ if (g) {
+ if (ExportIsBuiltIn(ep)) {
+ ASSERT(ep->code[4]);
+ ep->code[4] = (BeamInstr) bif_table[i].traced;
+ }
+ }
+ }
}
-static void clear_trace_bif(int bif_index) {
- Export *ep = bif_export[bif_index];
-
-#ifdef HARDDEBUG
- erts_fprintf(stderr, "clear_trace_bif: %T:%T/%bpu\n",
- ep->code[0], ep->code[1], ep->code[2]);
-#endif
- ASSERT(ExportIsBuiltIn(ep));
- MatchSetUnref(ep->match_prog_set);
- ep->match_prog_set = NULL;
+static void
+reset_bif_trace(void)
+{
+ int i;
+ ErtsBpIndex active = erts_active_bp_ix();
+
+ for (i = 0; i < BIF_SIZE; ++i) {
+ Export *ep = bif_export[i];
+ BeamInstr* pc = ep->code+3;
+ GenericBp* g = (GenericBp *) pc[-4];
+ if (g && g->data[active].flags == 0) {
+ if (ExportIsBuiltIn(ep)) {
+ ASSERT(ep->code[4]);
+ ep->code[4] = (BeamInstr) bif_table[i].f;
+ }
+ }
+ }
}
/*
@@ -1776,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));
}
}
@@ -2142,13 +2254,15 @@ BIF_RETTYPE system_profile_2(BIF_ALIST_2)
/* Check if valid process, no locks are taken */
if (is_internal_pid(profiler)) {
- if (internal_pid_index(profiler) >= erts_max_processes) goto error;
- profiler_p = process_tab[internal_pid_index(profiler)];
- if (INVALID_PID(profiler_p, profiler)) goto error;
+ profiler_p = erts_proc_lookup(profiler);
+ 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;
}
@@ -2212,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);
}
}
@@ -2232,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_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index fe3693d0ca..88c6c34881 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -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
@@ -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... */
- esdp->run_queue->flags |= ERTS_RUNQ_FLG_CHK_CPU_BIND;
+ (void) ERTS_RUNQ_FLGS_SET(esdp->run_queue, ERTS_RUNQ_FLG_CHK_CPU_BIND);
}
#endif
@@ -498,9 +498,6 @@ erts_sched_check_cpu_bind(ErtsSchedulerData *esdp)
erts_cpu_groups_map_t *cgm;
erts_cpu_groups_callback_list_t *cgcl;
erts_cpu_groups_callback_call_t *cgcc;
-#ifdef ERTS_SMP
- esdp->run_queue->flags &= ~ERTS_RUNQ_FLG_CHK_CPU_BIND;
-#endif
erts_smp_runq_unlock(esdp->run_queue);
erts_smp_rwmtx_rwlock(&cpuinfo_rwmtx);
cpu_id = scheduler2cpu_map[esdp->no].bind_id;
@@ -623,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..b502258dae 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010. 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
@@ -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 7409564167..d0afc8ed8d 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -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
@@ -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,20 +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_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)
{
/*
@@ -274,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,
@@ -441,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;
}
@@ -541,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);
@@ -635,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;
@@ -1214,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
@@ -1457,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);
@@ -1479,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);
}
@@ -1526,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));
@@ -1538,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.");
}
@@ -1657,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
@@ -1674,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);
/*
@@ -1689,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);
@@ -1768,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;
@@ -1791,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);
@@ -1853,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;
}
@@ -2667,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);
@@ -2814,7 +2802,6 @@ void init_db(void)
{
DbTable init_tb;
int i;
- extern BeamInstr* em_apply_bif;
Eterm *hp;
unsigned bits;
size_t size;
@@ -2848,7 +2835,7 @@ void init_db(void)
else
db_max_tabs = user_requested_db_max_tabs;
- bits = erts_fit_in_bits(db_max_tabs-1);
+ bits = erts_fit_in_bits_int32(db_max_tabs-1);
if (bits > SMALL_BITS) {
erl_exit(1,"Max limit for ets tabled too high %u (max %u).",
db_max_tabs, ((Uint)1)<<SMALL_BITS);
@@ -2945,49 +2932,24 @@ void init_db(void)
}
/* Non visual BIF to trap to. */
- memset(&ets_select_delete_continue_exp, 0, sizeof(Export));
- ets_select_delete_continue_exp.address =
- &ets_select_delete_continue_exp.code[3];
- ets_select_delete_continue_exp.code[0] = am_ets;
- ets_select_delete_continue_exp.code[1] = am_atom_put("delete_trap",11);
- ets_select_delete_continue_exp.code[2] = 1;
- ets_select_delete_continue_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_delete_continue_exp.code[4] =
- (BeamInstr) &ets_select_delete_1;
+ erts_init_trap_export(&ets_select_delete_continue_exp,
+ am_ets, am_atom_put("delete_trap",11), 1,
+ &ets_select_delete_1);
/* Non visual BIF to trap to. */
- memset(&ets_select_count_continue_exp, 0, sizeof(Export));
- ets_select_count_continue_exp.address =
- &ets_select_count_continue_exp.code[3];
- ets_select_count_continue_exp.code[0] = am_ets;
- ets_select_count_continue_exp.code[1] = am_atom_put("count_trap",11);
- ets_select_count_continue_exp.code[2] = 1;
- ets_select_count_continue_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_count_continue_exp.code[4] =
- (BeamInstr) &ets_select_count_1;
+ erts_init_trap_export(&ets_select_count_continue_exp,
+ am_ets, am_atom_put("count_trap",11), 1,
+ &ets_select_count_1);
/* Non visual BIF to trap to. */
- memset(&ets_select_continue_exp, 0, sizeof(Export));
- ets_select_continue_exp.address =
- &ets_select_continue_exp.code[3];
- ets_select_continue_exp.code[0] = am_ets;
- ets_select_continue_exp.code[1] = am_atom_put("select_trap",11);
- ets_select_continue_exp.code[2] = 1;
- ets_select_continue_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_continue_exp.code[4] =
- (BeamInstr) &ets_select_trap_1;
+ erts_init_trap_export(&ets_select_continue_exp,
+ am_ets, am_atom_put("select_trap",11), 1,
+ &ets_select_trap_1);
/* Non visual BIF to trap to. */
- memset(&ets_delete_continue_exp, 0, sizeof(Export));
- ets_delete_continue_exp.address = &ets_delete_continue_exp.code[3];
- ets_delete_continue_exp.code[0] = am_ets;
- ets_delete_continue_exp.code[1] = am_atom_put("delete_trap",11);
- ets_delete_continue_exp.code[2] = 1;
- ets_delete_continue_exp.code[3] = (BeamInstr) em_apply_bif;
- ets_delete_continue_exp.code[4] = (BeamInstr) &ets_delete_trap;
+ erts_init_trap_export(&ets_delete_continue_exp,
+ am_ets, am_atom_put("delete_trap",11), 1,
+ &ets_delete_trap);
hp = ms_delete_all_buff;
ms_delete_all = CONS(hp, am_true, NIL);
@@ -3085,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,
@@ -3100,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);
}
@@ -3111,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;
@@ -3120,7 +3082,8 @@ retry:
if (to_proc == NULL) {
return 0; /* heir not alive, table still mine */
}
- if (erts_cmp_timeval(&to_proc->started, &tb->common.heir_started) != 0) {
+ 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 */
}
@@ -3145,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;
@@ -3154,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) \
@@ -3164,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;
@@ -3346,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:
@@ -3367,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));
}
@@ -3399,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);
@@ -3411,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;
@@ -3422,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.");
@@ -3442,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);
@@ -3456,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));
@@ -3515,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) {
- tb->common.heir_started = me->started;
+ 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_pid2proc_opt(me, ERTS_PROC_LOCK_MAIN, heir,
- 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ Process* heir_proc= erts_proc_lookup(heir);
if (heir_proc != NULL) {
- tb->common.heir_started = heir_proc->started;
- erts_smp_proc_dec_refc(heir_proc);
+ 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;
}
@@ -3861,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..6b62e10eb7 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -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
@@ -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_hash.h b/erts/emulator/beam/erl_db_hash.h
index cddd8dfadd..d17bd9f693 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -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
@@ -33,7 +33,7 @@ typedef struct hash_db_term {
DbTerm dbterm; /* The actual term */
} HashDbTerm;
-#define DB_HASH_LOCK_CNT 16
+#define DB_HASH_LOCK_CNT 64
typedef struct db_table_hash_fine_locks {
union {
erts_smp_rwmtx_t lck;
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 312050b931..25029ba90f 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -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
@@ -452,16 +452,8 @@ DbTableMethod db_tree =
void db_initialize_tree(void)
{
- memset(&ets_select_reverse_exp, 0, sizeof(Export));
- ets_select_reverse_exp.address =
- &ets_select_reverse_exp.code[3];
- ets_select_reverse_exp.code[0] = am_ets;
- ets_select_reverse_exp.code[1] = am_reverse;
- ets_select_reverse_exp.code[2] = 3;
- ets_select_reverse_exp.code[3] =
- (BeamInstr) em_apply_bif;
- ets_select_reverse_exp.code[4] =
- (BeamInstr) &ets_select_reverse;
+ erts_init_trap_export(&ets_select_reverse_exp, am_ets, am_reverse, 3,
+ &ets_select_reverse);
return;
};
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 42907e2e84..713ac0ba18 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2012. 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
@@ -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));
@@ -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);
@@ -3410,8 +3415,7 @@ static DMCRet dmc_one_term(DMCContext *context,
}
default:
erl_exit(1, "db_match_compile: "
- "Bad object on heap: 0x%08lx\n",
- (unsigned long) c);
+ "Bad object on heap: 0x%bex\n", c);
}
return retOk;
}
@@ -4485,7 +4489,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 +4506,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 +4770,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;
}
@@ -4853,7 +4860,7 @@ static Eterm my_copy_struct(Eterm t, Eterm **hp, ErlOffHeap* off_heap)
ret = copy_struct(b,sz,hp,off_heap);
} else {
erl_exit(1, "Trying to constant-copy non constant expression "
- "0x%08x in (d)ets:match compilation.", (unsigned long) t);
+ "0x%bex in (d)ets:match compilation.", t);
}
} else {
sz = size_object(t);
@@ -5002,7 +5009,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;
@@ -5387,7 +5394,7 @@ void db_match_dis(Binary *bp)
erts_printf("Caller\n");
break;
default:
- erts_printf("??? (0x%08x)\n", *t);
+ erts_printf("??? (0x%bpx)\n", *t);
++t;
break;
}
@@ -5399,13 +5406,13 @@ void db_match_dis(Binary *bp)
first = 0;
else
erts_printf(", ");
- erts_printf("0x%08x", (unsigned long) tmp);
+ erts_printf("%p", tmp);
}
erts_printf("}\n");
erts_printf("num_bindings: %d\n", prog->num_bindings);
erts_printf("heap_size: %beu\n", prog->heap_size);
erts_printf("stack_offset: %beu\n", prog->stack_offset);
- erts_printf("text: 0x%08x\n", (unsigned long) prog->text);
+ erts_printf("text: %p\n", prog->text);
erts_printf("stack_size: %d (words)\n", prog->heap_size-prog->stack_offset);
}
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 6a96e174e1..90b79e6044 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -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
@@ -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 */
@@ -219,7 +219,7 @@ typedef struct db_table_common {
Eterm owner; /* Pid of the creator */
Eterm heir; /* Pid of the heir */
UWord heir_data; /* To send in ETS-TRANSFER (is_immed or (DbTerm*) */
- SysTimeval heir_started; /* To further identify the heir */
+ Uint64 heir_started_interval; /* To further identify the heir */
Eterm the_name; /* an atom */
Eterm id; /* atom | integer */
DbTableMethod* meth; /* table methods */
@@ -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 22e873afc6..b90d00f236 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -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_driver.h b/erts/emulator/beam/erl_driver.h
index 1ae9a211d7..e280563de1 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -1,7 +1,7 @@
/*
* %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
@@ -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);
@@ -405,6 +408,11 @@ EXTERN int driver_cancel_timer(ErlDrvPort port);
EXTERN int driver_read_timer(ErlDrvPort port, unsigned long *time_left);
/*
+ * Inform runtime system about lengthy work.
+ */
+EXTERN int erl_drv_consume_timeslice(ErlDrvPort port, int percent);
+
+/*
* Get plain-text error message from within a driver
*/
EXTERN char* erl_errno_id(int error);
@@ -601,11 +609,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 5c66a0bf73..b673ef6b3c 100644
--- a/erts/emulator/beam/erl_fun.h
+++ b/erts/emulator/beam/erl_fun.h
@@ -77,8 +77,6 @@ ErlFunEntry* erts_get_fun_entry(Eterm mod, int uniq, int index);
ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
byte* uniq, int index, int arity);
-ErlFunEntry* erts_get_fun_entry2(Eterm mod, int old_uniq, int old_index,
- byte* uniq, int index, int arity);
void erts_erase_fun_entry(ErlFunEntry* fe);
void erts_cleanup_funs(ErlFunThing* funp);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index e2689f58c3..298909c921 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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
@@ -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;
@@ -216,7 +231,7 @@ erts_next_heap_size(Uint size, Uint offset)
low = mid + 1;
}
}
- erl_exit(1, "no next heap size found: %lu, offset %lu\n", (unsigned long)size, (unsigned long)offset);
+ erl_exit(1, "no next heap size found: %beu, offset %beu\n", size, offset);
}
return 0;
}
@@ -370,11 +385,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
trace_gc(p, am_gc_start);
}
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->gcstatus = p->status;
- p->status = P_GARBING;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
if (erts_system_monitor_long_gc != 0) {
get_now(&ms1, &s1, &us1);
}
@@ -418,9 +429,8 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
ErtsGcQuickSanityCheck(p);
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->status = p->gcstatus;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
+
if (IS_TRACED_FL(p, F_TRACE_GC)) {
trace_gc(p, am_gc_end);
}
@@ -504,10 +514,7 @@ erts_garbage_collect_hibernate(Process* p)
/*
* Preliminaries.
*/
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->gcstatus = p->status;
- p->status = P_GARBING;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
ErtsGcQuickSanityCheck(p);
ASSERT(p->mbuf_sz == 0);
ASSERT(p->mbuf == 0);
@@ -618,9 +625,7 @@ erts_garbage_collect_hibernate(Process* p)
ErtsGcQuickSanityCheck(p);
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->status = p->gcstatus;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
}
@@ -644,10 +649,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
/*
* Set GC state.
*/
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->gcstatus = p->status;
- p->status = P_GARBING;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
/*
* We assume that the caller has already done a major collection
@@ -789,9 +791,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
/*
* Restore status.
*/
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- p->status = p->gcstatus;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
}
static int
@@ -883,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. */
}
@@ -1449,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);
}
@@ -1961,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)) {
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index e7d4ac2b67..c2088929e9 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -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 1eb3dba240..e7e4030900 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. 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
@@ -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,68 @@
# 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!
+ */
+#ifdef ERTS_SMP
+const int etp_smp_compiled = 1;
+#else
+const int etp_smp_compiled = 0;
+#endif
+#ifdef USE_THREADS
+const int etp_thread_compiled = 1;
+#else
+const int etp_thread_compiled = 0;
+#endif
+const char etp_erts_version[] = ERLANG_VERSION;
+const char etp_otp_release[] = ERLANG_OTP_RELEASE;
+const char etp_compile_date[] = ERLANG_COMPILE_DATE;
+const char etp_arch[] = ERLANG_ARCHITECTURE;
+#ifdef ERTS_ENABLE_KERNEL_POLL
+const int etp_kernel_poll_support = 1;
+#else
+const int etp_kernel_poll_support = 0;
+#endif
+#if defined(ARCH_64)
+const int etp_arch_bits = 64;
+#elif defined(ARCH_32)
+const int etp_arch_bits = 32;
+#else
+# error "Not 64-bit, nor 32-bit arch"
+#endif
+#if HALFWORD_HEAP
+const int etp_halfword = 1;
+#else
+const int etp_halfword = 0;
+#endif
+#ifdef HIPE
+const int etp_hipe = 1;
+#else
+const int etp_hipe = 0;
+#endif
+#ifdef DEBUG
+const int etp_debug_compiled = 1;
+#else
+const int etp_debug_compiled = 0;
+#endif
+#ifdef ERTS_ENABLE_LOCK_COUNT
+const int etp_lock_count = 1;
+#else
+const int etp_lock_count = 0;
+#endif
+#ifdef ERTS_ENABLE_LOCK_CHECK
+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
@@ -66,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;
@@ -151,8 +215,6 @@ ErtsModifiedTimings erts_modified_timings[] = {
Export *erts_delay_trap = NULL;
-int erts_use_r9_pids_ports;
-
int ignore_break;
int replace_intr;
@@ -216,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();
@@ -229,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 */
@@ -241,6 +309,7 @@ erl_init(int ncpu)
erts_init_trace();
erts_init_binary();
erts_init_bits();
+ erts_code_ix_init();
erts_init_fun_table();
init_atom_table();
init_export_table();
@@ -250,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();
@@ -257,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();
@@ -288,8 +358,9 @@ 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));
- if (erts_find_function(start_mod, am_start, 2) == NULL) {
+ 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);
}
@@ -384,11 +455,11 @@ 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);
- res = erts_load_module(NULL, 0, NIL, &module_name, code, length);
+ res = erts_preload_module(NULL, 0, NIL, &module_name, code, length);
sys_preload_end(&preload_p[i]);
if (res != NIL)
erl_exit(1,"Failed loading preloaded module %s (%T)\n",
@@ -400,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");
@@ -424,7 +496,7 @@ void erts_usage(void)
erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n");
erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n");
-
+ erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n");
erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n",
H_DEFAULT_SIZE);
erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n",
@@ -433,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, "-pc <set> Control what characters are considered printable (default latin1)\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");
@@ -455,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",
@@ -556,7 +633,7 @@ early_init(int *argc, char **argv) /*
erts_disable_tolerant_timeofday = 0;
display_items = 200;
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;
@@ -583,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
@@ -627,7 +702,7 @@ early_init(int *argc, char **argv) /*
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;
@@ -839,11 +914,13 @@ 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_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
@@ -858,6 +935,12 @@ erl_start(int argc, char **argv)
(erts_aint32_t) max_gen_gcs);
}
+ envbufsz = sizeof(envbuf);
+ if (erts_sys_getenv_raw("ERL_MAX_PORTS", envbuf, &envbufsz) == 0) {
+ port_tab_sz = atoi(envbuf);
+ port_tab_sz_ignore_files = 1;
+ }
+
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__DARWIN__)
/*
* The default stack size on MacOS X is too small for pcre.
@@ -902,20 +985,83 @@ erl_start(int argc, char **argv)
VERBOSE(DEBUG_SYSTEM,
("using display items %d\n",display_items));
break;
+ case 'p':
+ if (!strncmp(argv[i],"-pc",3)) {
+ int printable_chars = ERL_PRINTABLE_CHARACTERS_LATIN1;
+ arg = get_arg(argv[i]+3, argv[i+1], &i);
+ if (!strcmp(arg,"unicode")) {
+ printable_chars = ERL_PRINTABLE_CHARACTERS_UNICODE;
+ } else if (strcmp(arg,"latin1")) {
+ erts_fprintf(stderr, "bad range of printable "
+ "characters: %s\n", arg);
+ erts_usage();
+ }
+ erts_set_printable_characters(printable_chars);
+ break;
+ } else {
+ erts_fprintf(stderr, "%s unknown flag %s\n", argv[0], argv[i]);
+ erts_usage();
+ }
case 'f':
if (!strncmp(argv[i],"-fn",3)) {
+ int warning_type = ERL_FILENAME_WARNING_WARNING;
arg = get_arg(argv[i]+3, argv[i+1], &i);
switch (*arg) {
case 'u':
- erts_set_user_requested_filename_encoding(ERL_FILENAME_UTF8);
+ switch (*(arg+1)) {
+ case 'w':
+ case 0:
+ break;
+ case 'i':
+ warning_type = ERL_FILENAME_WARNING_IGNORE;
+ break;
+ case 'e':
+ warning_type = ERL_FILENAME_WARNING_ERROR;
+ break;
+ default:
+ erts_fprintf(stderr, "bad type of warnings for "
+ "wrongly coded filename: %s\n", arg+1);
+ erts_usage();
+ }
+ erts_set_user_requested_filename_encoding
+ (
+ ERL_FILENAME_UTF8,
+ warning_type
+ );
break;
case 'l':
- erts_set_user_requested_filename_encoding(ERL_FILENAME_LATIN1);
+ erts_set_user_requested_filename_encoding
+ (
+ ERL_FILENAME_LATIN1,
+ warning_type
+ );
break;
case 'a':
- erts_set_user_requested_filename_encoding(ERL_FILENAME_UNKNOWN);
+ switch (*(arg+1)) {
+ case 'w':
+ case 0:
+ break;
+ case 'i':
+ warning_type = ERL_FILENAME_WARNING_IGNORE;
+ break;
+ case 'e':
+ warning_type = ERL_FILENAME_WARNING_ERROR;
+ break;
+ default:
+ erts_fprintf(stderr, "bad type of warnings for "
+ "wrongly coded filename: %s\n", arg+1);
+ erts_usage();
+ }
+ erts_set_user_requested_filename_encoding
+ (
+ ERL_FILENAME_UNKNOWN,
+ warning_type
+ );
+ break;
default:
- erts_fprintf(stderr, "bad filename encoding %s, can be (l,u or a)\n", arg);
+ erts_fprintf(stderr, "bad filename encoding %s, can be "
+ "(l,u or a, optionally followed by w, "
+ "i or e)\n", arg);
erts_usage();
}
break;
@@ -1094,12 +1240,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_max_processes = 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 */
@@ -1121,7 +1308,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:
@@ -1201,8 +1388,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) {
@@ -1282,22 +1492,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;
}
@@ -1339,8 +1546,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;
@@ -1395,14 +1600,13 @@ erl_start(int argc, char **argv)
i++;
}
- /* Delayed check of +P flag */
- if (erts_max_processes < ERTS_MIN_PROCESSES
- || erts_max_processes > ERTS_MAX_PROCESSES
- || (erts_use_r9_pids_ports
- && erts_max_processes > 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__
@@ -1424,9 +1628,14 @@ 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();
+ erts_commit_staging_code_ix();
erts_initialized = 1;
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
index 963c8b3c58..b5b245288b 100644
--- a/erts/emulator/beam/erl_instrument.c
+++ b/erts/emulator/beam/erl_instrument.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -775,8 +775,8 @@ Eterm erts_instr_get_memory_map(Process *proc)
ASSERT(hp + 3 == end_hp);
if (mem_anchor) {
- for (bp = mem_anchor; bp->next; bp = bp->next);
-
+ for (bp = mem_anchor; bp->next; bp = bp->next)
+ ;
ASSERT(org_mem_anchor);
org_mem_anchor->prev = bp;
bp->next = org_mem_anchor;
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 1f388c1796..69bb4be717 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -82,8 +82,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
#ifdef ERTS_SMP
{ "bif_timers", NULL },
{ "reg_tab", NULL },
- { "migration_info_update", NULL },
{ "proc_main", "pid" },
+ { "old_code", "address" },
#ifdef HIPE
{ "hipe_mfait_lock", NULL },
#endif
@@ -93,8 +93,8 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "proc_msgq", "pid" },
{ "dist_entry", "address" },
{ "dist_entry_links", "address" },
+ { "code_write_permission", NULL },
{ "proc_status", "pid" },
- { "proc_tab", NULL },
{ "ports_snapshot", NULL },
{ "meta_name_tab", "address" },
{ "meta_main_tab_slot", "address" },
@@ -114,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" },
@@ -124,7 +121,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "removed_fd_pre_alloc_lock", "address" },
{ "state_prealloc", NULL },
{ "schdlr_sspnd", NULL },
+ { "migration_info_update", NULL },
{ "run_queue", "address" },
+ { "process_table", NULL },
{ "cpu_info", NULL },
{ "pollset", "address" },
#ifdef __WIN32__
@@ -155,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 },
@@ -245,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;
@@ -362,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;
@@ -669,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);
@@ -677,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 df7b3758e1..068340abe7 100644
--- a/erts/emulator/beam/erl_lock_check.h
+++ b/erts/emulator/beam/erl_lock_check.h
@@ -102,6 +102,7 @@ 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) || ERTS_SOMEONE_IS_CRASH_DUMPING) ? 1 : erts_lc_assert_failed(__FILE__, __LINE__, #A)))
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 919567ab27..325d77e911 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -296,36 +296,6 @@ erts_msg_distext2heap(Process *pp,
return THE_NON_VALUE;
}
-static ERTS_INLINE void
-notify_new_message(Process *receiver)
-{
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS
- & erts_proc_lc_my_proc_locks(receiver));
-
- switch (receiver->status) {
- case P_GARBING:
- switch (receiver->gcstatus) {
- case P_SUSPENDED:
- goto suspended;
- case P_WAITING:
- goto waiting;
- default:
- break;
- }
- break;
- case P_SUSPENDED:
- suspended:
- receiver->rstatus = P_RUNABLE;
- break;
- case P_WAITING:
- waiting:
- erts_add_to_runq(receiver);
- break;
- default:
- break;
- }
-}
-
void
erts_queue_dist_message(Process *rcvr,
ErtsProcLocks *rcvr_locks,
@@ -339,7 +309,7 @@ erts_queue_dist_message(Process *rcvr,
Sint tok_serial = 0;
#endif
#ifdef ERTS_SMP
- ErtsProcLocks need_locks;
+ erts_aint_t state;
#endif
ERTS_SMP_LC_ASSERT(*rcvr_locks == erts_proc_lc_my_proc_locks(rcvr));
@@ -347,20 +317,21 @@ erts_queue_dist_message(Process *rcvr,
mp = message_alloc();
#ifdef ERTS_SMP
- need_locks = ~(*rcvr_locks) & (ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS);
- if (need_locks) {
- *rcvr_locks |= need_locks;
- if (erts_smp_proc_trylock(rcvr, need_locks) == EBUSY) {
- if (need_locks == ERTS_PROC_LOCK_MSGQ) {
+ if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (erts_smp_proc_trylock(rcvr, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
+ ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
+ if (*rcvr_locks & ERTS_PROC_LOCK_STATUS) {
erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_STATUS);
- need_locks = (ERTS_PROC_LOCK_MSGQ
- | ERTS_PROC_LOCK_STATUS);
+ need_locks |= ERTS_PROC_LOCK_STATUS;
}
erts_smp_proc_lock(rcvr, need_locks);
}
}
- if (rcvr->is_exiting || ERTS_PROC_PENDING_EXIT(rcvr)) {
+ state = erts_smp_atomic32_read_acqb(&rcvr->state);
+ if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+ if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
/* Drop message if receiver is exiting or has a pending exit ... */
if (is_not_nil(token)) {
ErlHeapFragment *heap_frag;
@@ -376,6 +347,8 @@ erts_queue_dist_message(Process *rcvr,
/* Ahh... need to decode it in order to trace it... */
ErlHeapFragment *mbuf;
Eterm msg;
+ if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
message_free(mp);
msg = erts_msg_distext2heap(rcvr, rcvr_locks, &mbuf, &token, dist_ext);
if (is_value(msg))
@@ -437,26 +410,33 @@ erts_queue_dist_message(Process *rcvr,
mp->data.dist_ext = dist_ext;
LINK_MESSAGE(rcvr, mp);
- notify_new_message(rcvr);
+ if (!(*rcvr_locks & ERTS_PROC_LOCK_MSGQ))
+ erts_smp_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ);
+
+ erts_proc_notify_new_message(rcvr);
}
}
/* Add a message last in message queue */
-void
-erts_queue_message(Process* receiver,
- ErtsProcLocks *receiver_locks,
- ErlHeapFragment* bp,
- Eterm message,
- Eterm seq_trace_token
+static Sint
+queue_message(Process *c_p,
+ Process* receiver,
+ ErtsProcLocks *receiver_locks,
+ erts_aint32_t *receiver_state,
+ ErlHeapFragment* bp,
+ Eterm message,
+ Eterm seq_trace_token
#ifdef USE_VM_PROBES
, Eterm dt_utag
#endif
-)
+ )
{
+ Sint res;
ErlMessage* mp;
-#ifdef ERTS_SMP
- ErtsProcLocks need_locks;
-#else
+ int locked_msgq = 0;
+ erts_aint_t state;
+
+#ifndef ERTS_SMP
ASSERT(bp != NULL || receiver->mbuf == NULL);
#endif
@@ -464,31 +444,45 @@ erts_queue_message(Process* receiver,
mp = message_alloc();
+ if (receiver_state)
+ state = *receiver_state;
+ else
+ state = erts_smp_atomic32_read_acqb(&receiver->state);
+
#ifdef ERTS_SMP
- need_locks = ~(*receiver_locks) & (ERTS_PROC_LOCK_MSGQ
- | ERTS_PROC_LOCK_STATUS);
- if (need_locks) {
- *receiver_locks |= need_locks;
- if (erts_smp_proc_trylock(receiver, need_locks) == EBUSY) {
- if (need_locks == ERTS_PROC_LOCK_MSGQ) {
+
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ goto exiting;
+
+ if (!(*receiver_locks & ERTS_PROC_LOCK_MSGQ)) {
+ if (erts_smp_proc_trylock(receiver, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
+ ErtsProcLocks need_locks = ERTS_PROC_LOCK_MSGQ;
+ if (*receiver_locks & ERTS_PROC_LOCK_STATUS) {
erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_STATUS);
- need_locks = (ERTS_PROC_LOCK_MSGQ
- | ERTS_PROC_LOCK_STATUS);
+ need_locks |= ERTS_PROC_LOCK_STATUS;
}
erts_smp_proc_lock(receiver, need_locks);
}
+ locked_msgq = 1;
+ state = erts_smp_atomic32_read_nob(&receiver->state);
+ if (receiver_state)
+ *receiver_state = state;
}
- if (receiver->is_exiting || ERTS_PROC_PENDING_EXIT(receiver)) {
- /* Drop message if receiver is exiting or has a pending
- * exit ...
- */
+#endif
+
+ if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) {
+#ifdef ERTS_SMP
+ exiting:
+#endif
+ /* Drop message if receiver is exiting or has a pending exit... */
+ if (locked_msgq)
+ erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
if (bp)
free_message_buffer(bp);
message_free(mp);
- return;
+ return 0;
}
-#endif
ERL_MESSAGE_TERM(mp) = message;
ERL_MESSAGE_TOKEN(mp) = seq_trace_token;
@@ -498,7 +492,10 @@ erts_queue_message(Process* receiver,
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
@@ -508,15 +505,15 @@ erts_queue_message(Process* receiver,
* 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);
}
- else {
+ else
+#endif
+ {
LINK_MESSAGE(receiver, mp);
}
-#else
- LINK_MESSAGE(receiver, mp);
-#endif
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(message_queued)) {
@@ -536,15 +533,43 @@ erts_queue_message(Process* receiver,
tok_label, tok_lastcnt, tok_serial);
}
#endif
- notify_new_message(receiver);
- if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
+ if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE))
trace_receive(receiver, message);
- }
+
+ if (locked_msgq)
+ erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ);
+
+ erts_proc_notify_new_message(receiver);
#ifndef ERTS_SMP
ERTS_HOLE_CHECK(receiver);
#endif
+ return res;
+}
+
+void
+erts_queue_message(Process* receiver,
+ ErtsProcLocks *receiver_locks,
+ ErlHeapFragment* bp,
+ Eterm message,
+ Eterm seq_trace_token
+#ifdef USE_VM_PROBES
+ , Eterm dt_utag
+#endif
+ )
+{
+ queue_message(NULL,
+ receiver,
+ receiver_locks,
+ NULL,
+ bp,
+ message,
+ seq_trace_token
+#ifdef USE_VM_PROBES
+ , dt_utag
+#endif
+ );
}
void
@@ -576,9 +601,7 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg)
#endif
#ifdef HARD_DEBUG
- ProcBin *dbg_mso_start = off_heap->mso;
- ErlFunThing *dbg_fun_start = off_heap->funs;
- ExternalThing *dbg_external_start = off_heap->externals;
+ struct erl_off_heap_header* dbg_oh_start = off_heap->first;
Eterm dbg_term, dbg_token;
ErlHeapFragment *dbg_bp;
Uint *dbg_hp, *dbg_thp_start;
@@ -752,48 +775,16 @@ copy_done:
int i, j;
ErlHeapFragment* frag;
{
- ProcBin *mso = off_heap->mso;
+ struct erl_off_heap_header* dbg_oh = off_heap->first;
i = j = 0;
- while (mso != dbg_mso_start) {
- mso = mso->next;
+ while (dbg_oh != dbg_oh_start) {
+ dbg_oh = dbg_oh->next;
i++;
}
for (frag=bp; frag; frag=frag->next) {
- mso = frag->off_heap.mso;
- while (mso) {
- mso = mso->next;
- j++;
- }
- }
- ASSERT(i == j);
- }
- {
- ErlFunThing *fun = off_heap->funs;
- i = j = 0;
- while (fun != dbg_fun_start) {
- fun = fun->next;
- i++;
- }
- for (frag=bp; frag; frag=frag->next) {
- fun = frag->off_heap.funs;
- while (fun) {
- fun = fun->next;
- j++;
- }
- }
- ASSERT(i == j);
- }
- {
- ExternalThing *external = off_heap->externals;
- i = j = 0;
- while (external != dbg_external_start) {
- external = external->next;
- i++;
- }
- for (frag=bp; frag; frag=frag->next) {
- external = frag->off_heap.externals;
- while (external) {
- external = external->next;
+ dbg_oh = frag->off_heap.first;
+ while (dbg_oh) {
+ dbg_oh = dbg_oh->next;
j++;
}
}
@@ -878,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,
@@ -888,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);
@@ -902,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(DTRACE_CHARBUF_NAME(sender_name)), "%T", sender->id);
- erts_snprintf(receiver_name, sizeof(DTRACE_CHARBUF_NAME(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)) {
@@ -925,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
}
@@ -956,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
@@ -974,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 ... */
@@ -1026,31 +1020,45 @@ 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;
Eterm *hp;
+ erts_aint32_t state;
+
BM_SWAP_TIMER(send,size);
msize = size_object(message);
BM_SWAP_TIMER(size,send);
- hp = erts_alloc_message_heap(msize,&bp,&ohp,receiver,receiver_locks);
+ hp = erts_alloc_message_heap_state(msize,
+ &bp,
+ &ohp,
+ receiver,
+ receiver_locks,
+ &state);
BM_SWAP_TIMER(send,copy);
message = copy_struct(message, msize, &hp, ohp);
BM_MESSAGE_COPIED(msz);
BM_SWAP_TIMER(copy,send);
DTRACE6(message_send, sender_name, receiver_name,
msize, tok_label, tok_lastcnt, tok_serial);
- erts_queue_message(receiver, receiver_locks, 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);
#else
ErlMessage* mp = message_alloc();
@@ -1080,19 +1088,16 @@ 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 (receiver->status == P_WAITING) {
- erts_add_to_runq(receiver);
- } else if (receiver->status == P_SUSPENDED) {
- receiver->rstatus = P_RUNABLE;
- }
if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
trace_receive(receiver, message);
}
BM_SWAP_TIMER(send,system);
#endif /* #ifndef ERTS_SMP */
- return;
}
+ return res;
}
/*
@@ -1131,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..70e592cc5f 100644
--- a/erts/emulator/beam/erl_monitors.c
+++ b/erts/emulator/beam/erl_monitors.c
@@ -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
@@ -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..6a360a2336 100644
--- a/erts/emulator/beam/erl_monitors.h
+++ b/erts/emulator/beam/erl_monitors.h
@@ -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
@@ -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..e538ba30c2 100644
--- a/erts/emulator/beam/erl_mtrace.c
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -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 4109c20fa7..d4c2b5bdcc 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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
@@ -263,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;
@@ -287,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,10 +315,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
#endif
Eterm receiver = to_pid->pid;
int flush_me = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
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;
}
@@ -334,10 +335,13 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
rp_had_locks = rp_locks;
#endif
- rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
- receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC);
+
+ rp = (scheduler
+ ? erts_proc_lookup(receiver)
+ : 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);
@@ -362,12 +366,12 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
, NIL
#endif
);
- if (rp_locks) {
- ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ |
- ERTS_PROC_LOCK_STATUS)));
- erts_smp_proc_unlock(rp, (ERTS_PROC_LOCK_MSGQ | ERTS_PROC_LOCK_STATUS));
- }
- erts_smp_proc_dec_refc(rp);
+ if (c_p == rp)
+ rp_locks &= ~ERTS_PROC_LOCK_MAIN;
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
if (flush_me) {
cache_env(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, ERTS_ATOM_ENC_LATIN1);
}
ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)
@@ -1392,6 +1406,57 @@ 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;
+}
+
+int enif_consume_timeslice(ErlNifEnv* env, int percent)
+{
+ Sint reds;
+
+ ASSERT(is_proc_bound(env) && percent >= 1 && percent <= 100);
+ if (percent < 1) percent = 1;
+ else if (percent > 100) percent = 100;
+
+ reds = ((CONTEXT_REDS+99) / 100) * percent;
+ ASSERT(reds > 0 && reds <= CONTEXT_REDS);
+ BUMP_REDS(env->proc, reds);
+ return ERTS_BIF_REDS_LEFT(env->proc) == 0;
+}
+
/***************************************************************************
** load_nif/2 **
***************************************************************************/
@@ -1524,6 +1589,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (len < 0) {
BIF_ERROR(BIF_P, BADARG);
}
+
lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1);
if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) {
@@ -1532,6 +1598,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
lib_name[len] = '\0';
+ if (!erts_try_seize_code_write_permission(BIF_P)) {
+ erts_free(ERTS_ALC_T_TMP, lib_name);
+ ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+
/* Block system (is this the right place to do it?) */
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
@@ -1545,11 +1617,11 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ASSERT(caller != NULL);
mod_atom = caller[0];
ASSERT(is_atom(mod_atom));
- mod=erts_get_module(mod_atom);
+ mod=erts_get_module(mod_atom, erts_active_code_ix());
ASSERT(mod != NULL);
- if (!in_area(caller, mod->code, mod->code_length)) {
- ASSERT(in_area(caller, mod->old_code, mod->old_code_length));
+ if (!in_area(caller, mod->curr.code, mod->curr.code_length)) {
+ ASSERT(in_area(caller, mod->old.code, mod->old.code_length));
ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
@@ -1584,7 +1656,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);
}
@@ -1594,8 +1666,8 @@ 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)
- || (code_pp = get_func_pp(mod->code, f_atom, f->arity))==NULL) {
+ if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
+ || (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);
}
@@ -1624,18 +1696,18 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_refc_init(&lib->rt_dtor_cnt, 0);
lib->mod = mod;
env.mod_nif = lib;
- if (mod->nif != NULL) { /* Reload */
+ if (mod->curr.nif != NULL) { /* Reload */
int k;
- lib->priv_data = mod->nif->priv_data;
+ lib->priv_data = mod->curr.nif->priv_data;
- ASSERT(mod->nif->entry != NULL);
+ ASSERT(mod->curr.nif->entry != NULL);
if (entry->reload == NULL) {
ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library.");
goto error;
}
/* Check that no NIF is removed */
- for (k=0; k < mod->nif->entry->num_of_funcs; k++) {
- ErlNifFunc* old_func = &mod->nif->entry->funcs[k];
+ for (k=0; k < mod->curr.nif->entry->num_of_funcs; k++) {
+ ErlNifFunc* old_func = &mod->curr.nif->entry->funcs[k];
for (i=0; i < entry->num_of_funcs; i++) {
if (old_func->arity == entry->funcs[i].arity
&& sys_strcmp(old_func->name, entry->funcs[i].name) == 0) {
@@ -1656,24 +1728,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful.");
}
else {
- mod->nif->entry = NULL; /* to prevent 'unload' callback */
- erts_unload_nif(mod->nif);
+ mod->curr.nif->entry = NULL; /* to prevent 'unload' callback */
+ erts_unload_nif(mod->curr.nif);
reload_warning = 1;
}
}
else {
lib->priv_data = NULL;
- if (mod->old_nif != NULL) { /* Upgrade */
- void* prev_old_data = mod->old_nif->priv_data;
+ if (mod->old.nif != NULL) { /* Upgrade */
+ void* prev_old_data = mod->old.nif->priv_data;
if (entry->upgrade == NULL) {
ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
erts_pre_nif(&env, BIF_P, lib);
- veto = entry->upgrade(&env, &lib->priv_data, &mod->old_nif->priv_data, BIF_ARG_2);
+ veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- mod->old_nif->priv_data = prev_old_data;
+ mod->old.nif->priv_data = prev_old_data;
ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
}
/*else if (mod->old_nif->priv_data != prev_old_data) {
@@ -1693,20 +1765,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
/*
** Everything ok, patch the beam code with op_call_nif
*/
- mod->nif = lib;
+ mod->curr.nif = lib;
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);
- code_ptr = *get_func_pp(mod->code, f_atom, entry->funcs[i].arity);
+ erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom, ERTS_ATOM_ENC_LATIN1);
+ code_ptr = *get_func_pp(mod->curr.code, f_atom, entry->funcs[i].arity);
if (code_ptr[1] == 0) {
code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif);
}
else { /* Function traced, patch the original instruction word */
- BpData** bps = (BpData**) code_ptr[1];
- BpData* bp = (BpData*) bps[erts_bp_sched2ix()];
- bp->orig_instr = (BeamInstr) BeamOp(op_call_nif);
+ GenericBp* g = (GenericBp *) code_ptr[1];
+ ASSERT(code_ptr[5+0] ==
+ (BeamInstr) BeamOp(op_i_generic_breakpoint));
+ g->orig_instr = (BeamInstr) BeamOp(op_call_nif);
}
code_ptr[5+1] = (BeamInstr) entry->funcs[i].fptr;
code_ptr[5+2] = (BeamInstr) lib;
@@ -1726,6 +1799,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_release_code_write_permission();
erts_free(ERTS_ALC_T_TMP, lib_name);
if (reload_warning) {
@@ -1793,7 +1867,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
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 50f99c90c4..8006741a63 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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,11 @@
** 2.0: R14A
** 2.1: R14B02 "vm_variant"
** 2.2: R14B03 enif_is_exception
-** 2.3: R15 enif_make_reverse_list
+** 2.3: R15 enif_make_reverse_list, enif_is_number
+** 2.4: R16 enif_consume_timeslice
*/
#define ERL_NIF_MAJOR_VERSION 2
-#define ERL_NIF_MINOR_VERSION 3
+#define ERL_NIF_MINOR_VERSION 4
#include <stdlib.h>
@@ -187,11 +188,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
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 6396af09d0..2f841645e1 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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
@@ -138,6 +138,9 @@ 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));
+ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent));
/*
** Add new entries here to keep compatibility on Windows!!!
@@ -260,6 +263,9 @@ 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)
+# define enif_consume_timeslice ERL_NIF_API_FUNC_MACRO(enif_consume_timeslice)
/*
** 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 329a2204cc..0f93a3a9f0 100644
--- a/erts/emulator/beam/erl_node_container_utils.h
+++ b/erts/emulator/beam/erl_node_container_utils.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,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,8 +116,20 @@ extern int erts_use_r9_pids_ports;
* Pids *
\* */
-#define internal_pid_index(x) (internal_pid_data((x)) \
- & erts_process_tab_index_mask)
+extern ErtsPTab erts_proc;
+
+#define make_internal_pid(D) erts_ptab_make_id(&erts_proc, \
+ (D), \
+ _TAG_IMMED1_PID)
+
+#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)
@@ -169,34 +169,37 @@ extern int erts_use_r9_pids_ports;
|| 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)
@@ -235,18 +238,18 @@ extern int erts_use_r9_pids_ports;
#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 5574cb0ac4..ebfba065d1 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -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,22 +1295,24 @@ setup_reference_table(void)
UnUseTmpHeapNoproc(3);
+ max = erts_ptab_max(&erts_proc);
/* Insert all processes */
- for (i = 0; i < erts_max_processes; i++)
- if (process_tab[i]) {
+ for (i = 0; i < max; i++) {
+ Process *proc = erts_pix2proc(i);
+ if (proc) {
ErlMessage *msg;
/* Insert Heap */
- insert_offheap(&(process_tab[i]->off_heap),
+ insert_offheap(&(proc->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->common.id);
/* Insert message buffers */
- for(hfp = process_tab[i]->mbuf; hfp; hfp = hfp->next)
+ for(hfp = proc->mbuf; hfp; hfp = hfp->next)
insert_offheap(&(hfp->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->common.id);
/* Insert msg msg buffers */
- for (msg = process_tab[i]->msg.first; msg; msg = msg->next) {
+ for (msg = proc->msg.first; msg; msg = msg->next) {
ErlHeapFragment *heap_frag = NULL;
if (msg->data.attached) {
if (is_value(ERL_MESSAGE_TERM(msg)))
@@ -1320,7 +1320,7 @@ setup_reference_table(void)
else {
if (msg->data.dist_ext->dep)
insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, process_tab[i]->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);
}
@@ -1328,10 +1328,10 @@ setup_reference_table(void)
if (heap_frag)
insert_offheap(&(heap_frag->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->common.id);
}
#ifdef ERTS_SMP
- for (msg = process_tab[i]->msg_inq.first; msg; msg = msg->next) {
+ for (msg = proc->msg_inq.first; msg; msg = msg->next) {
ErlHeapFragment *heap_frag = NULL;
if (msg->data.attached) {
if (is_value(ERL_MESSAGE_TERM(msg)))
@@ -1339,7 +1339,7 @@ setup_reference_table(void)
else {
if (msg->data.dist_ext->dep)
insert_dist_entry(msg->data.dist_ext->dep,
- HEAP_REF, process_tab[i]->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);
}
@@ -1347,42 +1347,55 @@ setup_reference_table(void)
if (heap_frag)
insert_offheap(&(heap_frag->off_heap),
HEAP_REF,
- process_tab[i]->id);
+ proc->common.id);
}
#endif
/* Insert links */
- if(process_tab[i]->nlinks)
- insert_links(process_tab[i]->nlinks, process_tab[i]->id);
- if(process_tab[i]->monitors)
- insert_monitors(process_tab[i]->monitors, process_tab[i]->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(process_tab[i]);
+ DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
if (dep)
- insert_dist_entry(dep, CTRL_REF, process_tab[i]->id, 0);
+ insert_dist_entry(dep, CTRL_REF, proc->common.id, 0);
}
}
+ }
#ifdef ERTS_SMP
erts_foreach_sys_msg_in_q(insert_sys_msg);
#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..377aa72ed5
--- /dev/null
+++ b/erts/emulator/beam/erl_port.h
@@ -0,0 +1,959 @@
+/*
+ * %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/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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"
+
+#ifndef __WIN32__
+#define ERTS_DEFAULT_MAX_PORTS (1 << 16)
+#else
+/*
+ * Do not default to as many max ports on Windows
+ * as there are no os limits to stop system
+ * from running amok. If allowed to go too high
+ * windows rarely recovers from the errors and
+ * other OS processes can be effected.
+ */
+#define ERTS_DEFAULT_MAX_PORTS (1 << 13)
+#endif /* __WIN32__ */
+#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;
+
+/*
+ * It would have been preferred to use NULL as value of
+ * ERTS_INVALID_ERL_DRV_PORT. That would, however, not be
+ * backward compatible. In pre-R16 systems, 0 was a valid
+ * port handle and -1 was used as invalid handle, so we
+ * are stuck with it.
+ */
+#define ERTS_INVALID_ERL_DRV_PORT ((struct _erl_drv_port *) ((SWord) -1))
+#ifdef DEBUG
+/* Make sure we use this api, and do not cast directly */
+#define ERTS_ErlDrvPort2Port(PH) \
+ ((PH) == ERTS_INVALID_ERL_DRV_PORT \
+ ? ERTS_INVALID_ERL_DRV_PORT \
+ : ((Port *) ((PH) - 4711)))
+#define ERTS_Port2ErlDrvPort(PH) \
+ ((PH) == ERTS_INVALID_ERL_DRV_PORT \
+ ? ERTS_INVALID_ERL_DRV_PORT \
+ : ((ErlDrvPort) ((PH) + 4711)))
+#else
+#define ERTS_ErlDrvPort2Port(PH) ((Port *) (PH))
+#define ERTS_Port2ErlDrvPort(PH) ((ErlDrvPort) (PH))
+#endif
+
+#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 */
+ int reds; /* Only used while executing driver callbacks */
+};
+
+#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(ErlDrvPort, int);
+ERTS_GLB_INLINE Port *erts_drvport2port_state(ErlDrvPort, erts_aint32_t *);
+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 *);
+
+#define erts_drvport2port(Prt) erts_drvport2port_state((Prt), NULL)
+
+#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(ErlDrvPort drvport, int lock_pdl)
+{
+ Port *prt = ERTS_ErlDrvPort2Port(drvport);
+ ASSERT(prt != NULL);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return ERTS_INVALID_ERL_DRV_PORT;
+
+ 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 (erts_lc_is_emu_thr()) {
+ 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
+
+ if (erts_atomic32_read_nob(&prt->state)
+ & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
+ if (lock_pdl && prt->port_data_lock)
+ driver_pdl_unlock(prt->port_data_lock);
+ return ERTS_INVALID_ERL_DRV_PORT;
+ }
+ return prt;
+}
+
+ERTS_GLB_INLINE Port *
+erts_drvport2port_state(ErlDrvPort drvport, erts_aint32_t *statep)
+{
+ Port *prt = ERTS_ErlDrvPort2Port(drvport);
+ erts_aint32_t state;
+ ASSERT(prt);
+ ERTS_LC_ASSERT(erts_lc_is_emu_thr());
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return ERTS_INVALID_ERL_DRV_PORT;
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)
+ || ERTS_IS_CRASH_DUMPING);
+ /*
+ * This state check is only needed since a driver callback
+ * might terminate the port, and then call back into the
+ * emulator. Drivers should preferably have been forbidden
+ * to call into the emulator after terminating the port,
+ * but it has been like this for ages. Perhaps forbid this
+ * in some future major release?
+ */
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
+ return ERTS_INVALID_ERL_DRV_PORT;
+ if (statep)
+ *statep = state;
+ return prt;
+}
+
+ERTS_GLB_INLINE Eterm
+erts_drvport2id(ErlDrvPort drvport)
+{
+ Port *prt = erts_drvport2port(drvport);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ 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 3dc7c14faf..ce045ec94e 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -33,42 +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/20)
+#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))
-
-#define ERTS_PORT_NOT_IN_RUNQ(P) \
-do { \
- (P)->sched.prev = NULL; \
- (P)->sched.next = NULL; \
-} while (0)
+#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); \
}
@@ -78,83 +70,773 @@ do { \
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;
+#ifdef DEBUG
+ erts_aint32_t flags;
+#endif
+ pp->sched.taskq.local.busy.first = ptp;
+
+#ifdef DEBUG
+ flags =
+#endif
+ 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);
+ ErtsPortTaskBusyPortQ *bpq;
+ int written = 0, resume_procs = 0;
+ ErlDrvSizeT low, high;
+
+ if (pp == ERTS_INVALID_ERL_DRV_PORT || !(bpq = pp->sched.taskq.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);
}
}
@@ -167,7 +849,6 @@ enqueue_port(ErtsRunQueue *runq, Port *pp)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
pp->sched.next = NULL;
- pp->sched.prev = runq->ports.end;
if (runq->ports.end) {
ASSERT(runq->ports.start);
runq->ports.end->sched.next = pp;
@@ -177,39 +858,10 @@ enqueue_port(ErtsRunQueue *runq, Port *pp)
runq->ports.start = pp;
}
- runq->ports.info.len++;
- if (runq->ports.info.max_len < runq->ports.info.len)
- runq->ports.info.max_len = runq->ports.info.len;
- runq->len++;
- if (runq->max_len < runq->len)
- runq->max_len = runq->len;
runq->ports.end = pp;
ASSERT(runq->ports.start && runq->ports.end);
-}
-static ERTS_INLINE void
-dequeue_port(ErtsRunQueue *runq, Port *pp)
-{
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- if (pp->sched.next)
- pp->sched.next->sched.prev = pp->sched.prev;
- else {
- ASSERT(runq->ports.end == pp);
- runq->ports.end = pp->sched.prev;
- }
- if (pp->sched.prev)
- pp->sched.prev->sched.next = pp->sched.next;
- else {
- ASSERT(runq->ports.start == pp);
- runq->ports.start = pp->sched.next;
- }
-
- ASSERT(runq->ports.info.len > 0);
- runq->ports.info.len--;
- ASSERT(runq->len > 0);
- runq->len--;
- ASSERT(runq->ports.start || !runq->ports.end);
- ASSERT(runq->ports.end || !runq->ports.start);
+ erts_smp_inc_runq_len(runq, &runq->ports.info, ERTS_PORT_PRIO_LEVEL);
}
static ERTS_INLINE Port *
@@ -222,16 +874,11 @@ pop_port(ErtsRunQueue *runq)
}
else {
runq->ports.start = runq->ports.start->sched.next;
- if (runq->ports.start)
- runq->ports.start->sched.prev = NULL;
- else {
+ if (!runq->ports.start) {
ASSERT(runq->ports.end == pp);
runq->ports.end = NULL;
}
- ASSERT(runq->ports.info.len > 0);
- runq->ports.info.len--;
- ASSERT(runq->len > 0);
- runq->len--;
+ erts_smp_dec_runq_len(runq, &runq->ports.info, ERTS_PORT_PRIO_LEVEL);
}
ASSERT(runq->ports.start || !runq->ports.end);
@@ -239,294 +886,443 @@ 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->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);
+ }
+
+ 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;
}
- ptqp->first = ptp;
- ERTS_PT_CHK_IN_TASKQ(ptqp, ptp);
+
+ 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;
-static void
-check_task_queue(ErtsPortTaskQueue *ptqp,
- ErtsPortTask *chk_ptp,
- int inq)
+ erts_port_task_sched_lock(&pp->sched);
+
+ 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
+
+/*
+ * Cut time slice
+ */
+
+int
+erl_drv_consume_timeslice(ErlDrvPort dprt, int percent)
+{
+ Port *pp = erts_drvport2port(dprt);
+ if (pp == ERTS_INVALID_ERL_DRV_PORT)
+ return -1;
+ if (percent < 1)
+ percent = 1;
+ else if (100 < percent)
+ percent = 100;
+ pp->reds += percent*((CONTEXT_REDS+99)/100);
+ if (pp->reds < CONTEXT_REDS)
+ return 0;
+ pp->reds = CONTEXT_REDS;
+ return 1;
+}
/*
* 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;
- int port_is_dequeued = 0;
-
- pp = &erts_port[internal_port_index(id)];
- runq = erts_port_runq(pp);
+#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)
+{
+ 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);
+ 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);
+
+ while (abort_list) {
+#ifdef DEBUG
+ ErtsPortTaskHandle *saved_pthp;
+#endif
+ ErtsPortTaskType type;
+ ErtsPortTaskTypeData td;
+ ErtsPortTaskHandle *pthp;
+ ErtsPortTask *ptp;
+ ErtsPortTaskHandleList *pthlp;
+ erts_aint32_t old_state;
- ASSERT(ptqp == pp->sched.taskq || ptqp == pp->sched.exe_taskq);
+ pthlp = abort_list;
+ abort_list = pthlp->u.next;
- if (ptqp->first || pp->sched.taskq != ptqp)
- ptqp = NULL;
- else {
- pp->sched.taskq = NULL;
- if (!pp->sched.exe_taskq) {
- dequeue_port(runq, pp);
- ERTS_PORT_NOT_IN_RUNQ(pp);
- port_is_dequeued = 1;
+#ifdef ERTS_SMP
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
+ 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;
}
- }
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
+#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
- erts_smp_runq_unlock(runq);
-
- if (erts_system_profile_flags.runnable_ports && port_is_dequeued) {
- profile_runnable_port(pp, am_inactive);
- }
+ 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;
+ }
- port_task_free(ptp);
- if (ptqp)
- port_taskq_free(ptqp);
+ reset_port_task_handle(pthp);
- return 0;
+ 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);
+ }
}
/*
@@ -537,243 +1333,261 @@ 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));
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
+ pp = erts_port_lookup_raw(id);
- if (!pp->sched.taskq && !pp->sched.exe_taskq) {
#ifdef ERTS_SMP
- 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 = xrunq;
- }
- enq_port = !pp->sched.taskq && !pp->sched.exe_taskq;
-#else
- enq_port = 1;
-#endif
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED) {
+ if (pp)
+ erts_port_inc_refc(pp);
+ erts_thr_progress_unmanaged_continue(dhndl);
}
+#endif
- ASSERT(!enq_port || !(runq->flags & ERTS_RUNQ_FLG_SUSPENDED));
+ if (!pp)
+ goto fail;
- if (!pp->sched.taskq)
- pp->sched.taskq = port_taskq_init(port_taskq_alloc(), pp);
+ if (type != ERTS_PORT_TASK_PROC_SIG) {
+ ptp = port_task_alloc();
- ASSERT(ptp);
+ ptp->type = type;
+ ptp->u.alive.flags = 0;
- ptp->type = type;
- ptp->event = event;
- ptp->event_data = event_data;
+ erts_smp_atomic32_init_nob(&ptp->state, ERTS_PT_STATE_SCHEDULED);
- set_handle(ptp, pthp);
+ 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);
- /* Fall through... */
+ 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);
+ 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.prev || runq->ports.start == pp);
+ 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)
{
+ erts_aint32_t flags;
ErtsRunQueue *runq;
- int port_is_dequeued = 0;
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);
- if (pp->sched.exe_taskq) {
- /* I (this thread) am currently executing this port, free it
- when scheduled out... */
- ErtsPortTask *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_SMP_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(pp->sched.exe_taskq, ptp);
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
- erts_smp_runq_unlock(runq);
- }
- else {
- ErtsPortTaskQueue *ptqp = pp->sched.taskq;
- if (ptqp) {
- dequeue_port(runq, pp);
- ERTS_PORT_NOT_IN_RUNQ(pp);
- port_is_dequeued = 1;
- }
- 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);
-#ifdef ERTS_SMP
- erts_smp_atomic_dec_nob(&pp->refc); /* Not alive */
-#endif
- ERTS_SMP_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);
-#ifndef ERTS_SMP
- ASSERT(pp->status & ERTS_PORT_SFLG_PORT_DEBUG);
- erts_port_status_set(pp, ERTS_PORT_SFLG_FREE);
-#endif
- 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);
+ erts_port_task_sched_unlock(&pp->sched);
+ erts_atomic32_read_bset_relb(&pp->state,
+ (ERTS_PORT_SFLG_CONNECTED
+ | ERTS_PORT_SFLG_EXITING
+ | ERTS_PORT_SFLG_CLOSING
+ | ERTS_PORT_SFLG_FREE),
+ ERTS_PORT_SFLG_FREE);
- if (erts_system_profile_flags.runnable_ports && port_is_dequeued) {
- profile_runnable_port(pp, am_inactive);
- }
+ erts_smp_runq_unlock(runq);
- if (ptqp)
- port_taskq_free(ptqp);
- }
+ if (!(flags & (ERTS_PTS_FLG_IN_RUNQ|ERTS_PTS_FLG_EXEC)))
+ begin_port_cleanup(pp, NULL, NULL);
}
-typedef struct {
- ErtsRunQueue *runq;
- int *resp;
-} ErtsPortTaskExeBlockData;
-
/*
- * 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)
{
- int port_was_enqueued = 0;
Port *pp;
- ErtsPortTaskQueue *ptqp;
- ErtsPortTask *ptp;
+ ErtsPortTask *execq;
+ int processing_busy_q;
int res = 0;
- int reds = ERTS_PORT_REDS_EXECUTE;
+ int vreds = 0;
+ int reds = 0;
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;
}
- ERTS_PORT_NOT_IN_RUNQ(pp);
+ erts_smp_runq_unlock(runq);
*curr_port_pp = pp;
-
- ASSERT(pp->sched.taskq);
- 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();
@@ -790,83 +1604,97 @@ 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);
+ pp->reds = ERTS_PORT_REDS_EXECUTE;
+ 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));
-#ifdef ERTS_SMP
- erts_smp_atomic_dec_nob(&pp->refc); /* Not alive */
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&pp->refc) > 0); /* Lock */
-#else
- erts_port_status_bor_set(pp, ERTS_PORT_SFLG_FREE);
-#endif
-
- 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)) {
+ reds = ERTS_PORT_REDS_TIMEOUT;
+ 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);
+ reds = ERTS_PORT_REDS_INPUT;
+ 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);
+ reds = ERTS_PORT_REDS_OUTPUT;
+ 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);
+ reds = ERTS_PORT_REDS_EVENT;
+ 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);
+ reds = erts_dist_command(pp, CONTEXT_REDS - pp->reds);
break;
default:
erl_exit(ERTS_ABORT_EXIT,
@@ -875,33 +1703,36 @@ 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);
- }
+ reds += erts_port_driver_callback_epilogue(pp, &state);
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ aborted_port_task:
+ schedule_port_task_free(ptp);
-#ifdef ERTS_SMP
- if (pp->xports)
- erts_smp_xports_unlock(pp);
- ASSERT(!pp->xports);
-#endif
+ begin_handle_tasks:
+ if (state & ERTS_PORT_SFLG_FREE) {
+ reds += ERTS_PORT_REDS_FREE;
+ begin_port_cleanup(pp, &execq, &processing_busy_q);
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
+ break;
+ }
- port_task_free(ptp);
+ vreds += ERTS_PORT_CALLBACK_VREDS;
+ reds += ERTS_PORT_CALLBACK_VREDS;
- erts_smp_runq_lock(runq);
+ pp->reds += reds;
+ reds = 0;
- ptp = pop_task(ptqp);
+ if (pp->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);
@@ -909,81 +1740,59 @@ 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);
+
+ reds = pp->reds - vreds;
+
+ 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);
}
else {
#ifdef ERTS_SMP
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
}
else {
/* Port emigrated ... */
erts_smp_atomic_set_nob(&pp->run_queue, (erts_aint_t) xrunq);
+ erts_smp_runq_unlock(runq);
+
+ xrunq = erts_port_runq(pp);
+ ASSERT(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);
+
+ erts_smp_runq_lock(runq);
}
#endif
- port_was_enqueued = 1;
}
+ done:
res = (erts_smp_atomic_read_nob(&erts_port_task_outstanding_io_tasks)
!= (erts_aint_t) 0);
- ERTS_PT_CHK_PRES_PORTQ(runq, pp);
-
- port_taskq_free(ptqp);
-
- if (erts_system_profile_flags.runnable_ports && (port_was_enqueued != 1)) {
- profile_runnable_port(pp, am_inactive);
- }
-
- /* 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:
runq->scheduler->reductions += reds;
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
@@ -992,121 +1801,241 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
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];
+ ErtsPortTaskHandleList *free_nshp = NULL;
+ ErtsProcList *plp;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
- for (i = 0; i < sizeof(ptqps)/sizeof(ErtsPortTaskQueue *); i++) {
- if (!ptqps[i])
- continue;
+ /*
+ * 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;
+ }
- ptp = pop_task(ptqps[i]);
- while (ptp) {
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);
+ aborted_port_task:
+ schedule_port_task_free(ptp);
+ }
+ }
+
+ erts_smp_atomic32_read_band_nob(&pp->sched.flags,
+ ~(ERTS_PTS_FLG_HAVE_BUSY_TASKS
+ |ERTS_PTS_FLG_HAVE_TASKS
+ |ERTS_PTS_FLGS_BUSY));
- erts_smp_runq_lock(runq);
- ptp = pop_task(ptqps[i]);
+ erts_port_task_sched_lock(&pp->sched);
+
+ /* Cleanup nosuspend handles... */
+ free_nshp = (pp->sched.taskq.local.busy.nosuspend
+ ? get_free_nosuspend_handles(pp)
+ : NULL);
+ ASSERT(!pp->sched.taskq.local.busy.nosuspend);
+
+ /* Make sure not to leave any processes suspended on the port... */
+ plp = pp->suspended;
+ pp->suspended = NULL;
+
+ erts_port_task_sched_unlock(&pp->sched);
+
+ if (free_nshp)
+ free_nosuspend_handles(free_nshp);
+
+ if (erts_proclist_fetch(&plp, NULL)) {
+#ifdef USE_VM_PROBES
+ 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", pp->common.id);
+ while (plp2 != NULL) {
+ erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid);
+ DTRACE2(process_port_unblocked, pid_str, port_str);
+ }
}
+#endif
+ erts_resume_processes(plp);
}
- ASSERT(!pp->sched.taskq || !pp->sched.taskq->first);
+ /*
+ * 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);
- 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
-ErtsMigrateResult
-erts_port_migrate(Port *prt, int *prt_locked,
- ErtsRunQueue *from_rq, int *from_locked,
- ErtsRunQueue *to_rq, int *to_locked)
+void
+erts_enqueue_port(ErtsRunQueue *rq, Port *pp)
{
- ERTS_SMP_LC_ASSERT(*from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- if (!*from_locked || !*to_locked) {
- if (from_rq < to_rq) {
- if (!*to_locked) {
- if (!*from_locked)
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- else if (erts_smp_runq_trylock(from_rq) == EBUSY) {
- erts_smp_runq_unlock(to_rq);
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- }
- else {
- if (!*from_locked) {
- if (!*to_locked)
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- else if (erts_smp_runq_trylock(to_rq) == EBUSY) {
- erts_smp_runq_unlock(from_rq);
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- }
- *to_locked = *from_locked = 1;
- }
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- /* Refuse to migrate to a suspended run queue */
- if (to_rq->flags & ERTS_RUNQ_FLG_SUSPENDED)
- return ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED;
- if (from_rq != (ErtsRunQueue *) erts_smp_atomic_read_nob(&prt->run_queue))
- return ERTS_MIGRATE_FAILED_RUNQ_CHANGED;
- if (!ERTS_PORT_IS_IN_RUNQ(from_rq, prt))
- return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ;
- dequeue_port(from_rq, prt);
- erts_smp_atomic_set_nob(&prt->run_queue, (erts_aint_t) to_rq);
- enqueue_port(to_rq, prt);
- return ERTS_MIGRATE_SUCCESS;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+ ASSERT(rq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
+ ASSERT(erts_smp_atomic32_read_nob(&pp->sched.flags) & ERTS_PTS_FLG_IN_RUNQ);
+ enqueue_port(rq, pp);
+}
+
+Port *
+erts_dequeue_port(ErtsRunQueue *rq)
+{
+ Port *pp;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+ pp = pop_port(rq);
+ ASSERT(!pp
+ || rq == (ErtsRunQueue *) erts_smp_atomic_read_nob(&pp->run_queue));
+ ASSERT(!pp || (erts_smp_atomic32_read_nob(&pp->sched.flags)
+ & ERTS_PTS_FLG_IN_RUNQ));
+ return pp;
}
#endif
@@ -1120,5 +2049,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 d7104e1143..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;
- Port *prev;
- 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->prev = NULL;
- 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,21 +243,20 @@ 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
-ErtsMigrateResult erts_port_migrate(Port *,
- int *,
- ErtsRunQueue *,
- int *,
- ErtsRunQueue *,
- int *);
+void erts_enqueue_port(ErtsRunQueue *rq, Port *pp);
+Port *erts_dequeue_port(ErtsRunQueue *rq);
#endif
#undef ERTS_INCLUDE_SCHEDULER_INTERNALS
#endif /* ERL_PORT_TASK_H__ */
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 34da9cab84..436147749e 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-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
@@ -57,17 +57,17 @@ do { \
(CNT) += res__; \
} while (0)
-#define PRINT_ULONG(CNT, FN, ARG, C, P, W, I) \
+#define PRINT_UWORD(CNT, FN, ARG, C, P, W, I) \
do { \
- int res__ = erts_printf_ulong((FN), (ARG), (C), (P), (W), (I)); \
+ int res__ = erts_printf_uword((FN), (ARG), (C), (P), (W), (I)); \
if (res__ < 0) \
return res__; \
(CNT) += res__; \
} while (0)
-#define PRINT_SLONG(CNT, FN, ARG, C, P, W, I) \
+#define PRINT_SWORD(CNT, FN, ARG, C, P, W, I) \
do { \
- int res__ = erts_printf_slong((FN), (ARG), (C), (P), (W), (I)); \
+ int res__ = erts_printf_sword((FN), (ARG), (C), (P), (W), (I)); \
if (res__ < 0) \
return res__; \
(CNT) += res__; \
@@ -153,7 +153,7 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)
if ((i < 0) || (i >= atom_table_size()) || (atom_tab(i) == NULL)) {
PRINT_STRING(res, fn, arg, "<bad atom index: ");
- PRINT_SLONG(res, fn, arg, 'd', 0, 1, (signed long) i);
+ PRINT_SWORD(res, fn, arg, 'd', 0, 1, (ErlPfSWord) i);
PRINT_CHAR(res, fn, arg, '>');
return res;
}
@@ -203,7 +203,7 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)
default:
if (IS_CNTRL(c)) {
PRINT_CHAR(res, fn, arg, '\\');
- PRINT_ULONG(res, fn, arg, 'o', 1, 3, (unsigned long) c);
+ PRINT_UWORD(res, fn, arg, 'o', 1, 3, (ErlPfUWord) c);
}
else
PRINT_CHAR(res, fn, arg, (char) c);
@@ -334,7 +334,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
break;
}
case SMALL_DEF:
- PRINT_SLONG(res, fn, arg, 'd', 0, 1, (signed long) signed_val(obj));
+ PRINT_SWORD(res, fn, arg, 'd', 0, 1, (ErlPfSWord) signed_val(obj));
break;
case BIG_DEF: {
int print_res;
@@ -360,36 +360,36 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
case REF_DEF:
case EXTERNAL_REF_DEF:
PRINT_STRING(res, fn, arg, "#Ref<");
- PRINT_ULONG(res, fn, arg, 'u', 0, 1,
- (unsigned long) ref_channel_no(wobj));
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1,
+ (ErlPfUWord) ref_channel_no(wobj));
ref_num = ref_numbers(wobj);
for (i = ref_no_of_numbers(wobj)-1; i >= 0; i--) {
PRINT_CHAR(res, fn, arg, '.');
- PRINT_ULONG(res, fn, arg, 'u', 0, 1, (unsigned long) ref_num[i]);
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) ref_num[i]);
}
PRINT_CHAR(res, fn, arg, '>');
break;
case PID_DEF:
case EXTERNAL_PID_DEF:
PRINT_CHAR(res, fn, arg, '<');
- PRINT_ULONG(res, fn, arg, 'u', 0, 1,
- (unsigned long) pid_channel_no(wobj));
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1,
+ (ErlPfUWord) pid_channel_no(wobj));
PRINT_CHAR(res, fn, arg, '.');
- PRINT_ULONG(res, fn, arg, 'u', 0, 1,
- (unsigned long) pid_number(wobj));
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1,
+ (ErlPfUWord) pid_number(wobj));
PRINT_CHAR(res, fn, arg, '.');
- PRINT_ULONG(res, fn, arg, 'u', 0, 1,
- (unsigned long) pid_serial(wobj));
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1,
+ (ErlPfUWord) pid_serial(wobj));
PRINT_CHAR(res, fn, arg, '>');
break;
case PORT_DEF:
case EXTERNAL_PORT_DEF:
PRINT_STRING(res, fn, arg, "#Port<");
- PRINT_ULONG(res, fn, arg, 'u', 0, 1,
- (unsigned long) port_channel_no(wobj));
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1,
+ (ErlPfUWord) port_channel_no(wobj));
PRINT_CHAR(res, fn, arg, '.');
- PRINT_ULONG(res, fn, arg, 'u', 0, 1,
- (unsigned long) port_number(wobj));
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1,
+ (ErlPfUWord) port_number(wobj));
PRINT_CHAR(res, fn, arg, '>');
break;
case LIST_DEF:
@@ -437,13 +437,16 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
}
break;
case BINARY_DEF:
- {
+ if (header_is_bin_matchstate(*boxed_val(wobj))) {
+ PRINT_STRING(res, fn, arg, "#MatchState");
+ }
+ else {
ProcBin* pb = (ProcBin *) binary_val(wobj);
if (pb->size == 1)
PRINT_STRING(res, fn, arg, "<<1 byte>>");
else {
PRINT_STRING(res, fn, arg, "<<");
- PRINT_ULONG(res, fn, arg, 'u', 0, 1, (unsigned long) pb->size);
+ PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) pb->size);
PRINT_STRING(res, fn, arg, " bytes>>");
}
}
@@ -459,8 +462,8 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
PRINT_CHAR(res, fn, arg, '.');
PRINT_BUF(res, fn, arg, name->name, name->len);
PRINT_CHAR(res, fn, arg, '.');
- PRINT_SLONG(res, fn, arg, 'd', 0, 1,
- (signed long) ep->code[2]);
+ PRINT_SWORD(res, fn, arg, 'd', 0, 1,
+ (ErlPfSWord) ep->code[2]);
PRINT_CHAR(res, fn, arg, '>');
}
break;
@@ -472,11 +475,11 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
PRINT_STRING(res, fn, arg, "#Fun<");
PRINT_BUF(res, fn, arg, ap->name, ap->len);
PRINT_CHAR(res, fn, arg, '.');
- PRINT_SLONG(res, fn, arg, 'd', 0, 1,
- (signed long) funp->fe->old_index);
+ PRINT_SWORD(res, fn, arg, 'd', 0, 1,
+ (ErlPfSWord) funp->fe->old_index);
PRINT_CHAR(res, fn, arg, '.');
- PRINT_SLONG(res, fn, arg, 'd', 0, 1,
- (signed long) funp->fe->old_uniq);
+ PRINT_SWORD(res, fn, arg, 'd', 0, 1,
+ (ErlPfSWord) funp->fe->old_uniq);
PRINT_CHAR(res, fn, arg, '>');
}
break;
@@ -495,10 +498,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,
}
int
-erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision,
- unsigned long* term_base)
+erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision,
+ ErlPfEterm* term_base)
{
- int res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base);
+ int res;
+ ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm));
+
+ res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base);
if (res < 0)
return res;
if (precision <= 0)
diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h
index a48a3de34c..f92c99d713 100644
--- a/erts/emulator/beam/erl_printf_term.h
+++ b/erts/emulator/beam/erl_printf_term.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-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
@@ -21,6 +21,6 @@
#define ERL_PRINTF_TERM_H__
#include "erl_printf_format.h"
-int erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision,
- unsigned long* term_base);
+int erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision,
+ ErlPfEterm* term_base);
#endif
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index c5127bc29d..00247b387a 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -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
@@ -42,6 +42,7 @@
#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)
@@ -97,34 +98,55 @@
#define HIGH_BIT (1 << PRIORITY_HIGH)
#define NORMAL_BIT (1 << PRIORITY_NORMAL)
#define LOW_BIT (1 << PRIORITY_LOW)
+#define PORT_BIT (1 << ERTS_PORT_PRIO_LEVEL)
-#define ERTS_MAYBE_SAVE_TERMINATING_PROCESS(P) \
-do { \
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&proc_tab_mtx)); \
- if (saved_term_procs.end) \
- save_terminating_process((P)); \
-} while (0)
+#define ERTS_EMPTY_RUNQ(RQ) \
+ ((ERTS_RUNQ_FLGS_GET_NOB((RQ)) & ERTS_RUNQ_FLGS_QMASK) == 0 \
+ && (RQ)->misc.start == NULL)
-#define ERTS_EMPTY_RUNQ(RQ) \
- ((RQ)->len == 0 && (RQ)->misc.start == NULL)
+#undef RUNQ_READ_RQ
+#undef RUNQ_SET_RQ
+#define RUNQ_READ_RQ(X) ((ErtsRunQueue *) erts_smp_atomic_read_nob((X)))
+#define RUNQ_SET_RQ(X, RQ) erts_smp_atomic_set_nob((X), (erts_aint_t) (RQ))
+
+#ifdef DEBUG
+# if defined(ARCH_64) && !HALFWORD_HEAP
+# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
+ (RUNQ_SET_RQ((RQP), (0xdeadbeefdead0003LL | ((N) << 4)))
+# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
+do { \
+ ASSERT((RQP) != NULL); \
+ ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
+ ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000LL));\
+} while (0)
+# else
+# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
+ (RUNQ_SET_RQ((RQP), (0xdead0003 | ((N) << 4))))
+# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
+do { \
+ ASSERT((RQP) != NULL); \
+ ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
+ ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
+} while (0)
+# endif
+#else
+# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
+# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
+#endif
#define ERTS_EMPTY_RUNQ_PORTS(RQ) \
- ((RQ)->ports.info.len == 0 && (RQ)->misc.start == NULL)
+ (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[];
-static Sint p_last;
-static Sint p_next;
-static Sint p_serial;
-static Uint p_serial_mask;
-static Uint p_serial_shift;
-
int erts_sched_compact_load;
Uint erts_no_schedulers;
-Uint erts_max_processes = ERTS_DEFAULT_MAX_PROCESSES;
-Uint erts_process_tab_index_mask;
+
+ErtsPTab erts_proc erts_align_attribute(ERTS_CACHE_LINE_SIZE);
int erts_sched_thread_suggested_stack_size = -1;
@@ -191,7 +213,7 @@ static struct {
struct {
int active_runqs;
int reds;
- int max_len;
+ erts_aint32_t max_len;
} prev_rise;
Uint n;
} balance_info;
@@ -211,8 +233,6 @@ erts_sched_stat_t erts_sched_stat;
static erts_tsd_key_t sched_data_key;
#endif
-static erts_smp_mtx_t proc_tab_mtx;
-
static erts_smp_atomic32_t function_calls;
#ifdef ERTS_SMP
@@ -234,7 +254,6 @@ typedef union {
static ErtsAlignedSchedulerSleepInfo *aligned_sched_sleep_info;
-Process** process_tab;
static Uint last_reductions;
static Uint last_exact_reductions;
Uint erts_default_process_flags;
@@ -250,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;
- SysTimeval spawned;
- SysTimeval exited;
- } process;
- struct {
- SysTimeval time;
- } bif_invocation;
- } u;
-};
-
-static struct {
- ErtsTermProcElement *start;
- ErtsTermProcElement *end;
-} saved_term_procs;
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(misc_op_list,
ErtsMiscOpList,
@@ -334,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,
@@ -366,6 +360,7 @@ dbg_chk_aux_work_val(erts_aint32_t value)
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;
@@ -402,6 +397,31 @@ erts_smp_lc_runq_is_locked(ErtsRunQueue *runq)
}
#endif
+
+static ERTS_INLINE Uint64
+ensure_later_proc_interval(Uint64 interval)
+{
+ return erts_smp_ensure_later_interval_nob(erts_ptab_interval(&erts_proc), interval);
+}
+
+Uint64
+erts_get_proc_interval(void)
+{
+ return erts_smp_current_interval_nob(erts_ptab_interval(&erts_proc));
+}
+
+Uint64
+erts_ensure_later_proc_interval(Uint64 interval)
+{
+ return ensure_later_proc_interval(interval);
+}
+
+Uint64
+erts_step_proc_interval(void)
+{
+ return erts_smp_step_interval_nob(erts_ptab_interval(&erts_proc));
+}
+
void
erts_pre_init_process(void)
{
@@ -447,11 +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)
{
- Uint proc_bits = ERTS_PROC_BITS;
#ifdef ERTS_SMP
erts_disable_proc_not_running_opt = 0;
@@ -460,25 +487,17 @@ 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;
- ASSERT(erts_max_processes <= (1 << ERTS_R9_PROC_BITS));
- }
-
- process_tab = (Process**) erts_alloc(ERTS_ALC_T_PROC_TABLE,
- erts_max_processes*sizeof(Process*));
- sys_memzero(process_tab, erts_max_processes * sizeof(Process*));
-
- erts_smp_mtx_init(&proc_tab_mtx, "proc_tab");
- p_last = -1;
- p_next = 0;
- p_serial = 0;
+ 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");
- p_serial_shift = erts_fit_in_bits(erts_max_processes - 1);
- p_serial_mask = ((~(~((Uint) 0) << proc_bits)) >> p_serial_shift);
- erts_process_tab_index_mask = ~(~((Uint) 0) << p_serial_shift);
last_reductions = 0;
last_exact_reductions = 0;
erts_default_process_flags = 0;
@@ -488,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++) {
@@ -736,8 +754,9 @@ static ERTS_INLINE ErtsProcList *
proclist_create(Process *p)
{
ErtsProcList *plp = proclist_alloc();
- plp->pid = p->id;
- plp->started = p->started;
+ 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;
}
@@ -747,13 +766,6 @@ proclist_destroy(ErtsProcList *plp)
proclist_free(plp);
}
-static ERTS_INLINE int
-proclist_same(ErtsProcList *plp, Process *p)
-{
- return (plp->pid == p->id
- && erts_cmp_timeval(&plp->started, &p->started) == 0);
-}
-
ErtsProcList *
erts_proclist_create(Process *p)
{
@@ -766,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)
{
@@ -1229,7 +1235,8 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp,
}
}
-#endif
+#endif /* ERTS_USE_ASYNC_READY_Q */
+
static ERTS_INLINE erts_aint32_t
handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
@@ -1348,6 +1355,77 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i
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
@@ -1460,37 +1538,32 @@ 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);
+ for (i = 0; i < max; i++) {
+ erts_aint32_t state;
+ Port *prt = erts_pix2port(i);
+ if (!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);
- 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) {
@@ -1573,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);
@@ -1763,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);
- rq->flags |= (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;
@@ -1840,8 +1918,8 @@ static ERTS_INLINE void
sched_waiting(Uint no, ErtsRunQueue *rq)
{
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- rq->flags |= (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
@@ -1873,9 +1951,8 @@ ongoing_multi_scheduling_block(void)
static ERTS_INLINE void
empty_runq(ErtsRunQueue *rq)
{
- erts_aint32_t oifls = erts_smp_atomic32_read_band_nob(&rq->info_flags,
- ~ERTS_RUNQ_IFLG_NONEMPTY);
- if (oifls & ERTS_RUNQ_IFLG_NONEMPTY) {
+ Uint32 old_flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_NONEMPTY|ERTS_RUNQ_FLG_PROTECTED);
+ if (old_flags & ERTS_RUNQ_FLG_NONEMPTY) {
#ifdef DEBUG
erts_aint32_t empty = erts_smp_atomic32_read_nob(&no_empty_run_queues);
/*
@@ -1891,9 +1968,8 @@ empty_runq(ErtsRunQueue *rq)
static ERTS_INLINE void
non_empty_runq(ErtsRunQueue *rq)
{
- erts_aint32_t oifls = erts_smp_atomic32_read_bor_nob(&rq->info_flags,
- ERTS_RUNQ_IFLG_NONEMPTY);
- if (!(oifls & ERTS_RUNQ_IFLG_NONEMPTY)) {
+ Uint32 old_flags = ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_NONEMPTY);
+ if (!(old_flags & ERTS_RUNQ_FLG_NONEMPTY)) {
#ifdef DEBUG
erts_aint32_t empty = erts_smp_atomic32_read_nob(&no_empty_run_queues);
/*
@@ -2508,19 +2584,16 @@ try_inc_no_active_runqs(int active)
static ERTS_INLINE int
chk_wake_sched(ErtsRunQueue *crq, int ix, int activate)
{
- erts_aint32_t iflgs;
+ Uint32 flags;
ErtsRunQueue *wrq;
if (crq->ix == ix)
return 0;
wrq = ERTS_RUNQ_IX(ix);
- iflgs = erts_smp_atomic32_read_nob(&wrq->info_flags);
- if (!(iflgs & (ERTS_RUNQ_IFLG_SUSPENDED|ERTS_RUNQ_IFLG_NONEMPTY))) {
+ flags = ERTS_RUNQ_FLGS_GET(wrq);
+ if (!(flags & (ERTS_RUNQ_FLG_SUSPENDED|ERTS_RUNQ_FLG_NONEMPTY))) {
if (activate) {
- if (try_inc_no_active_runqs(ix+1)) {
- erts_smp_xrunq_lock(crq, wrq);
- wrq->flags &= ~ERTS_RUNQ_FLG_INACTIVE;
- erts_smp_xrunq_unlock(crq, wrq);
- }
+ if (try_inc_no_active_runqs(ix+1))
+ (void) ERTS_RUNQ_FLGS_UNSET(wrq, ERTS_RUNQ_FLG_INACTIVE);
}
wake_scheduler(wrq, 0);
return 1;
@@ -2587,9 +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_smp_runq_lock(rq);
- rq->flags |= ERTS_RUNQ_FLG_CHK_CPU_BIND;
- erts_smp_runq_unlock(rq);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
wake_scheduler(rq, 0);
}
#else
@@ -2598,409 +2669,547 @@ erts_sched_notify_check_cpu_bind(void)
}
-#ifdef ERTS_SMP
+static ERTS_INLINE void
+enqueue_process(ErtsRunQueue *runq, int prio, Process *p)
+{
+ ErtsRunPrioQueue *rpq;
-ErtsRunQueue *
-erts_prepare_emigrate(ErtsRunQueue *c_rq, ErtsRunQueueInfo *c_rqi, int prio)
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
+
+ erts_smp_inc_runq_len(runq, &runq->procs.prio_info[prio], prio);
+
+ if (prio == PRIORITY_LOW) {
+ p->schedule_count = RESCHEDULE_LOW;
+ rpq = &runq->procs.prio[PRIORITY_NORMAL];
+ }
+ else {
+ p->schedule_count = 1;
+ rpq = &runq->procs.prio[prio];
+ }
+
+ p->next = NULL;
+ if (rpq->last)
+ rpq->last->next = p;
+ else
+ rpq->first = p;
+ rpq->last = p;
+}
+
+
+static ERTS_INLINE void
+unqueue_process(ErtsRunQueue *runq,
+ ErtsRunPrioQueue *rpq,
+ ErtsRunQueueInfo *rqi,
+ int prio,
+ Process *prev_proc,
+ Process *proc)
{
- ASSERT(ERTS_CHK_RUNQ_FLG_EMIGRATE(c_rq->flags, prio));
- ASSERT(ERTS_CHK_RUNQ_FLG_EVACUATE(c_rq->flags, prio)
- || c_rqi->len >= c_rqi->migrate.limit.this);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- while (1) {
- ErtsRunQueue *n_rq = c_rqi->migrate.runq;
- ERTS_DBG_VERIFY_VALID_RUNQP(n_rq);
- erts_smp_xrunq_lock(c_rq, n_rq);
-
- /*
- * erts_smp_xrunq_lock() may release lock on c_rq! We have
- * to check that we still want to emigrate and emigrate
- * to the same run queue as before.
- */
+ if (prev_proc)
+ prev_proc->next = proc->next;
+ else
+ rpq->first = proc->next;
+ if (!proc->next)
+ rpq->last = prev_proc;
- if (ERTS_CHK_RUNQ_FLG_EMIGRATE(c_rq->flags, prio)) {
- Uint32 force = (ERTS_CHK_RUNQ_FLG_EVACUATE(c_rq->flags, prio)
- | (c_rq->flags & ERTS_RUNQ_FLG_INACTIVE));
- if (force || c_rqi->len > c_rqi->migrate.limit.this) {
- ErtsRunQueueInfo *n_rqi;
- /* We still want to emigrate */
-
- if (n_rq != c_rqi->migrate.runq) {
- /* Ahh... run queue changed; need to do it all over again... */
- erts_smp_runq_unlock(n_rq);
- continue;
- }
- else {
+ if (!rpq->first)
+ rpq->last = NULL;
- if (prio == ERTS_PORT_PRIO_LEVEL)
- n_rqi = &n_rq->ports.info;
- else
- n_rqi = &n_rq->procs.prio_info[prio];
+ erts_smp_dec_runq_len(runq, rqi, prio);
+}
- if (force || (n_rqi->len < c_rqi->migrate.limit.other)) {
- /* emigrate ... */
- return n_rq;
- }
- }
- }
- }
- ASSERT(n_rq != c_rq);
- erts_smp_runq_unlock(n_rq);
- if (!(c_rq->flags & ERTS_RUNQ_FLG_INACTIVE)) {
- /* No more emigrations to this runq */
- ERTS_UNSET_RUNQ_FLG_EMIGRATE(c_rq->flags, prio);
- ERTS_DBG_SET_INVALID_RUNQP(c_rqi->migrate.runq, 0x3);
- }
+static ERTS_INLINE Process *
+dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep)
+{
+ erts_aint32_t state;
+ int prio;
+ ErtsRunPrioQueue *rpq;
+ ErtsRunQueueInfo *rqi;
+ Process *p;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
+ ASSERT(PRIORITY_NORMAL == prio_q
+ || PRIORITY_HIGH == prio_q
+ || PRIORITY_MAX == prio_q);
+
+ rpq = &runq->procs.prio[prio_q];
+ p = rpq->first;
+ if (!p)
return NULL;
+
+ ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
+
+ state = erts_smp_atomic32_read_nob(&p->state);
+ if (statep)
+ *statep = state;
+
+ prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+
+ rqi = &runq->procs.prio_info[prio];
+
+ if (p)
+ unqueue_process(runq, rpq, rqi, prio, NULL, p);
+
+ return p;
+}
+
+static ERTS_INLINE int
+check_requeue_process(ErtsRunQueue *rq, int prio_q)
+{
+ ErtsRunPrioQueue *rpq = &rq->procs.prio[prio_q];
+ Process *p = rpq->first;
+ if (--p->schedule_count > 0 && p != rpq->last) {
+ /* reschedule */
+ rpq->first = p->next;
+ rpq->last->next = p;
+ rpq->last = p;
+ p->next = NULL;
+ return 1;
}
+ return 0;
+}
+
+#ifdef ERTS_SMP
+
+static ErtsRunQueue *
+check_immigration_need(ErtsRunQueue *c_rq, ErtsMigrationPath *mp, int prio)
+{
+ int len;
+ Uint32 f_flags, f_rq_flags;
+ ErtsRunQueue *f_rq;
+
+ f_flags = mp->prio[prio].flags;
+
+ ASSERT(ERTS_CHK_RUNQ_FLG_IMMIGRATE(mp->flags, prio));
+
+ f_rq = mp->prio[prio].runq;
+ if (!f_rq)
+ return NULL;
+
+ f_rq_flags = ERTS_RUNQ_FLGS_GET(f_rq);
+ if (f_rq_flags & ERTS_RUNQ_FLG_PROTECTED)
+ return NULL;
+
+ if (ERTS_CHK_RUNQ_FLG_EVACUATE(f_flags, prio))
+ return f_rq;
+
+ if (f_rq_flags & ERTS_RUNQ_FLG_INACTIVE)
+ return f_rq;
+
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&c_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&c_rq->procs.prio_info[prio].len);
+
+ if (len < mp->prio[prio].limit.this) {
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&f_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&f_rq->procs.prio_info[prio].len);
+
+ if (len > mp->prio[prio].limit.other)
+ return f_rq;
+ }
+ return NULL;
}
static void
-immigrate(ErtsRunQueue *rq)
+immigrate(ErtsRunQueue *c_rq, ErtsMigrationPath *mp)
{
- int prio;
+ Uint32 iflags, iflag;
+ erts_smp_runq_unlock(c_rq);
- ASSERT(rq->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK);
+ ASSERT(erts_thr_progress_is_managed_thread());
- for (prio = 0; prio < ERTS_NO_PRIO_LEVELS; prio++) {
- if (ERTS_CHK_RUNQ_FLG_IMMIGRATE(rq->flags, prio)) {
- ErtsRunQueueInfo *rqi = (prio == ERTS_PORT_PRIO_LEVEL
- ? &rq->ports.info
- : &rq->procs.prio_info[prio]);
- ErtsRunQueue *from_rq = rqi->migrate.runq;
- int rq_locked, from_rq_locked;
+ iflags = mp->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK;
- ERTS_DBG_VERIFY_VALID_RUNQP(from_rq);
+ iflag = iflags & -iflags;
- rq_locked = 1;
- from_rq_locked = 1;
- erts_smp_xrunq_lock(rq, from_rq);
- /*
- * erts_smp_xrunq_lock() may release lock on rq! We have
- * to check that we still want to immigrate from the same
- * run queue as before.
- */
- if (ERTS_CHK_RUNQ_FLG_IMMIGRATE(rq->flags, prio)
- && from_rq == rqi->migrate.runq) {
- ErtsRunQueueInfo *from_rqi = (prio == ERTS_PORT_PRIO_LEVEL
- ? &from_rq->ports.info
- : &from_rq->procs.prio_info[prio]);
- if ((ERTS_CHK_RUNQ_FLG_EVACUATE(rq->flags, prio)
- && ERTS_CHK_RUNQ_FLG_EVACUATE(from_rq->flags, prio)
- && from_rqi->len)
- || (from_rqi->len > rqi->migrate.limit.other
- && rqi->len < rqi->migrate.limit.this)) {
- if (prio == ERTS_PORT_PRIO_LEVEL) {
- Port *prt = from_rq->ports.start;
- if (prt) {
- int prt_locked = 0;
- (void) erts_port_migrate(prt, &prt_locked,
- from_rq, &from_rq_locked,
- rq, &rq_locked);
- if (prt_locked)
- erts_smp_port_unlock(prt);
- }
- }
- else {
- Process *proc;
- ErtsRunPrioQueue *from_rpq;
- from_rpq = (prio == PRIORITY_LOW
- ? &from_rq->procs.prio[PRIORITY_NORMAL]
- : &from_rq->procs.prio[prio]);
- for (proc = from_rpq->first; proc; proc = proc->next)
- if (proc->prio == prio && !proc->bound_runq)
- break;
- if (proc) {
- ErtsProcLocks proc_locks = 0;
- (void) erts_proc_migrate(proc, &proc_locks,
- from_rq, &from_rq_locked,
- rq, &rq_locked);
- if (proc_locks)
- erts_smp_proc_unlock(proc, proc_locks);
- }
+ while (iflag) {
+ ErtsRunQueue *rq;
+ int prio;
+
+ switch (iflag) {
+ case (MAX_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_MAX;
+ break;
+ case (HIGH_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_HIGH;
+ break;
+ case (NORMAL_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_NORMAL;
+ break;
+ case (LOW_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = PRIORITY_LOW;
+ break;
+ case (PORT_BIT << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT):
+ prio = ERTS_PORT_PRIO_LEVEL;
+ break;
+ default:
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s(): Invalid immigrate queue mask",
+ __FILE__, __LINE__, __func__);
+ prio = 0;
+ break;
+ }
+
+ iflags &= ~iflag;
+ iflag = iflags & -iflags;
+
+ rq = check_immigration_need(c_rq, mp, prio);
+ if (rq) {
+ erts_smp_runq_lock(rq);
+ if (prio == ERTS_PORT_PRIO_LEVEL) {
+ Port *prt;
+ prt = erts_dequeue_port(rq);
+ if (prt)
+ RUNQ_SET_RQ(&prt->run_queue, c_rq);
+ erts_smp_runq_unlock(rq);
+ if (prt) {
+ /* port might terminate while we have no lock... */
+ rq = erts_port_runq(prt);
+ if (rq) {
+ if (rq != c_rq)
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s(): Internal error",
+ __FILE__, __LINE__, __func__);
+ erts_enqueue_port(c_rq, prt);
+ if (!iflag)
+ return; /* done */
+ erts_smp_runq_unlock(c_rq);
}
}
- else {
- ERTS_UNSET_RUNQ_FLG_IMMIGRATE(rq->flags, prio);
- ERTS_DBG_SET_INVALID_RUNQP(rqi->migrate.runq, 0x1);
+ }
+ else {
+ ErtsRunPrioQueue *rpq = &rq->procs.prio[prio == PRIORITY_LOW
+ ? PRIORITY_NORMAL
+ : prio];
+ Process *prev_proc = NULL;
+ Process *proc = rpq->first;
+ int rq_locked = 1;
+
+ while (proc) {
+ erts_aint32_t state;
+ state = erts_smp_atomic32_read_acqb(&proc->state);
+ if (!(ERTS_PSFLG_BOUND & state)
+ && (prio == (int) (ERTS_PSFLG_PRIO_MASK & state))) {
+ ErtsRunQueueInfo *rqi = &rq->procs.prio_info[prio];
+ unqueue_process(rq, rpq, rqi, prio, prev_proc, proc);
+ erts_smp_runq_unlock(rq);
+ RUNQ_SET_RQ(&proc->run_queue, c_rq);
+ rq_locked = 0;
+
+ erts_smp_runq_lock(c_rq);
+ enqueue_process(c_rq, prio, proc);
+ if (!iflag)
+ return; /* done */
+ erts_smp_runq_unlock(c_rq);
+ break;
+ }
+ prev_proc = proc;
+ proc = proc->next;
}
+ if (rq_locked)
+ erts_smp_runq_unlock(rq);
}
- if (from_rq_locked)
- erts_smp_runq_unlock(from_rq);
- if (!rq_locked)
- erts_smp_runq_lock(rq);
}
}
+
+ erts_smp_runq_lock(c_rq);
+}
+
+static ERTS_INLINE void
+suspend_run_queue(ErtsRunQueue *rq)
+{
+ erts_smp_atomic32_read_bor_nob(&rq->scheduler->ssi->flags,
+ ERTS_SSI_FLG_SUSPENDED);
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_SUSPENDED);
+
+ wake_scheduler(rq, 0);
+}
+
+static void scheduler_ix_resume_wake(Uint ix);
+
+static ERTS_INLINE void
+resume_run_queue(ErtsRunQueue *rq)
+{
+ int pix;
+
+ erts_smp_runq_lock(rq);
+
+ (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++) {
+ rq->procs.prio_info[pix].max_len = 0;
+ rq->procs.prio_info[pix].reds = 0;
+ }
+ rq->ports.info.max_len = 0;
+ rq->ports.info.reds = 0;
+ rq->max_len = 0;
+
+ erts_smp_runq_unlock(rq);
+
+ scheduler_ix_resume_wake(rq->ix);
}
+typedef struct {
+ Process *first;
+ Process *last;
+} ErtsStuckBoundProcesses;
+
static void
-evacuate_run_queue(ErtsRunQueue *evac_rq, ErtsRunQueue *rq)
+schedule_bound_processes(ErtsRunQueue *rq,
+ ErtsStuckBoundProcesses *sbpp)
{
- Port *prt;
- int notify_to_rq = 0;
- int prio;
- int prt_locked = 0;
- int rq_locked = 0;
- int evac_rq_locked = 1;
- ErtsMigrateResult mres;
+ Process *proc, *next;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- erts_smp_runq_lock(evac_rq);
+ proc = sbpp->first;
+ while (proc) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ next = proc->next;
+ enqueue_process(rq, (int) (ERTS_PSFLG_PRIO_MASK & state), proc);
+ proc = next;
+ }
+}
- erts_smp_atomic32_read_bor_nob(&evac_rq->scheduler->ssi->flags,
- ERTS_SSI_FLG_SUSPENDED);
+static void
+evacuate_run_queue(ErtsRunQueue *rq,
+ ErtsStuckBoundProcesses *sbpp)
+{
+ int prio_q;
+ ErtsRunQueue *to_rq;
+ ErtsMigrationPaths *mps;
+ ErtsMigrationPath *mp;
- evac_rq->flags &= ~ERTS_RUNQ_FLGS_IMMIGRATE_QMASK;
- evac_rq->flags |= (ERTS_RUNQ_FLGS_EMIGRATE_QMASK
- | ERTS_RUNQ_FLGS_EVACUATE_QMASK
- | ERTS_RUNQ_FLG_SUSPENDED);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- erts_smp_atomic32_read_bor_nob(&evac_rq->info_flags, ERTS_RUNQ_IFLG_SUSPENDED);
- /*
- * Need to set up evacuation paths first since we
- * may release the run queue lock on evac_rq
- * when evacuating.
- */
- evac_rq->misc.evac_runq = rq;
- evac_rq->ports.info.migrate.runq = rq;
- for (prio = 0; prio < ERTS_NO_PROC_PRIO_LEVELS; prio++)
- evac_rq->procs.prio_info[prio].migrate.runq = rq;
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+
+ mps = erts_get_migration_paths_managed();
+ mp = &mps->mpath[rq->ix];
/* Evacuate scheduled misc ops */
- if (evac_rq->misc.start) {
- rq_locked = 1;
- erts_smp_xrunq_lock(evac_rq, rq);
- if (rq->misc.end)
- rq->misc.end->next = evac_rq->misc.start;
+ if (rq->misc.start) {
+ ErtsMiscOpList *start, *end;
+
+ to_rq = mp->misc_evac_runq;
+ if (!to_rq)
+ return;
+
+ start = rq->misc.start;
+ end = rq->misc.end;
+ rq->misc.start = NULL;
+ rq->misc.end = NULL;
+ erts_smp_runq_unlock(rq);
+
+ erts_smp_runq_lock(to_rq);
+ if (to_rq->misc.end)
+ to_rq->misc.end->next = start;
else
- rq->misc.start = evac_rq->misc.start;
- rq->misc.end = evac_rq->misc.end;
- evac_rq->misc.start = NULL;
- evac_rq->misc.end = NULL;
+ to_rq->misc.start = start;
+
+ to_rq->misc.end = end;
+ erts_smp_runq_unlock(to_rq);
+ smp_notify_inc_runq(to_rq);
+ erts_smp_runq_lock(to_rq);
}
- /* Evacuate scheduled ports */
- prt = evac_rq->ports.start;
- while (prt) {
- mres = erts_port_migrate(prt, &prt_locked,
- evac_rq, &evac_rq_locked,
- rq, &rq_locked);
- if (mres == ERTS_MIGRATE_SUCCESS)
- notify_to_rq = 1;
- if (prt_locked)
- erts_smp_port_unlock(prt);
- if (!evac_rq_locked) {
- evac_rq_locked = 1;
- erts_smp_runq_lock(evac_rq);
+ if (rq->ports.start) {
+ Port *prt;
+
+ to_rq = mp->prio[ERTS_PORT_PRIO_LEVEL].runq;
+ if (!to_rq)
+ return;
+
+ /* Evacuate scheduled ports */
+ prt = rq->ports.start;
+ while (prt) {
+ ErtsRunQueue *prt_rq;
+ prt = erts_dequeue_port(rq);
+ RUNQ_SET_RQ(&prt->run_queue, to_rq);
+ erts_smp_runq_unlock(rq);
+ /*
+ * The port might terminate while
+ * we have no lock on it...
+ */
+ prt_rq = erts_port_runq(prt);
+ if (prt_rq) {
+ if (prt_rq != to_rq)
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s() internal error\n",
+ __FILE__, __LINE__, __func__);
+ erts_enqueue_port(to_rq, prt);
+ erts_smp_runq_unlock(to_rq);
+ }
+ erts_smp_runq_lock(rq);
+ prt = rq->ports.start;
}
- prt = evac_rq->ports.start;
+ smp_notify_inc_runq(to_rq);
}
/* Evacuate scheduled processes */
- for (prio = 0; prio < ERTS_NO_PROC_PRIO_LEVELS; prio++) {
+ for (prio_q = 0; prio_q < ERTS_NO_PROC_PRIO_QUEUES; prio_q++) {
+ erts_aint32_t state;
Process *proc;
+ int notify = 0;
+ to_rq = NULL;
- switch (prio) {
- case PRIORITY_MAX:
- case PRIORITY_HIGH:
- case PRIORITY_NORMAL:
- proc = evac_rq->procs.prio[prio].first;
- while (proc) {
- ErtsProcLocks proc_locks = 0;
-
- /* Bound processes are stuck... */
- while (proc->bound_runq) {
- proc = proc->next;
- if (!proc)
- goto end_of_proc;
- }
-
- mres = erts_proc_migrate(proc, &proc_locks,
- evac_rq, &evac_rq_locked,
- rq, &rq_locked);
- if (mres == ERTS_MIGRATE_SUCCESS)
- notify_to_rq = 1;
- if (proc_locks)
- erts_smp_proc_unlock(proc, proc_locks);
- if (!evac_rq_locked) {
- erts_smp_runq_lock(evac_rq);
- evac_rq_locked = 1;
- }
+ if (!mp->prio[prio_q].runq)
+ return;
+ if (prio_q == PRIORITY_NORMAL && !mp->prio[PRIORITY_LOW].runq)
+ return;
- proc = evac_rq->procs.prio[prio].first;
+ proc = dequeue_process(rq, prio_q, &state);
+ while (proc) {
+ if (ERTS_PSFLG_BOUND & state) {
+ /* Bound processes get stuck here... */
+ proc->next = NULL;
+ if (sbpp->last)
+ sbpp->last->next = proc;
+ else
+ sbpp->first = proc;
+ sbpp->last = proc;
}
+ else {
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+ erts_smp_runq_unlock(rq);
- end_of_proc:
+ to_rq = mp->prio[prio].runq;
+ RUNQ_SET_RQ(&proc->run_queue, to_rq);
-#ifdef DEBUG
- for (proc = evac_rq->procs.prio[prio].first;
- proc;
- proc = proc->next) {
- ASSERT(proc->bound_runq);
+ erts_smp_runq_lock(to_rq);
+ enqueue_process(to_rq, prio, proc);
+ erts_smp_runq_unlock(to_rq);
+ notify = 1;
+
+ erts_smp_runq_lock(rq);
}
-#endif
- break;
- case PRIORITY_LOW:
- break;
- default:
- ASSERT(!"Invalid process priority");
- break;
+ proc = dequeue_process(rq, prio_q, &state);
}
+ if (notify)
+ smp_notify_inc_runq(to_rq);
}
- if (rq_locked)
- erts_smp_runq_unlock(rq);
-
- if (evac_rq_locked)
- erts_smp_runq_unlock(evac_rq);
-
- if (notify_to_rq)
- smp_notify_inc_runq(rq);
-
- wake_scheduler(evac_rq, 0);
+ if (ERTS_EMPTY_RUNQ(rq))
+ empty_runq(rq);
}
static int
-try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq)
+try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq, Uint32 flags)
{
- Process *proc;
- int vrq_locked;
+ Uint32 procs_qmask = flags & ERTS_RUNQ_FLGS_PROCS_QMASK;
+ int max_prio_bit;
+ ErtsRunPrioQueue *rpq;
- if (*rq_lockedp)
- erts_smp_xrunq_lock(rq, vrq);
- else
- erts_smp_runq_lock(vrq);
- vrq_locked = 1;
+ if (*rq_lockedp) {
+ erts_smp_runq_unlock(rq);
+ *rq_lockedp = 0;
+ }
+
+ ERTS_SMP_LC_ASSERT(!erts_smp_lc_runq_is_locked(rq));
- ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked);
+ erts_smp_runq_lock(vrq);
if (rq->halt_in_progress)
- goto try_steal_port;
+ goto no_procs;
/*
* Check for a runnable process to steal...
*/
- switch (vrq->flags & ERTS_RUNQ_FLGS_PROCS_QMASK) {
- case MAX_BIT:
- case MAX_BIT|HIGH_BIT:
- case MAX_BIT|NORMAL_BIT:
- case MAX_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT:
- case MAX_BIT|HIGH_BIT|LOW_BIT:
- case MAX_BIT|NORMAL_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT|LOW_BIT:
- for (proc = vrq->procs.prio[PRIORITY_MAX].last;
- proc;
- proc = proc->prev) {
- if (!proc->bound_runq)
- break;
- }
- if (proc)
+ while (procs_qmask) {
+ Process *prev_proc;
+ Process *proc;
+
+ max_prio_bit = procs_qmask & -procs_qmask;
+ switch (max_prio_bit) {
+ case MAX_BIT:
+ rpq = &vrq->procs.prio[PRIORITY_MAX];
break;
- case HIGH_BIT:
- case HIGH_BIT|NORMAL_BIT:
- case HIGH_BIT|LOW_BIT:
- case HIGH_BIT|NORMAL_BIT|LOW_BIT:
- for (proc = vrq->procs.prio[PRIORITY_HIGH].last;
- proc;
- proc = proc->prev) {
- if (!proc->bound_runq)
- break;
- }
- if (proc)
+ case HIGH_BIT:
+ rpq = &vrq->procs.prio[PRIORITY_HIGH];
break;
- case NORMAL_BIT:
- case LOW_BIT:
- case NORMAL_BIT|LOW_BIT:
- for (proc = vrq->procs.prio[PRIORITY_NORMAL].last;
- proc;
- proc = proc->prev) {
- if (!proc->bound_runq)
- break;
- }
- if (proc)
+ case NORMAL_BIT:
+ case LOW_BIT:
+ rpq = &vrq->procs.prio[PRIORITY_NORMAL];
break;
- case 0:
- proc = NULL;
- break;
- default:
- ASSERT(!"Invalid queue mask");
- proc = NULL;
- break;
- }
+ case 0:
+ goto no_procs;
+ default:
+ ASSERT(!"Invalid queue mask");
+ goto no_procs;
+ }
- if (proc) {
- ErtsProcLocks proc_locks = 0;
- int res;
- ErtsMigrateResult mres;
- mres = erts_proc_migrate(proc, &proc_locks,
- vrq, &vrq_locked,
- rq, rq_lockedp);
- if (proc_locks)
- erts_smp_proc_unlock(proc, proc_locks);
- res = !0;
- switch (mres) {
- case ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED:
- res = 0;
- case ERTS_MIGRATE_SUCCESS:
- if (vrq_locked)
+ prev_proc = NULL;
+ proc = rpq->first;
+
+ while (proc) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&proc->state);
+ if (!(ERTS_PSFLG_BOUND & state)) {
+ /* Steal process */
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+ ErtsRunQueueInfo *rqi = &vrq->procs.prio_info[prio];
+ unqueue_process(vrq, rpq, rqi, prio, prev_proc, proc);
erts_smp_runq_unlock(vrq);
- return res;
- default: /* Other failures */
- break;
- }
- }
+ RUNQ_SET_RQ(&proc->run_queue, rq);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked);
+ erts_smp_runq_lock(rq);
+ *rq_lockedp = 1;
+ enqueue_process(rq, prio, proc);
+ return !0;
+ }
+ prev_proc = proc;
+ proc = proc->next;
+ }
- if (!vrq_locked) {
- if (*rq_lockedp)
- erts_smp_xrunq_lock(rq, vrq);
- else
- erts_smp_runq_lock(vrq);
- vrq_locked = 1;
+ procs_qmask &= ~max_prio_bit;
}
- try_steal_port:
+no_procs:
- ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked);
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(vrq));
/*
* Check for a runnable port to steal...
*/
- if (vrq->ports.info.len) {
- Port *prt = vrq->ports.end;
- int prt_locked = 0;
- int res;
- ErtsMigrateResult mres;
-
- mres = erts_port_migrate(prt, &prt_locked,
- vrq, &vrq_locked,
- rq, rq_lockedp);
- if (prt_locked)
- erts_smp_port_unlock(prt);
- res = !0;
- switch (mres) {
- case ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED:
- res = 0;
- case ERTS_MIGRATE_SUCCESS:
- if (vrq_locked)
- erts_smp_runq_unlock(vrq);
- return res;
- default: /* Other failures */
- break;
+ if (vrq->ports.start) {
+ ErtsRunQueue *prt_rq;
+ Port *prt = erts_dequeue_port(vrq);
+ RUNQ_SET_RQ(&prt->run_queue, rq);
+ erts_smp_runq_unlock(vrq);
+
+ /*
+ * The port might terminate while
+ * we have no lock on it...
+ */
+
+ prt_rq = erts_port_runq(prt);
+ if (!prt_rq)
+ return 0;
+ else {
+ if (prt_rq != rq)
+ erl_exit(ERTS_ABORT_EXIT,
+ "%s:%d:%s() internal error\n",
+ __FILE__, __LINE__, __func__);
+ *rq_lockedp = 1;
+ erts_enqueue_port(rq, prt);
+ return !0;
}
}
- if (vrq_locked)
- erts_smp_runq_unlock(vrq);
+ erts_smp_runq_unlock(vrq);
return 0;
}
@@ -3010,9 +3219,10 @@ static ERTS_INLINE int
check_possible_steal_victim(ErtsRunQueue *rq, int *rq_lockedp, int vix)
{
ErtsRunQueue *vrq = ERTS_RUNQ_IX(vix);
- erts_aint32_t iflgs = erts_smp_atomic32_read_nob(&vrq->info_flags);
- if (iflgs & ERTS_RUNQ_IFLG_NONEMPTY)
- return try_steal_task_from_victim(rq, rq_lockedp, vrq);
+ Uint32 flags = ERTS_RUNQ_FLGS_GET(vrq);
+ if ((flags & (ERTS_RUNQ_FLG_NONEMPTY
+ | ERTS_RUNQ_FLG_PROTECTED)) == ERTS_RUNQ_FLG_NONEMPTY)
+ return try_steal_task_from_victim(rq, rq_lockedp, vrq, flags);
else
return 0;
}
@@ -3022,15 +3232,12 @@ static int
try_steal_task(ErtsRunQueue *rq)
{
int res, rq_locked, vix, active_rqs, blnc_rqs;
+ Uint32 flags;
- /*
- * We are not allowed to steal jobs to this run queue
- * if it is suspended. Note that it might get suspended
- * at any time when we don't have the lock on the run
- * queue.
- */
- if (rq->flags & ERTS_RUNQ_FLG_SUSPENDED)
- return 0;
+ /* Protect jobs we steal from getting stolen from us... */
+ flags = ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_PROTECTED);
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED)
+ return 0; /* go suspend instead... */
res = 0;
rq_locked = 1;
@@ -3149,12 +3356,123 @@ do { \
ASSERT(sum__ == (RQ)->full_reds_history_sum); \
} while (0);
+#define ERTS_PRE_ALLOCED_MPATHS 8
+
+erts_atomic_t erts_migration_paths;
+
+static struct {
+ size_t size;
+ ErtsMigrationPaths *freelist;
+ struct {
+ ErtsMigrationPaths *first;
+ ErtsMigrationPaths *last;
+ } retired;
+} mpaths;
+
+static void
+init_migration_paths(void)
+{
+ int qix, i;
+ char *p;
+ ErtsMigrationPaths *mps;
+
+ mpaths.size = sizeof(ErtsMigrationPaths);
+ mpaths.size += sizeof(ErtsMigrationPath)*(erts_no_schedulers-1);
+ mpaths.size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(mpaths.size);
+
+ p = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_LL_MPATHS,
+ (mpaths.size
+ * ERTS_PRE_ALLOCED_MPATHS));
+ mpaths.freelist = NULL;
+ for (i = 0; i < ERTS_PRE_ALLOCED_MPATHS-1; i++) {
+ mps = (ErtsMigrationPaths *) p;
+ mps->next = mpaths.freelist;
+ mpaths.freelist = mps;
+ p += mpaths.size;
+ }
+
+ mps = (ErtsMigrationPaths *) p;
+ mps->block = NULL;
+ for (qix = 0; qix < erts_no_run_queues; qix++) {
+ int pix;
+ mps->mpath[qix].flags = 0;
+ mps->mpath[qix].misc_evac_runq = NULL;
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ mps->mpath[qix].prio[pix].limit.this = -1;
+ mps->mpath[qix].prio[pix].limit.other = -1;
+ mps->mpath[qix].prio[pix].runq = NULL;
+ mps->mpath[qix].prio[pix].flags = 0;
+ }
+ }
+ erts_atomic_init_wb(&erts_migration_paths, (erts_aint_t) mps);
+}
+
+static ERTS_INLINE ErtsMigrationPaths *
+alloc_mpaths(void)
+{
+ void *block;
+ ErtsMigrationPaths *res;
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&balance_info.update_mtx));
+
+ res = mpaths.freelist;
+ if (res) {
+ mpaths.freelist = res->next;
+ res->block = NULL;
+ return res;
+ }
+ res = erts_alloc(ERTS_ALC_T_SL_MPATHS,
+ mpaths.size+ERTS_CACHE_LINE_SIZE);
+ block = (void *) res;
+ if (((UWord) res) & ERTS_CACHE_LINE_MASK)
+ res = (ErtsMigrationPaths *) ((((UWord) res) & ~ERTS_CACHE_LINE_MASK)
+ + ERTS_CACHE_LINE_SIZE);
+ res->block = block;
+ return res;
+}
+
+static ERTS_INLINE void
+retire_mpaths(ErtsMigrationPaths *mps)
+{
+ ErtsThrPrgrVal current;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(&balance_info.update_mtx));
+
+ current = erts_thr_progress_current();
+
+ while (mpaths.retired.first) {
+ ErtsMigrationPaths *tmp = mpaths.retired.first;
+ if (!erts_thr_progress_has_reached_this(current, tmp->thr_prgr))
+ break;
+ mpaths.retired.first = tmp->next;
+ if (tmp->block) {
+ erts_free(ERTS_ALC_T_SL_MPATHS, tmp->block);
+ }
+ else {
+ tmp->next = mpaths.freelist;
+ mpaths.freelist = tmp;
+ }
+ }
+
+ if (!mpaths.retired.first)
+ mpaths.retired.last = NULL;
+
+ mps->thr_prgr = erts_thr_progress_later(NULL);
+ mps->next = NULL;
+
+ if (mpaths.retired.last)
+ mpaths.retired.last->next = mps;
+ else
+ mpaths.retired.first = mps;
+ mpaths.retired.last = mps;
+}
+
static void
check_balance(ErtsRunQueue *c_rq)
{
#if ERTS_MAX_PROCESSES >= (1 << 27)
# error check_balance() assumes ERTS_MAX_PROCESS < (1 << 27)
#endif
+ ErtsMigrationPaths *new_mpaths, *old_mpaths;
ErtsRunQueueBalance avg = {0};
Sint64 scheds_reds, full_scheds_reds;
int forced, active, current_active, oowc, half_full_scheds, full_scheds,
@@ -3180,9 +3498,9 @@ check_balance(ErtsRunQueue *c_rq)
ERTS_FOREACH_RUNQ(rq,
{
if (rq->waiting)
- rq->flags |= ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK;
+ (void) ERTS_RUNQ_FLGS_SET(rq, ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK);
else
- rq->flags &= ~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;
});
@@ -3224,7 +3542,7 @@ check_balance(ErtsRunQueue *c_rq)
ErtsRunQueue *rq = ERTS_RUNQ_IX(qix);
erts_smp_runq_lock(rq);
- run_queue_info[qix].flags = rq->flags;
+ run_queue_info[qix].flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
run_queue_info[qix].prio[pix].max_len
= rq->procs.prio_info[pix].max_len;
@@ -3558,47 +3876,27 @@ erts_fprintf(stderr, "--------------------------------\n");
set_no_active_runqs(active);
balance_info.halftime = 1;
- erts_smp_atomic32_set_nob(&balance_info.checking_balance, 0);
+ new_mpaths = alloc_mpaths();
+
+ /* Write migration paths */
- /* Write migration paths and reset balance statistics in all queues */
for (qix = 0; qix < blnc_no_rqs; qix++) {
int mqix;
- Uint32 flags;
- ErtsRunQueue *rq = ERTS_RUNQ_IX(qix);
- ErtsRunQueueInfo *rqi;
- flags = run_queue_info[qix].flags;
- erts_smp_runq_lock(rq);
- flags |= (rq->flags & ~ERTS_RUNQ_FLGS_MIGRATION_INFO);
- ASSERT(!(flags & ERTS_RUNQ_FLG_OUT_OF_WORK));
- if (rq->waiting)
- flags |= ERTS_RUNQ_FLG_OUT_OF_WORK;
+ Uint32 flags = run_queue_info[qix].flags;
+ ErtsMigrationPath *mp = &new_mpaths->mpath[qix];
- rq->full_reds_history_sum
- = run_queue_info[qix].full_reds_history_sum;
- rq->full_reds_history[freds_hist_ix]
- = run_queue_info[qix].full_reds_history_change;
-
- ERTS_DBG_CHK_FULL_REDS_HISTORY(rq);
+ mp->flags = flags;
+ mp->misc_evac_runq = NULL;
- rq->out_of_work_count = 0;
- rq->flags = flags;
- rq->max_len = rq->len;
for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
- rqi = (pix == ERTS_PORT_PRIO_LEVEL
- ? &rq->ports.info
- : &rq->procs.prio_info[pix]);
- rqi->max_len = rqi->len;
- rqi->reds = 0;
if (!(ERTS_CHK_RUNQ_FLG_EMIGRATE(flags, pix)
| ERTS_CHK_RUNQ_FLG_IMMIGRATE(flags, pix))) {
ASSERT(run_queue_info[qix].prio[pix].immigrate_from < 0);
ASSERT(run_queue_info[qix].prio[pix].emigrate_to < 0);
-#ifdef DEBUG
- rqi->migrate.limit.this = -1;
- rqi->migrate.limit.other = -1;
- ERTS_DBG_SET_INVALID_RUNQP(rqi->migrate.runq, 0x2);
-#endif
-
+ mp->prio[pix].limit.this = -1;
+ mp->prio[pix].limit.other = -1;
+ mp->prio[pix].runq = NULL;
+ mp->prio[pix].flags = 0;
}
else if (ERTS_CHK_RUNQ_FLG_EMIGRATE(flags, pix)) {
ASSERT(!ERTS_CHK_RUNQ_FLG_IMMIGRATE(flags, pix));
@@ -3606,11 +3904,12 @@ erts_fprintf(stderr, "--------------------------------\n");
ASSERT(run_queue_info[qix].prio[pix].emigrate_to >= 0);
mqix = run_queue_info[qix].prio[pix].emigrate_to;
- rqi->migrate.limit.this
+ mp->prio[pix].limit.this
= run_queue_info[qix].prio[pix].migration_limit;
- rqi->migrate.limit.other
+ mp->prio[pix].limit.other
= run_queue_info[mqix].prio[pix].migration_limit;
- rqi->migrate.runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].flags = run_queue_info[mqix].flags;
}
else {
ASSERT(ERTS_CHK_RUNQ_FLG_IMMIGRATE(flags, pix));
@@ -3618,24 +3917,142 @@ erts_fprintf(stderr, "--------------------------------\n");
ASSERT(run_queue_info[qix].prio[pix].immigrate_from >= 0);
mqix = run_queue_info[qix].prio[pix].immigrate_from;
- rqi->migrate.limit.this
+ mp->prio[pix].limit.this
= run_queue_info[qix].prio[pix].migration_limit;
- rqi->migrate.limit.other
+ mp->prio[pix].limit.other
= run_queue_info[mqix].prio[pix].migration_limit;
- rqi->migrate.runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].runq = ERTS_RUNQ_IX(mqix);
+ mp->prio[pix].flags = run_queue_info[mqix].flags;
}
}
+ }
+
+ old_mpaths = erts_get_migration_paths_managed();
+
+ /* Keep offline run-queues as is */
+ for (qix = blnc_no_rqs; qix < erts_no_schedulers; qix++) {
+ ErtsMigrationPath *nmp = &new_mpaths->mpath[qix];
+ ErtsMigrationPath *omp = &old_mpaths->mpath[qix];
+
+ nmp->flags = omp->flags;
+ nmp->misc_evac_runq = omp->misc_evac_runq;
+
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ nmp->prio[pix].limit.this = omp->prio[pix].limit.this;
+ nmp->prio[pix].limit.other = omp->prio[pix].limit.other;
+ nmp->prio[pix].runq = omp->prio[pix].runq;
+ nmp->prio[pix].flags = omp->prio[pix].flags;
+ }
+ }
+
+
+ /* Publish new migration paths... */
+ erts_atomic_set_wb(&erts_migration_paths, (erts_aint_t) new_mpaths);
+
+ /* Reset balance statistics in all online queues */
+ for (qix = 0; qix < blnc_no_rqs; qix++) {
+ Uint32 flags = run_queue_info[qix].flags;
+ ErtsRunQueue *rq = ERTS_RUNQ_IX(qix);
+
+ erts_smp_runq_lock(rq);
+ ASSERT(!(flags & ERTS_RUNQ_FLG_OUT_OF_WORK));
+ if (rq->waiting)
+ flags |= ERTS_RUNQ_FLG_OUT_OF_WORK;
+
+ rq->full_reds_history_sum
+ = run_queue_info[qix].full_reds_history_sum;
+ rq->full_reds_history[freds_hist_ix]
+ = run_queue_info[qix].full_reds_history_change;
+
+ ERTS_DBG_CHK_FULL_REDS_HISTORY(rq);
+
+ rq->out_of_work_count = 0;
+ (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++) {
+ ErtsRunQueueInfo *rqi;
+ rqi = (pix == ERTS_PORT_PRIO_LEVEL
+ ? &rq->ports.info
+ : &rq->procs.prio_info[pix]);
+ erts_smp_reset_max_len(rq, rqi);
+ rqi->reds = 0;
+ }
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
erts_smp_runq_unlock(rq);
}
+ erts_smp_atomic32_set_nob(&balance_info.checking_balance, 0);
+
balance_info.n++;
+ retire_mpaths(old_mpaths);
erts_smp_mtx_unlock(&balance_info.update_mtx);
erts_smp_runq_lock(c_rq);
}
+static void
+change_no_used_runqs(int used)
+{
+ ErtsMigrationPaths *new_mpaths, *old_mpaths;
+ int active, qix;
+ erts_smp_mtx_lock(&balance_info.update_mtx);
+ get_no_runqs(&active, NULL);
+ set_no_used_runqs(used);
+
+ old_mpaths = erts_get_migration_paths_managed();
+ new_mpaths = alloc_mpaths();
+
+ /* Write migration paths... */
+
+ for (qix = 0; qix < used; qix++) {
+ int pix;
+ ErtsMigrationPath *omp = &old_mpaths->mpath[qix];
+ ErtsMigrationPath *nmp = &new_mpaths->mpath[qix];
+
+ nmp->flags = omp->flags & ~ERTS_RUNQ_FLGS_MIGRATION_QMASKS;
+ nmp->misc_evac_runq = NULL;
+
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ nmp->prio[pix].limit.this = -1;
+ nmp->prio[pix].limit.other = -1;
+ nmp->prio[pix].runq = NULL;
+ nmp->prio[pix].flags = 0;
+ }
+ }
+ for (qix = used; qix < erts_no_run_queues; qix++) {
+ int pix;
+ ErtsRunQueue *to_rq = ERTS_RUNQ_IX(qix % used);
+ ErtsMigrationPath *nmp = &new_mpaths->mpath[qix];
+
+ nmp->flags = (ERTS_RUNQ_FLGS_EMIGRATE_QMASK
+ | ERTS_RUNQ_FLGS_EVACUATE_QMASK);
+ nmp->misc_evac_runq = to_rq;
+ for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) {
+ nmp->prio[pix].limit.this = -1;
+ nmp->prio[pix].limit.other = -1;
+ nmp->prio[pix].runq = to_rq;
+ nmp->prio[pix].flags = 0;
+ }
+ }
+
+ /* ... and publish them. */
+ erts_atomic_set_wb(&erts_migration_paths, (erts_aint_t) new_mpaths);
+
+ retire_mpaths(old_mpaths);
+
+ /* Make sure that we balance soon... */
+ balance_info.forced_check_balance = 1;
+
+ erts_smp_mtx_unlock(&balance_info.update_mtx);
+
+ erts_smp_runq_lock(ERTS_RUNQ_IX(0));
+ ERTS_RUNQ_IX(0)->check_balance_reds = 0;
+ erts_smp_runq_unlock(ERTS_RUNQ_IX(0));
+}
+
+
#endif /* #ifdef ERTS_SMP */
Uint
@@ -3663,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)
@@ -3684,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)
@@ -3703,11 +4120,11 @@ static struct {
int limit;
int dec_shift;
int dec_mask;
- void (*check)(ErtsRunQueue *rq);
+ void (*check)(ErtsRunQueue *rq, Uint32 flags);
} wakeup_other;
static void
-wakeup_other_check(ErtsRunQueue *rq)
+wakeup_other_check(ErtsRunQueue *rq, Uint32 flags)
{
int wo_reds = rq->wakeup_other_reds;
if (wo_reds) {
@@ -3725,6 +4142,8 @@ wakeup_other_check(ErtsRunQueue *rq)
if (rq->wakeup_other > wakeup_other.limit) {
int empty_rqs =
erts_smp_atomic32_read_acqb(&no_empty_run_queues);
+ if (flags & 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;
@@ -3769,18 +4188,21 @@ wakeup_other_set_limit(void)
}
static void
-wakeup_other_check_legacy(ErtsRunQueue *rq)
+wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags)
{
int wo_reds = rq->wakeup_other_reds;
if (wo_reds) {
- if (rq->len < 2) {
+ erts_aint32_t len = rq->len;
+ if (len < 2) {
rq->wakeup_other -= ERTS_WAKEUP_OTHER_DEC_LEGACY*wo_reds;
if (rq->wakeup_other < 0)
rq->wakeup_other = 0;
}
else if (rq->wakeup_other < wakeup_other.limit)
- rq->wakeup_other += rq->len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY;
+ rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY;
else {
+ if (flags & 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;
@@ -3817,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;
@@ -3836,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
@@ -3848,6 +4270,7 @@ erts_early_init_scheduling(int no_schedulers)
int
erts_sched_set_wakeup_other_thresold(char *str)
{
+#ifdef ERTS_SMP
ErtsSchedWakeupOtherThreshold threshold;
if (sys_strcmp(str, "very_high") == 0)
threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH;
@@ -3861,29 +4284,38 @@ erts_sched_set_wakeup_other_thresold(char *str)
threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW;
else
return EINVAL;
-#ifdef ERTS_SMP
wakeup_other.threshold = threshold;
set_wakeup_other_data();
-#endif
return 0;
+#else
+ if (sys_strcmp(str, "very_high") == 0 || sys_strcmp(str, "high") == 0 ||
+ sys_strcmp(str, "medium") == 0 || sys_strcmp(str, "low") == 0 ||
+ sys_strcmp(str, "very_low") == 0) {
+ return 0;
+ }
+ return EINVAL;
+#endif
}
int
erts_sched_set_wakeup_other_type(char *str)
{
+#ifdef ERTS_SMP
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
return EINVAL;
-#ifdef ERTS_SMP
wakeup_other.type = type;
-#endif
return 0;
+#else
+ if (sys_strcmp(str, "default") == 0 || sys_strcmp(str, "legacy") == 0) {
+ return 0;
+ }
+ return EINVAL;
+#endif
}
int
@@ -3937,6 +4369,8 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp)
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
@@ -4001,7 +4435,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
rq->ix = ix;
- erts_smp_atomic32_init_nob(&rq->info_flags, ERTS_RUNQ_IFLG_NONEMPTY);
/* make sure that the "extra" id correponds to the schedulers
* id if the esdp->no <-> ix+1 mapping change.
@@ -4012,7 +4445,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
rq->waiting = 0;
rq->woken = 0;
- rq->flags = 0;
+ ERTS_RUNQ_FLGS_INIT(rq, ERTS_RUNQ_FLG_NONEMPTY);
rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS;
rq->full_reds_history_sum = 0;
for (rix = 0; rix < ERTS_FULL_REDS_HISTORY_SIZE; rix++) {
@@ -4026,19 +4459,14 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
rq->wakeup_other_reds = 0;
rq->halt_in_progress = 0;
- rq->procs.len = 0;
rq->procs.pending_exiters = NULL;
rq->procs.context_switches = 0;
rq->procs.reductions = 0;
for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
- rq->procs.prio_info[pix].len = 0;
+ erts_smp_atomic32_init_nob(&rq->procs.prio_info[pix].len, 0);
rq->procs.prio_info[pix].max_len = 0;
rq->procs.prio_info[pix].reds = 0;
- rq->procs.prio_info[pix].migrate.limit.this = 0;
- rq->procs.prio_info[pix].migrate.limit.other = 0;
- ERTS_DBG_SET_INVALID_RUNQP(rq->procs.prio_info[pix].migrate.runq,
- 0x0);
if (pix < ERTS_NO_PROC_PRIO_LEVELS - 1) {
rq->procs.prio[pix].first = NULL;
rq->procs.prio[pix].last = NULL;
@@ -4047,14 +4475,10 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
rq->misc.start = NULL;
rq->misc.end = NULL;
- rq->misc.evac_runq = NULL;
- rq->ports.info.len = 0;
+ erts_smp_atomic32_init_nob(&rq->ports.info.len, 0);
rq->ports.info.max_len = 0;
rq->ports.info.reds = 0;
- rq->ports.info.migrate.limit.this = 0;
- rq->ports.info.migrate.limit.other = 0;
- rq->ports.info.migrate.runq = NULL;
rq->ports.start = NULL;
rq->ports.end = NULL;
}
@@ -4192,10 +4616,12 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online)
balance_info.prev_rise.reds = 0;
balance_info.n = 0;
+ init_migration_paths();
+
if (no_schedulers_online < no_schedulers) {
+ change_no_used_runqs(no_schedulers_online);
for (ix = no_schedulers_online; ix < erts_no_run_queues; ix++)
- evacuate_run_queue(ERTS_RUNQ_IX(ix),
- ERTS_RUNQ_IX(ix % no_schedulers_online));
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
}
schdlr_sspnd.wait_curr_online = no_schedulers_online;
@@ -4237,6 +4663,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 *
@@ -4258,91 +4689,197 @@ erts_get_scheduler_data(void)
#endif
-static int remove_proc_from_runq(ErtsRunQueue *rq, Process *p, int to_inactive);
+/*
+ * scheduler_out_process() return with c_rq locked.
+ */
+static ERTS_INLINE int
+schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p)
+{
+ erts_aint32_t a, e, n;
+ int res = 0;
+
+ a = state;
+
+ while (1) {
+ n = e = a;
+
+ ASSERT(a & ERTS_PSFLG_RUNNING);
+ ASSERT(!(a & ERTS_PSFLG_IN_RUNQ));
+
+ n &= ~ERTS_PSFLG_RUNNING;
+ if ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE)
+ n |= ERTS_PSFLG_IN_RUNQ;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
+
+ if (!(n & ERTS_PSFLG_IN_RUNQ)) {
+ if (erts_system_profile_flags.runnable_procs)
+ profile_runnable_proc(p, am_inactive);
+ }
+ else {
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & n);
+ ErtsRunQueue *runq = erts_get_runq_proc(p);
+
+ ASSERT(!(n & ERTS_PSFLG_SUSPENDED));
+
+#ifdef ERTS_SMP
+ if (!(ERTS_PSFLG_BOUND & n)) {
+ ErtsRunQueue *new_runq = erts_check_emigration_need(runq, prio);
+ if (new_runq) {
+ RUNQ_SET_RQ(&p->run_queue, new_runq);
+ runq = new_runq;
+ }
+ }
+#endif
+ ASSERT(runq);
+ res = 1;
+
+ erts_smp_runq_lock(runq);
+
+ /* Enqueue the process */
+ enqueue_process(runq, prio, p);
+
+ if (runq == c_rq)
+ return res;
+ erts_smp_runq_unlock(runq);
+ smp_notify_inc_runq(runq);
+ }
+ erts_smp_runq_lock(c_rq);
+ return res;
+}
static ERTS_INLINE void
-suspend_process(ErtsRunQueue *rq, Process *p)
+add2runq(Process *p, erts_aint32_t state)
{
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- p->rcount++; /* count number of suspend */
-#ifdef ERTS_SMP
- ASSERT(!(p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)
- || p == erts_get_current_process());
- ASSERT(p->status != P_RUNNING
- || p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING);
- if (p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ)
- goto runable;
-#endif
- switch(p->status) {
- case P_SUSPENDED:
- break;
- case P_RUNABLE:
+ int prio = (int) (ERTS_PSFLG_PRIO_MASK & state);
+ ErtsRunQueue *runq = erts_get_runq_proc(p);
+
#ifdef ERTS_SMP
- runable:
- if (!ERTS_PROC_PENDING_EXIT(p))
+ if (!(ERTS_PSFLG_BOUND & state)) {
+ ErtsRunQueue *new_runq = erts_check_emigration_need(runq, prio);
+ if (new_runq) {
+ RUNQ_SET_RQ(&p->run_queue, new_runq);
+ runq = new_runq;
+ }
+ }
#endif
- remove_proc_from_runq(rq, p, 1);
- /* else:
- * leave process in schedq so it will discover the pending exit
- */
- p->rstatus = P_RUNABLE; /* wakeup as runnable */
- break;
- case P_RUNNING:
- p->rstatus = P_RUNABLE; /* wakeup as runnable */
- break;
- case P_WAITING:
- p->rstatus = P_WAITING; /* wakeup as waiting */
- break;
- case P_EXITING:
- return; /* ignore this */
- case P_GARBING:
- case P_FREE:
- erl_exit(1, "bad state in suspend_process()\n");
+ ASSERT(runq);
+
+ erts_smp_runq_lock(runq);
+
+ /* Enqueue the process */
+ enqueue_process(runq, prio, p);
+
+ erts_smp_runq_unlock(runq);
+ smp_notify_inc_runq(runq);
+
+}
+
+static ERTS_INLINE void
+schedule_process(Process *p, erts_aint32_t state, int active_enq)
+{
+ erts_aint32_t a = state, n;
+
+ while (1) {
+ erts_aint32_t e;
+ n = e = a;
+
+ 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;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ if (!active_enq && (a & ERTS_PSFLG_ACTIVE))
+ return; /* Someone else activated process ... */
}
- if ((erts_system_profile_flags.runnable_procs) && (p->rcount == 1) && (p->status != P_WAITING)) {
- profile_runnable_proc(p, am_inactive);
+ if (erts_system_profile_flags.runnable_procs
+ && !(a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))) {
+ profile_runnable_proc(p, am_active);
}
- p->status = P_SUSPENDED;
-
+ if ((n & ERTS_PSFLG_IN_RUNQ) && !(a & ERTS_PSFLG_IN_RUNQ))
+ add2runq(p, n);
}
-static ERTS_INLINE void
-resume_process(Process *p)
+void
+erts_schedule_process(Process *p, erts_aint32_t state)
{
- Uint32 *statusp;
+ schedule_process(p, state, 0);
+}
+
+static ERTS_INLINE int
+suspend_process(Process *c_p, Process *p)
+{
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ int suspended = 0;
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- switch (p->status) {
- case P_SUSPENDED:
- statusp = &p->status;
- break;
- case P_GARBING:
- if (p->gcstatus == P_SUSPENDED) {
- statusp = &p->gcstatus;
- break;
+
+ if ((state & ERTS_PSFLG_SUSPENDED))
+ suspended = -1;
+ else {
+ if (c_p == p) {
+ state = erts_smp_atomic32_read_bor_relb(&p->state,
+ ERTS_PSFLG_SUSPENDED);
+ state |= ERTS_PSFLG_SUSPENDED;
+ ASSERT(state & ERTS_PSFLG_RUNNING);
+ suspended = 1;
+ }
+ else {
+ while (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_EXITING))) {
+ erts_aint32_t e, n;
+ n = e = state;
+ n |= ERTS_PSFLG_SUSPENDED;
+ state = erts_smp_atomic32_cmpxchg_relb(&p->state, n, e);
+ if (state == e) {
+ state = n;
+ suspended = 1;
+ break;
+ }
+ }
}
- /* Fall through */
- default:
- return;
}
+ if (state & ERTS_PSFLG_SUSPENDED) {
+
+ ASSERT(!(ERTS_PSFLG_RUNNING & state)
+ || p == erts_get_current_process());
+
+ if (erts_system_profile_flags.runnable_procs
+ && (p->rcount == 0)
+ && (state & ERTS_PSFLG_ACTIVE)) {
+ profile_runnable_proc(p, am_inactive);
+ }
+
+ p->rcount++; /* count number of suspend */
+ }
+ return suspended;
+}
+
+static ERTS_INLINE void
+resume_process(Process *p)
+{
+ erts_aint32_t state;
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
+
ASSERT(p->rcount > 0);
if (--p->rcount > 0) /* multiple suspend */
return;
- switch(p->rstatus) {
- case P_RUNABLE:
- erts_add_to_runq(p);
- break;
- case P_WAITING:
- *statusp = P_WAITING;
- break;
- default:
- erl_exit(1, "bad state in resume_process()\n");
+
+ state = erts_smp_atomic32_read_band_mb(&p->state, ~ERTS_PSFLG_SUSPENDED);
+ state &= ~ERTS_PSFLG_SUSPENDED;
+ if ((state & (ERTS_PSFLG_EXITING
+ | ERTS_PSFLG_ACTIVE
+ | ERTS_PSFLG_IN_RUNQ
+ | ERTS_PSFLG_RUNNING)) == ERTS_PSFLG_ACTIVE) {
+ schedule_process(p, state, 1);
}
- p->rstatus = P_FREE;
}
int
@@ -4466,6 +5003,7 @@ suspend_scheduler(ErtsSchedulerData *esdp)
int wake = 0;
erts_aint32_t aux_work;
int thr_prgr_active = 1;
+ ErtsStuckBoundProcesses sbp = {NULL, NULL};
/*
* Schedulers may be suspended in two different ways:
@@ -4480,6 +5018,8 @@ suspend_scheduler(ErtsSchedulerData *esdp)
ASSERT(no != 1);
+ evacuate_run_queue(esdp->run_queue, &sbp);
+
erts_smp_runq_unlock(esdp->run_queue);
erts_sched_check_cpu_bind_prep_suspend(esdp);
@@ -4543,19 +5083,28 @@ suspend_scheduler(ErtsSchedulerData *esdp)
erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
while (1) {
+ erts_aint32_t qmask;
erts_aint32_t flgs;
+ qmask = (ERTS_RUNQ_FLGS_GET(esdp->run_queue)
+ & ERTS_RUNQ_FLGS_QMASK);
aux_work = erts_atomic32_read_acqb(&ssi->aux_work);
- if (aux_work) {
+ if (aux_work|qmask) {
if (!thr_prgr_active) {
erts_thr_progress_active(esdp, thr_prgr_active = 1);
sched_wall_time_change(esdp, 1);
}
- aux_work = handle_aux_work(&esdp->aux_work_data,
- aux_work,
- 1);
+ if (aux_work)
+ aux_work = handle_aux_work(&esdp->aux_work_data,
+ aux_work,
+ 1);
if (aux_work && erts_thr_progress_update(esdp))
erts_thr_progress_leader_update(esdp);
+ if (qmask) {
+ erts_smp_runq_lock(esdp->run_queue);
+ evacuate_run_queue(esdp->run_queue, &sbp);
+ erts_smp_runq_unlock(esdp->run_queue);
+ }
}
if (!aux_work) {
@@ -4625,56 +5174,11 @@ suspend_scheduler(ErtsSchedulerData *esdp)
erts_smp_runq_lock(esdp->run_queue);
non_empty_runq(esdp->run_queue);
+ schedule_bound_processes(esdp->run_queue, &sbp);
+
erts_sched_check_cpu_bind_post_suspend(esdp);
}
-#define ERTS_RUNQ_RESET_SUSPEND_INFO(RQ, DBG_ID) \
-do { \
- int pix__; \
- (RQ)->misc.evac_runq = NULL; \
- (RQ)->ports.info.migrate.runq = NULL; \
- (RQ)->flags &= ~(ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EVACUATE_QMASK \
- | ERTS_RUNQ_FLG_SUSPENDED); \
- (RQ)->flags |= (ERTS_RUNQ_FLG_OUT_OF_WORK \
- | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); \
- (RQ)->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; \
- erts_smp_atomic32_read_band_nob(&(RQ)->info_flags, ~ERTS_RUNQ_IFLG_SUSPENDED);\
- for (pix__ = 0; pix__ < ERTS_NO_PROC_PRIO_LEVELS; pix__++) { \
- (RQ)->procs.prio_info[pix__].max_len = 0; \
- (RQ)->procs.prio_info[pix__].reds = 0; \
- ERTS_DBG_SET_INVALID_RUNQP((RQ)->procs.prio_info[pix__].migrate.runq,\
- (DBG_ID)); \
- } \
- (RQ)->ports.info.max_len = 0; \
- (RQ)->ports.info.reds = 0; \
-} while (0)
-
-#define ERTS_RUNQ_RESET_MIGRATION_PATHS__(RQ) \
-do { \
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked((RQ))); \
- (RQ)->misc.evac_runq = NULL; \
- (RQ)->ports.info.migrate.runq = NULL; \
- (RQ)->flags &= ~(ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
- | ERTS_RUNQ_FLGS_EVACUATE_QMASK); \
-} while (0)
-
-#ifdef DEBUG
-#define ERTS_RUNQ_RESET_MIGRATION_PATHS(RQ, DBG_ID) \
-do { \
- int pix__; \
- ERTS_RUNQ_RESET_MIGRATION_PATHS__((RQ)); \
- for (pix__ = 0; pix__ < ERTS_NO_PROC_PRIO_LEVELS; pix__++) \
- ERTS_DBG_SET_INVALID_RUNQP((RQ)->procs.prio_info[pix__].migrate.runq,\
- (DBG_ID)); \
-} while (0)
-#else
-#define ERTS_RUNQ_RESET_MIGRATION_PATHS(RQ, DBG_ID) \
- ERTS_RUNQ_RESET_MIGRATION_PATHS__((RQ))
-#endif
-
ErtsSchedSuspendResult
erts_schedulers_state(Uint *total,
Uint *online,
@@ -4744,27 +5248,13 @@ erts_set_schedulers_online(Process *p,
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- erts_smp_mtx_lock(&balance_info.update_mtx);
- for (ix = online; ix < no; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ERTS_RUNQ_RESET_SUSPEND_INFO(rq, 0x5);
- erts_smp_runq_unlock(rq);
- scheduler_ix_resume_wake(ix);
- }
- /*
- * Spread evacuation paths among all online
- * run queues.
- */
- for (ix = no; ix < erts_no_run_queues; ix++) {
- ErtsRunQueue *from_rq = ERTS_RUNQ_IX(ix);
- ErtsRunQueue *to_rq = ERTS_RUNQ_IX(ix % no);
- evacuate_run_queue(from_rq, to_rq);
- }
- set_no_used_runqs(no);
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ change_no_used_runqs(no);
+
+ for (ix = online; ix < no; ix++)
+ resume_run_queue(ERTS_RUNQ_IX(ix));
+
+ for (ix = no; ix < erts_no_run_queues; ix++)
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
}
res = ERTS_SCHDLR_SSPND_DONE;
}
@@ -4791,25 +5281,11 @@ erts_set_schedulers_online(Process *p,
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- erts_smp_mtx_lock(&balance_info.update_mtx);
-
- for (ix = 0; ix < online; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ERTS_RUNQ_RESET_MIGRATION_PATHS(rq, 0x6);
- erts_smp_runq_unlock(rq);
- }
- /*
- * Evacutation order important! Newly suspended run queues
- * has to be evacuated last.
- */
- for (ix = erts_no_run_queues-1; ix >= no; ix--)
- evacuate_run_queue(ERTS_RUNQ_IX(ix),
- ERTS_RUNQ_IX(ix % no));
- set_no_used_runqs(no);
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+
+ change_no_used_runqs(no);
+ for (ix = no; ix < erts_no_run_queues; ix++)
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
+
for (ix = no; ix < online; ix++) {
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
wake_scheduler(rq, 0);
@@ -4869,8 +5345,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);
@@ -4906,25 +5381,14 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
schdlr_sspnd.msb.wait_active = 2;
}
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
- erts_smp_mtx_lock(&balance_info.update_mtx);
- set_no_used_runqs(1);
- for (ix = 0; ix < online; ix++) {
+ change_no_used_runqs(1);
+ for (ix = 1; ix < erts_no_run_queues; ix++)
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
+
+ for (ix = 1; ix < online; ix++) {
ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ASSERT(!(rq->flags & ERTS_RUNQ_FLG_SUSPENDED));
- ERTS_RUNQ_RESET_MIGRATION_PATHS(rq, 0x7);
- erts_smp_runq_unlock(rq);
+ wake_scheduler(rq, 0);
}
- /*
- * Evacuate all activities in all other run queues
- * into the first run queue. Note order is important,
- * online run queues has to be evacuated last.
- */
- for (ix = erts_no_run_queues-1; ix >= 1; ix--)
- evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(0));
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
if (erts_smp_atomic32_read_nob(&schdlr_sspnd.active)
!= schdlr_sspnd.msb.wait_active) {
@@ -4966,17 +5430,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;
-#ifdef DEBUG
- ERTS_FOREACH_RUNQ(srq,
- {
- if (srq != ERTS_RUNQ_IX(0)) {
- ASSERT(ERTS_EMPTY_RUNQ(srq));
- ASSERT(srq->flags & ERTS_RUNQ_FLG_SUSPENDED);
- }
- });
-#endif
+ erts_proclist_store_last(&schdlr_sspnd.msb.procs, plp);
ASSERT(p->scheduler_data);
}
}
@@ -4987,20 +5441,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;
}
}
}
@@ -5008,27 +5458,6 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED;
else {
ERTS_SCHDLR_SSPND_CHNG_SET(ERTS_SCHDLR_SSPND_CHNG_MSB, 0);
-#ifdef DEBUG
- ERTS_FOREACH_RUNQ(rq,
- {
- if (rq != p->scheduler_data->run_queue) {
- if (!ERTS_EMPTY_RUNQ(rq)) {
- Process *rp;
- int pix;
- ASSERT(rq->ports.info.len == 0);
- for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
- for (rp = rq->procs.prio[pix].first;
- rp;
- rp = rp->next) {
- ASSERT(rp->bound_runq);
- }
- }
- }
-
- ASSERT(rq->flags & ERTS_RUNQ_FLG_SUSPENDED);
- }
- });
-#endif
p->flags &= ~F_HAVE_BLCKD_MSCHED;
schdlr_sspnd.msb.ongoing = 0;
if (schdlr_sspnd.online == 1) {
@@ -5038,35 +5467,19 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all)
}
else {
int online = schdlr_sspnd.online;
- erts_smp_mtx_unlock(&schdlr_sspnd.mtx);
if (plocks) {
have_unlocked_plocks = 1;
erts_smp_proc_unlock(p, plocks);
}
- erts_smp_mtx_lock(&balance_info.update_mtx);
+
+ change_no_used_runqs(online);
/* Resume all online run queues */
- for (ix = 1; ix < online; ix++) {
- ErtsRunQueue *rq = ERTS_RUNQ_IX(ix);
- erts_smp_runq_lock(rq);
- ERTS_RUNQ_RESET_SUSPEND_INFO(rq, 0x4);
- erts_smp_runq_unlock(rq);
- scheduler_ix_resume_wake(ix);
- }
+ for (ix = 1; ix < online; ix++)
+ resume_run_queue(ERTS_RUNQ_IX(ix));
- /* Spread evacuation paths among all online run queues */
for (ix = online; ix < erts_no_run_queues; ix++)
- evacuate_run_queue(ERTS_RUNQ_IX(ix),
- ERTS_RUNQ_IX(ix % online));
-
- set_no_used_runqs(online);
- /* Make sure that we balance soon... */
- balance_info.forced_check_balance = 1;
- erts_smp_runq_lock(ERTS_RUNQ_IX(0));
- ERTS_RUNQ_IX(0)->check_balance_reds = 0;
- erts_smp_runq_unlock(ERTS_RUNQ_IX(0));
- erts_smp_mtx_unlock(&balance_info.update_mtx);
- erts_smp_mtx_lock(&schdlr_sspnd.mtx);
+ suspend_run_queue(ERTS_RUNQ_IX(ix));
}
res = ERTS_SCHDLR_SSPND_DONE;
}
@@ -5106,23 +5519,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;
@@ -5373,11 +5788,8 @@ handle_pend_sync_suspend(Process *suspendee,
if (suspender) {
ASSERT(is_nil(suspender->suspendee));
if (suspendee_alive) {
- ErtsRunQueue *rq = erts_get_runq_proc(suspendee);
- erts_smp_runq_lock(rq);
- suspend_process(rq, suspendee);
- erts_smp_runq_unlock(rq);
- suspender->suspendee = suspendee->id;
+ erts_suspend(suspendee, suspendee_locks, NULL);
+ suspender->suspendee = suspendee->common.id;
}
/* suspender is suspended waiting for suspendee to suspend;
resume suspender */
@@ -5398,7 +5810,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)
@@ -5419,10 +5831,9 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
resume_process(rp);
}
else {
- ErtsRunQueue *cp_rq, *rp_rq;
rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS,
- pid, ERTS_PROC_LOCK_STATUS);
+ pid, pid_locks|ERTS_PROC_LOCK_STATUS);
if (!rp) {
c_p->flags &= ~F_P2PNR_RESCHED;
@@ -5431,58 +5842,36 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks,
ASSERT(!(c_p->flags & F_P2PNR_RESCHED));
- cp_rq = erts_get_runq_proc(c_p);
- rp_rq = erts_get_runq_proc(rp);
- erts_smp_runqs_lock(cp_rq, rp_rq);
- if (rp->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) {
- running:
- /* Phiu... */
-
- /*
- * If we got pending suspenders and suspend ourselves waiting
- * to suspend another process we might deadlock.
- * In this case we have to yield, be suspended by
- * someone else and then do it all over again.
- */
- if (!c_p->pending_suspenders) {
- /* Mark rp pending for suspend by c_p */
- add_pend_suspend(rp, c_p->id, handle_pend_sync_suspend);
- ASSERT(is_nil(c_p->suspendee));
-
- /* Suspend c_p; when rp is suspended c_p will be resumed. */
- suspend_process(cp_rq, c_p);
- c_p->flags |= F_P2PNR_RESCHED;
- }
- /* Yield (caller is assumed to yield immediately in bif). */
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- rp = ERTS_PROC_LOCK_BUSY;
+ if (suspend) {
+ if (suspend_process(c_p, rp))
+ goto done;
}
else {
- ErtsProcLocks need_locks = pid_locks & ~ERTS_PROC_LOCK_STATUS;
- if (need_locks && erts_smp_proc_trylock(rp, need_locks) == EBUSY) {
- erts_smp_runqs_unlock(cp_rq, rp_rq);
- erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
- rp = erts_pid2proc(c_p, c_p_locks|ERTS_PROC_LOCK_STATUS,
- pid, pid_locks|ERTS_PROC_LOCK_STATUS);
- if (!rp)
- goto done;
- /* run-queues may have changed */
- cp_rq = erts_get_runq_proc(c_p);
- rp_rq = erts_get_runq_proc(rp);
- erts_smp_runqs_lock(cp_rq, rp_rq);
- if (rp->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) {
- /* Ahh... */
- erts_smp_proc_unlock(rp,
- pid_locks & ~ERTS_PROC_LOCK_STATUS);
- goto running;
- }
- }
+ if (!(ERTS_PSFLG_RUNNING & erts_smp_atomic32_read_acqb(&rp->state)))
+ goto done;
+
+ }
+
+ /* Other process running */
- /* rp is not running and we got the locks we want... */
- if (suspend)
- suspend_process(rp_rq, rp);
+ /*
+ * If we got pending suspenders and suspend ourselves waiting
+ * to suspend another process we might deadlock.
+ * In this case we have to yield, be suspended by
+ * someone else and then do it all over again.
+ */
+ if (!c_p->pending_suspenders) {
+ /* Mark rp pending for suspend by c_p */
+ 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. */
+ suspend_process(c_p, c_p);
+ c_p->flags |= F_P2PNR_RESCHED;
}
- erts_smp_runqs_unlock(cp_rq, rp_rq);
+ /* Yield (caller is assumed to yield immediately in bif). */
+ erts_smp_proc_unlock(rp, pid_locks|ERTS_PROC_LOCK_STATUS);
+ rp = ERTS_PROC_LOCK_BUSY;
}
done:
@@ -5539,36 +5928,26 @@ erts_pid2proc_nropt(Process *c_p, ErtsProcLocks c_p_locks,
return erts_pid2proc_not_running(c_p, c_p_locks, pid, pid_locks);
}
-static ERTS_INLINE void
-do_bif_suspend_process(ErtsSuspendMonitor *smon,
- Process *suspendee,
- ErtsRunQueue *locked_runq)
+static ERTS_INLINE int
+do_bif_suspend_process(Process *c_p,
+ ErtsSuspendMonitor *smon,
+ Process *suspendee)
{
ASSERT(suspendee);
- ASSERT(!suspendee->is_exiting);
+ ASSERT(!ERTS_PROC_IS_EXITING(suspendee));
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS
& erts_proc_lc_my_proc_locks(suspendee));
if (smon) {
if (!smon->active) {
- ErtsRunQueue *rq;
-
- if (locked_runq)
- rq = locked_runq;
- else {
- rq = erts_get_runq_proc(suspendee);
- erts_smp_runq_lock(rq);
- }
-
- suspend_process(rq, suspendee);
-
- if (!locked_runq)
- erts_smp_runq_unlock(rq);
+ if (!suspend_process(c_p, suspendee))
+ return 0;
}
smon->active += smon->pending;
ASSERT(smon->active);
smon->pending = 0;
+ return 1;
}
-
+ return 0;
}
static void
@@ -5589,13 +5968,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);
- do_bif_suspend_process(smon, suspendee, NULL);
- suspender->suspendee = suspendee->id;
+ suspendee->common.id);
+#ifdef DEBUG
+ res =
+#endif
+ do_bif_suspend_process(suspendee, smon, suspendee);
+ ASSERT(!smon || res != 0);
+ suspender->suspendee = suspendee->common.id;
}
/* suspender is suspended waiting for suspendee to suspend;
resume suspender */
@@ -5624,12 +6010,19 @@ 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);
- do_bif_suspend_process(smon, suspendee, NULL);
+ suspendee->common.id);
+#ifdef DEBUG
+ res =
+#endif
+ do_bif_suspend_process(suspendee, smon, suspendee);
+ ASSERT(!smon || res != 0);
}
erts_smp_proc_unlock(suspender, ERTS_PROC_LOCK_LINK);
}
@@ -5669,7 +6062,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)) {
@@ -5714,7 +6107,8 @@ suspend_process_2(BIF_ALIST_2)
/* This is really a piece of cake without SMP support... */
if (!smon->active) {
- suspend_process(ERTS_RUNQ_IX(0), suspendee);
+ erts_smp_atomic32_read_bor_nob(&suspendee->state, ERTS_PSFLG_SUSPENDED);
+ suspend_process(BIF_P, suspendee);
smon->active++;
res = am_true;
}
@@ -5757,21 +6151,15 @@ suspend_process_2(BIF_ALIST_2)
if (smon->pending && unless_suspending)
res = am_false;
else {
- ErtsRunQueue *rq;
if (smon->pending == INT_MAX)
goto system_limit;
smon->pending++;
- rq = erts_get_runq_proc(suspendee);
- erts_smp_runq_lock(rq);
- if (suspendee->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)
+ 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);
- else
- do_bif_suspend_process(smon, suspendee, rq);
- erts_smp_runq_unlock(rq);
res = am_true;
}
@@ -5808,7 +6196,6 @@ suspend_process_2(BIF_ALIST_2)
/* done */
}
else {
- ErtsRunQueue *cp_rq, *s_rq;
/* We haven't got any active suspends on the suspendee */
/*
@@ -5825,12 +6212,7 @@ suspend_process_2(BIF_ALIST_2)
if (!unless_suspending || smon->pending == 0)
smon->pending++;
- cp_rq = erts_get_runq_proc(BIF_P);
- s_rq = erts_get_runq_proc(suspendee);
- erts_smp_runqs_lock(cp_rq, s_rq);
- if (!(suspendee->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)) {
- do_bif_suspend_process(smon, suspendee, s_rq);
- erts_smp_runqs_unlock(cp_rq, s_rq);
+ if (do_bif_suspend_process(BIF_P, smon, suspendee)) {
res = (!unless_suspending || smon->active == 1
? am_true
: am_false);
@@ -5839,7 +6221,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));
@@ -5850,8 +6232,7 @@ suspend_process_2(BIF_ALIST_2)
* This time with BIF_P->suspendee == BIF_ARG_1 (see
* above).
*/
- suspend_process(cp_rq, BIF_P);
- erts_smp_runqs_unlock(cp_rq, s_rq);
+ suspend_process(BIF_P, BIF_P);
goto yield;
}
}
@@ -5859,9 +6240,15 @@ suspend_process_2(BIF_ALIST_2)
}
#endif /* ERTS_SMP */
-
- ASSERT(suspendee->status == P_SUSPENDED || (asynchronous && smon->pending));
- ASSERT(suspendee->status == P_SUSPENDED || !smon->active);
+#ifdef DEBUG
+ {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&suspendee->state);
+ ASSERT((state & ERTS_PSFLG_SUSPENDED)
+ || (asynchronous && smon->pending));
+ ASSERT((state & ERTS_PSFLG_SUSPENDED)
+ || !smon->active);
+ }
+#endif
erts_smp_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
erts_smp_proc_unlock(BIF_P, xlocks);
@@ -5908,7 +6295,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);
@@ -5956,9 +6343,8 @@ resume_process_1(BIF_ALIST_1)
if (!suspendee)
goto no_suspendee;
- ASSERT(suspendee->status == P_SUSPENDED
- || (suspendee->status == P_GARBING
- && suspendee->gcstatus == P_SUSPENDED));
+ ASSERT(ERTS_PSFLG_SUSPENDED
+ & erts_smp_atomic32_read_nob(&suspendee->state));
resume_process(suspendee);
erts_smp_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS);
@@ -5987,449 +6373,46 @@ erts_run_queues_len(Uint *qlen)
Uint len = 0;
ERTS_ATOMIC_FOREACH_RUNQ(rq,
{
- if (qlen)
- qlen[i++] = rq->procs.len;
- len += rq->procs.len;
+ Sint pqlen = 0;
+ int pix;
+ for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++)
+ pqlen += RUNQ_READ_LEN(&rq->procs.prio_info[pix].len);
+
+ if (pqlen < 0)
+ pqlen = 0;
+ if (qlen)
+ qlen[i++] = pqlen;
+ len += pqlen;
}
);
return len;
}
-#ifdef HARDDEBUG_RUNQS
-static void
-check_procs_runq(ErtsRunQueue *runq, Process *p_in_q, Process *p_not_in_q)
-{
- int len[ERTS_NO_PROC_PRIO_LEVELS] = {0};
- int tot_len;
- int prioq, prio;
- int found_p_in_q;
- Process *p, *prevp;
-
- found_p_in_q = 0;
- for (prioq = 0; prioq < ERTS_NO_PROC_PRIO_LEVELS - 1; prioq++) {
- prevp = NULL;
- for (p = runq->procs.prio[prioq].first; p; p = p->next) {
- ASSERT(p != p_not_in_q);
- if (p == p_in_q)
- found_p_in_q = 1;
- switch (p->prio) {
- case PRIORITY_MAX:
- case PRIORITY_HIGH:
- case PRIORITY_NORMAL:
- ASSERT(prioq == p->prio);
- break;
- case PRIORITY_LOW:
- ASSERT(prioq == PRIORITY_NORMAL);
- break;
- default:
- ASSERT(!"Bad prio on process");
- }
- len[p->prio]++;
- ASSERT(prevp == p->prev);
- if (p->prev) {
- ASSERT(p->prev->next == p);
- }
- else {
- ASSERT(runq->procs.prio[prioq].first == p);
- }
- if (p->next) {
- ASSERT(p->next->prev == p);
- }
- else {
- ASSERT(runq->procs.prio[prioq].last == p);
- }
- ASSERT(p->run_queue == runq);
- prevp = p;
- }
- }
-
- ASSERT(!p_in_q || found_p_in_q);
-
- tot_len = 0;
- for (prio = 0; prio < ERTS_NO_PROC_PRIO_LEVELS; prio++) {
- ASSERT(len[prio] == runq->procs.prio_info[prio].len);
- if (len[prio]) {
- ASSERT(runq->flags & (1 << prio));
- }
- else {
- ASSERT(!(runq->flags & (1 << prio)));
- }
- tot_len += len[prio];
- }
- ASSERT(runq->procs.len == tot_len);
-}
-# define ERTS_DBG_CHK_PROCS_RUNQ(RQ) check_procs_runq((RQ), NULL, NULL)
-# define ERTS_DBG_CHK_PROCS_RUNQ_PROC(RQ, P) check_procs_runq((RQ), (P), NULL)
-# define ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(RQ, P) check_procs_runq((RQ), NULL, (P))
-#else
-# define ERTS_DBG_CHK_PROCS_RUNQ(RQ)
-# define ERTS_DBG_CHK_PROCS_RUNQ_PROC(RQ, P)
-# define ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(RQ, P)
-#endif
-
-
-static ERTS_INLINE void
-enqueue_process(ErtsRunQueue *runq, Process *p)
-{
- ErtsRunPrioQueue *rpq;
- ErtsRunQueueInfo *rqi;
-
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- ASSERT(p->bound_runq || !(runq->flags & ERTS_RUNQ_FLG_SUSPENDED));
-
- rqi = &runq->procs.prio_info[p->prio];
- rqi->len++;
- if (rqi->max_len < rqi->len)
- rqi->max_len = rqi->len;
-
- runq->procs.len++;
- runq->len++;
- if (runq->max_len < runq->len)
- runq->max_len = runq->len;
-
- runq->flags |= (1 << p->prio);
-
- rpq = (p->prio == PRIORITY_LOW
- ? &runq->procs.prio[PRIORITY_NORMAL]
- : &runq->procs.prio[p->prio]);
-
- p->next = NULL;
- p->prev = rpq->last;
- if (rpq->last)
- rpq->last->next = p;
- else
- rpq->first = p;
- rpq->last = p;
-
- switch (p->status) {
- case P_EXITING:
- break;
- case P_GARBING:
- p->gcstatus = P_RUNABLE;
- break;
- default:
- p->status = P_RUNABLE;
- break;
- }
-
-#ifdef ERTS_SMP
- p->status_flags |= ERTS_PROC_SFLG_INRUNQ;
-#endif
-
- ERTS_DBG_CHK_PROCS_RUNQ_PROC(runq, p);
-}
-
-
-static ERTS_INLINE int
-dequeue_process(ErtsRunQueue *runq, Process *p)
-{
- ErtsRunPrioQueue *rpq;
- int res = 1;
-
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
- ERTS_DBG_CHK_PROCS_RUNQ(runq);
-
- rpq = &runq->procs.prio[p->prio == PRIORITY_LOW ? PRIORITY_NORMAL : p->prio];
- if (p->prev) {
- p->prev->next = p->next;
- }
- else if (rpq->first == p) {
- rpq->first = p->next;
- }
- else {
- res = 0;
- }
- if (p->next) {
- p->next->prev = p->prev;
- }
- else if (rpq->last == p) {
- rpq->last = p->prev;
- }
- else {
- ASSERT(res == 0);
- }
-
- if (res) {
-
- if (--runq->procs.prio_info[p->prio].len == 0)
- runq->flags &= ~(1 << p->prio);
- runq->procs.len--;
- runq->len--;
-
-#ifdef ERTS_SMP
- p->status_flags &= ~ERTS_PROC_SFLG_INRUNQ;
-#endif
- }
-
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p);
- return res;
-}
-
-/* schedule a process */
-static ERTS_INLINE ErtsRunQueue *
-internal_add_to_runq(ErtsRunQueue *runq, Process *p)
-{
- Uint32 prev_status = p->status;
- ErtsRunQueue *add_runq;
-#ifdef ERTS_SMP
-
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
-
- if (p->status_flags & ERTS_PROC_SFLG_INRUNQ)
- return NULL;
- else if (p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) {
- ASSERT(ERTS_PROC_IS_EXITING(p) || p->rcount == 0);
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p);
- p->status_flags |= ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- return NULL;
- }
- ASSERT(!p->scheduler_data);
-#endif
-
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p);
-#ifndef ERTS_SMP
- /* Never schedule a suspended process (ok in smp case) */
- ASSERT(ERTS_PROC_IS_EXITING(p) || p->rcount == 0);
- add_runq = runq;
-#else
- ASSERT(!p->bound_runq || p->bound_runq == p->run_queue);
- if (p->bound_runq) {
- if (p->bound_runq == runq)
- add_runq = runq;
- else {
- add_runq = p->bound_runq;
- erts_smp_xrunq_lock(runq, add_runq);
- }
- }
- else {
- add_runq = erts_check_emigration_need(runq, p->prio);
- if (!add_runq)
- add_runq = runq;
- else /* Process emigrated */
- p->run_queue = add_runq;
- }
-#endif
-
- /* Enqueue the process */
- enqueue_process(add_runq, p);
-
- if ((erts_system_profile_flags.runnable_procs)
- && (prev_status == P_WAITING
- || prev_status == P_SUSPENDED)) {
- profile_runnable_proc(p, am_active);
- }
-
- if (add_runq != runq)
- erts_smp_runq_unlock(add_runq);
-
- return add_runq;
-}
-
-
-void
-erts_add_to_runq(Process *p)
-{
- ErtsRunQueue *notify_runq;
- ErtsRunQueue *runq = erts_get_runq_proc(p);
- erts_smp_runq_lock(runq);
- notify_runq = internal_add_to_runq(runq, p);
- erts_smp_runq_unlock(runq);
- smp_notify_inc_runq(notify_runq);
-
-}
-
-/* Possibly remove a scheduled process we need to suspend */
-
-static int
-remove_proc_from_runq(ErtsRunQueue *rq, Process *p, int to_inactive)
-{
- int res;
-
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
-
-#ifdef ERTS_SMP
- if (p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ) {
- p->status_flags &= ~ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- ASSERT(!remove_proc_from_runq(rq, p, 0));
- return 1;
- }
-#endif
-
- res = dequeue_process(rq, p);
-
- if (res && erts_system_profile_flags.runnable_procs && to_inactive)
- profile_runnable_proc(p, am_inactive);
-
-#ifdef ERTS_SMP
- ASSERT(!(p->status_flags & ERTS_PROC_SFLG_INRUNQ));
-#endif
-
- return res;
-}
-
-#ifdef ERTS_SMP
-
-ErtsMigrateResult
-erts_proc_migrate(Process *p, ErtsProcLocks *plcks,
- ErtsRunQueue *from_rq, int *from_locked,
- ErtsRunQueue *to_rq, int *to_locked)
-{
- ERTS_SMP_LC_ASSERT(*plcks == erts_proc_lc_my_proc_locks(p));
- ERTS_SMP_LC_ASSERT((ERTS_PROC_LOCK_STATUS & *plcks)
- || from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- /*
- * If we have the lock on the run queue to migrate to,
- * check that it isn't suspended. If it is suspended,
- * we will refuse to migrate to it anyway.
- */
- if (*to_locked && (to_rq->flags & ERTS_RUNQ_FLG_SUSPENDED))
- return ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED;
-
- /* We need status lock on process and locks on both run queues */
-
- if (!(ERTS_PROC_LOCK_STATUS & *plcks)) {
- if (erts_smp_proc_trylock(p, ERTS_PROC_LOCK_STATUS) == EBUSY) {
- ErtsProcLocks lcks = *plcks;
- Eterm pid = p->id;
- Process *proc = *plcks ? p : NULL;
-
- if (*from_locked) {
- *from_locked = 0;
- erts_smp_runq_unlock(from_rq);
- }
- if (*to_locked) {
- *to_locked = 0;
- erts_smp_runq_unlock(to_rq);
- }
-
- proc = erts_pid2proc_opt(proc,
- lcks,
- pid,
- lcks|ERTS_PROC_LOCK_STATUS,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- if (!proc) {
- *plcks = 0;
- return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ;
- }
- ASSERT(proc == p);
- }
- *plcks |= ERTS_PROC_LOCK_STATUS;
- }
-
- ASSERT(!p->bound_runq);
-
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- if (p->run_queue != from_rq)
- return ERTS_MIGRATE_FAILED_RUNQ_CHANGED;
-
- if (!*from_locked || !*to_locked) {
- if (from_rq < to_rq) {
- if (!*to_locked) {
- if (!*from_locked)
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- else if (erts_smp_runq_trylock(from_rq) == EBUSY) {
- erts_smp_runq_unlock(to_rq);
- erts_smp_runq_lock(from_rq);
- erts_smp_runq_lock(to_rq);
- }
- }
- else {
- if (!*from_locked) {
- if (!*to_locked)
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- else if (erts_smp_runq_trylock(to_rq) == EBUSY) {
- erts_smp_runq_unlock(from_rq);
- erts_smp_runq_lock(to_rq);
- erts_smp_runq_lock(from_rq);
- }
- }
- *to_locked = *from_locked = 1;
- }
-
- ERTS_SMP_LC_CHK_RUNQ_LOCK(from_rq, *from_locked);
- ERTS_SMP_LC_CHK_RUNQ_LOCK(to_rq, *to_locked);
-
- /* Ok we now got all locks we need; do it... */
-
- /* Refuse to migrate to a suspended run queue */
- if (to_rq->flags & ERTS_RUNQ_FLG_SUSPENDED)
- return ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED;
-
- if ((p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING)
- || !(p->status_flags & ERTS_PROC_SFLG_INRUNQ))
- return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ;
-
- dequeue_process(from_rq, p);
- p->run_queue = to_rq;
- enqueue_process(to_rq, p);
-
- return ERTS_MIGRATE_SUCCESS;
-}
-#endif /* ERTS_SMP */
-
Eterm
erts_process_status(Process *c_p, ErtsProcLocks c_p_locks,
Process *rp, Eterm rpid)
{
Eterm res = am_undefined;
- Process *p;
-
- if (rp) {
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS
- & erts_proc_lc_my_proc_locks(rp));
- p = rp;
- }
- else {
- p = erts_pid2proc_opt(c_p, c_p_locks,
- rpid, ERTS_PROC_LOCK_STATUS,
- ERTS_P2P_FLG_ALLOW_OTHER_X);
- }
+ Process *p = rp ? rp : erts_proc_lookup_raw(rpid);
if (p) {
- switch (p->status) {
- case P_RUNABLE:
- res = am_runnable;
- break;
- case P_WAITING:
- res = am_waiting;
- break;
- case P_RUNNING:
- res = am_running;
- break;
- case P_EXITING:
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ if (state & ERTS_PSFLG_FREE)
+ res = am_free;
+ else if (state & ERTS_PSFLG_EXITING)
res = am_exiting;
- break;
- case P_GARBING:
+ else if (state & ERTS_PSFLG_GC)
res = am_garbage_collecting;
- break;
- case P_SUSPENDED:
+ else if (state & ERTS_PSFLG_SUSPENDED)
res = am_suspended;
- break;
- case P_FREE: /* We cannot look up a process in P_FREE... */
- default: /* Not a valid status... */
- erl_exit(1, "Bad status (%b32u) found for process %T\n",
- p->status, p->id);
- break;
- }
-
-#ifdef ERTS_SMP
- if (!rp && (p != c_p || !(ERTS_PROC_LOCK_STATUS & c_p_locks)))
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ else if (state & ERTS_PSFLG_RUNNING)
+ res = am_running;
+ else if (state & ERTS_PSFLG_ACTIVE)
+ res = am_runnable;
+ else
+ res = am_waiting;
}
+#ifdef ERTS_SMP
else {
int i;
ErtsSchedulerData *esdp;
@@ -6437,50 +6420,53 @@ 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;
}
erts_smp_runq_unlock(esdp->run_queue);
}
-
-#endif
-
}
-
+#endif
return res;
}
/*
-** Suspend a process
+** Suspend a currently executing process
** If we are to suspend on a port the busy_port is the thing
** otherwise busy_port is NIL
*/
void
-erts_suspend(Process* process, ErtsProcLocks process_locks, Port *busy_port)
+erts_suspend(Process* c_p, ErtsProcLocks c_p_locks, Port *busy_port)
{
- ErtsRunQueue *rq;
+ int suspend;
- ERTS_SMP_LC_ASSERT(process_locks == erts_proc_lc_my_proc_locks(process));
- if (!(process_locks & ERTS_PROC_LOCK_STATUS))
- erts_smp_proc_lock(process, ERTS_PROC_LOCK_STATUS);
-
- rq = erts_get_runq_proc(process);
-
- erts_smp_runq_lock(rq);
-
- suspend_process(rq, process);
-
- erts_smp_runq_unlock(rq);
+ 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)
- erts_wake_process_later(busy_port, process);
+ suspend = erts_save_suspend_process_on_port(busy_port, c_p);
+ else
+ suspend = 1;
- if (!(process_locks & ERTS_PROC_LOCK_STATUS))
- erts_smp_proc_unlock(process, ERTS_PROC_LOCK_STATUS);
+ if (suspend) {
+#ifdef DEBUG
+ int res =
+#endif
+ 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
@@ -6495,16 +6481,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++;
}
@@ -6520,57 +6509,54 @@ erts_resume_processes(ErtsProcList *plp)
Eterm
erts_get_process_priority(Process *p)
{
- ErtsRunQueue *rq;
- Eterm value;
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- rq = erts_get_runq_proc(p);
- erts_smp_runq_lock(rq);
- switch(p->prio) {
- case PRIORITY_MAX: value = am_max; break;
- case PRIORITY_HIGH: value = am_high; break;
- case PRIORITY_NORMAL: value = am_normal; break;
- case PRIORITY_LOW: value = am_low; break;
- default: ASSERT(0); value = am_undefined; break;
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
+ switch (state & ERTS_PSFLG_PRIO_MASK) {
+ case PRIORITY_MAX: return am_max;
+ case PRIORITY_HIGH: return am_high;
+ case PRIORITY_NORMAL: return am_normal;
+ case PRIORITY_LOW: return am_low;
+ default: ASSERT(0); return am_undefined;
}
- erts_smp_runq_unlock(rq);
- return value;
}
Eterm
-erts_set_process_priority(Process *p, Eterm new_value)
+erts_set_process_priority(Process *p, Eterm value)
{
- ErtsRunQueue *rq;
- Eterm old_value;
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
- rq = erts_get_runq_proc(p);
-#ifdef ERTS_SMP
- ASSERT(!(p->status_flags & ERTS_PROC_SFLG_INRUNQ));
-#endif
- erts_smp_runq_lock(rq);
- switch(p->prio) {
- case PRIORITY_MAX: old_value = am_max; break;
- case PRIORITY_HIGH: old_value = am_high; break;
- case PRIORITY_NORMAL: old_value = am_normal; break;
- case PRIORITY_LOW: old_value = am_low; break;
- default: ASSERT(0); old_value = am_undefined; break;
- }
- switch (new_value) {
- case am_max: p->prio = PRIORITY_MAX; break;
- case am_high: p->prio = PRIORITY_HIGH; break;
- case am_normal: p->prio = PRIORITY_NORMAL; break;
- case am_low: p->prio = PRIORITY_LOW; break;
- default: old_value = THE_NON_VALUE; break;
+ erts_aint32_t a, oprio, nprio;
+
+ switch (value) {
+ case am_max: nprio = (erts_aint32_t) PRIORITY_MAX; break;
+ case am_high: nprio = (erts_aint32_t) PRIORITY_HIGH; break;
+ case am_normal: nprio = (erts_aint32_t) PRIORITY_NORMAL; break;
+ case am_low: nprio = (erts_aint32_t) PRIORITY_LOW; break;
+ default: return THE_NON_VALUE; break;
}
- erts_smp_runq_unlock(rq);
- return old_value;
-}
-/* note that P_RUNNING is only set so that we don't try to remove
-** running processes from the schedule queue if they exit - a running
-** process not being in the schedule queue!!
-** Schedule for up to INPUT_REDUCTIONS context switches,
-** return 1 if more to do.
-*/
+ a = erts_smp_atomic32_read_nob(&p->state);
+ if (nprio == (a & ERTS_PSFLG_PRIO_MASK))
+ oprio = nprio;
+ else {
+ erts_aint32_t e, n;
+ do {
+ oprio = a & ERTS_PSFLG_PRIO_MASK;
+ n = e = a;
+
+ ASSERT(!(a & ERTS_PSFLG_IN_RUNQ));
+
+ n &= ~ERTS_PSFLG_PRIO_MASK;
+ n |= nprio;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ } while (a != e);
+ }
+
+ switch (oprio) {
+ case PRIORITY_MAX: return am_max;
+ case PRIORITY_HIGH: return am_high;
+ case PRIORITY_NORMAL: return am_normal;
+ case PRIORITY_LOW: return am_low;
+ default: ASSERT(0); return am_undefined;
+ }
+}
/*
* schedule() is called from BEAM (process_main()) or HiPE
@@ -6593,7 +6579,6 @@ erts_set_process_priority(Process *p, Eterm new_value)
Process *schedule(Process *p, int calls)
{
ErtsRunQueue *rq;
- ErtsRunPrioQueue *rpq;
erts_aint_t dt;
ErtsSchedulerData *esdp;
int context_reds;
@@ -6601,6 +6586,8 @@ Process *schedule(Process *p, int calls)
int input_reductions;
int actual_reds;
int reds;
+ Uint32 flags;
+ erts_aint32_t state = 0; /* Supress warning... */
#ifdef USE_VM_PROBES
if (p != NULL && DTRACE_ENABLED(process_unscheduled)) {
@@ -6656,95 +6643,61 @@ Process *schedule(Process *p, int calls)
erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if ((erts_system_profile_flags.runnable_procs)
- && (p->status == P_WAITING)) {
- profile_runnable_proc(p, am_inactive);
- }
+ state = erts_smp_atomic32_read_acqb(&p->state);
if (IS_TRACED(p)) {
- if (IS_TRACED_FL(p, F_TRACE_CALLS) && p->status != P_FREE) {
+ if (IS_TRACED_FL(p, F_TRACE_CALLS) && !(state & ERTS_PSFLG_FREE))
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_OUT);
- }
- switch (p->status) {
- case P_EXITING:
- if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, am_out_exiting);
- break;
- case P_FREE:
+ if (state & (ERTS_PSFLG_FREE|ERTS_PSFLG_EXITING)) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
- trace_sched(p, am_out_exited);
- break;
- default:
+ trace_sched(p, ((state & ERTS_PSFLG_FREE)
+ ? am_out_exited
+ : am_out_exiting));
+ }
+ else {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED))
trace_sched(p, am_out);
else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
trace_virtual_sched(p, am_out);
- break;
}
- }
-
-#ifdef ERTS_SMP
- if (ERTS_PROC_PENDING_EXIT(p)) {
- erts_handle_pending_exit(p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- p->status_flags |= ERTS_PROC_SFLG_PENDADD2SCHEDQ;
}
- if (p->pending_suspenders) {
- handle_pending_suspend(p,
- ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- ASSERT(!(p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ)
- || p->rcount == 0);
- }
+#ifdef ERTS_SMP
+ if (state & ERTS_PSFLG_PENDING_EXIT)
+ erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS));
+ if (p->pending_suspenders)
+ handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS));
#endif
esdp->reductions += reds;
- erts_smp_runq_lock(rq);
+ schedule_out_process(rq, state, p); /* Returns with rq locked! */
- ERTS_PROC_REDUCTIONS_EXECUTED(rq, p->prio, reds, actual_reds);
+ ERTS_PROC_REDUCTIONS_EXECUTED(rq,
+ (int) (state & ERTS_PSFLG_PRIO_MASK),
+ reds,
+ actual_reds);
esdp->current_process = NULL;
#ifdef ERTS_SMP
p->scheduler_data = NULL;
- p->runq_flags &= ~ERTS_PROC_RUNQ_FLG_RUNNING;
- p->status_flags &= ~ERTS_PROC_SFLG_RUNNING;
-
- if (p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ) {
- ErtsRunQueue *notify_runq;
- p->status_flags &= ~ERTS_PROC_SFLG_PENDADD2SCHEDQ;
- notify_runq = internal_add_to_runq(rq, p);
- if (notify_runq != rq)
- smp_notify_inc_runq(notify_runq);
- }
#endif
- if (p->status == P_FREE) {
+ if (state & ERTS_PSFLG_FREE) {
#ifdef ERTS_SMP
ASSERT(esdp->free_process == p);
esdp->free_process = NULL;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- erts_smp_proc_dec_refc(p);
#else
erts_free_proc(p);
#endif
- } else {
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
}
-#ifdef ERTS_SMP
- {
- ErtsProcList *pnd_xtrs = rq->procs.pending_exiters;
- rq->procs.pending_exiters = NULL;
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
- if (pnd_xtrs) {
- erts_smp_runq_unlock(rq);
- handle_pending_exiters(pnd_xtrs);
- erts_smp_runq_lock(rq);
- }
-
- }
+#ifdef ERTS_SMP
ASSERT(!esdp->free_process);
#endif
ASSERT(!esdp->current_process);
@@ -6764,8 +6717,22 @@ Process *schedule(Process *p, int calls)
ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
check_activities_to_run: {
+#ifdef ERTS_SMP
+ ErtsMigrationPaths *mps;
+ ErtsMigrationPath *mp;
#ifdef ERTS_SMP
+ {
+ ErtsProcList *pnd_xtrs = rq->procs.pending_exiters;
+ 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);
+ }
+
+ }
+#endif
if (rq->check_balance_reds <= 0)
check_balance(rq);
@@ -6773,20 +6740,28 @@ Process *schedule(Process *p, int calls)
ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
- if (rq->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK)
- immigrate(rq);
+ mps = erts_get_migration_paths_managed();
+ mp = &mps->mpath[rq->ix];
+
+ if (mp->flags & ERTS_RUNQ_FLGS_IMMIGRATE_QMASK)
+ immigrate(rq, mp);
+
+ continue_check_activities_to_run:
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ continue_check_activities_to_run_known_flags:
- continue_check_activities_to_run:
- if (rq->flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND
- | ERTS_RUNQ_FLG_SUSPENDED)) {
- if (rq->flags & ERTS_RUNQ_FLG_SUSPENDED) {
- ASSERT(erts_smp_atomic32_read_nob(&esdp->ssi->flags)
- & ERTS_SSI_FLG_SUSPENDED);
+ if (flags & (ERTS_RUNQ_FLG_CHK_CPU_BIND|ERTS_RUNQ_FLG_SUSPENDED)) {
+
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
suspend_scheduler(esdp);
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
}
- if (rq->flags & ERTS_RUNQ_FLG_CHK_CPU_BIND)
+ if (flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) {
+ flags = ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_CHK_CPU_BIND);
+ flags &= ~ ERTS_RUNQ_FLG_CHK_CPU_BIND;
erts_sched_check_cpu_bind(esdp);
+ }
}
{
@@ -6815,14 +6790,12 @@ Process *schedule(Process *p, int calls)
}
#endif /* ERTS_SMP */
- ASSERT(rq->len == rq->procs.len + rq->ports.info.len);
-
- if ((rq->len == 0 && !rq->misc.start)
- || (rq->halt_in_progress
- && rq->ports.info.len == 0 && !rq->misc.start)) {
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ if ((!(flags & ERTS_RUNQ_FLGS_QMASK) && !rq->misc.start)
+ || (rq->halt_in_progress && ERTS_EMPTY_RUNQ_PORTS(rq))) {
+ /* Prepare for scheduler wait */
#ifdef ERTS_SMP
-
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
rq->wakeup_other = 0;
@@ -6830,21 +6803,27 @@ Process *schedule(Process *p, int calls)
empty_runq(rq);
- if (rq->flags & ERTS_RUNQ_FLG_SUSPENDED) {
- ASSERT(erts_smp_atomic32_read_nob(&esdp->ssi->flags)
- & ERTS_SSI_FLG_SUSPENDED);
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
non_empty_runq(rq);
- goto continue_check_activities_to_run;
+ goto continue_check_activities_to_run_known_flags;
}
- else if (!(rq->flags & ERTS_RUNQ_FLG_INACTIVE)) {
+ else if (!(flags & ERTS_RUNQ_FLG_INACTIVE)) {
+ if (try_steal_task(rq)) {
+ non_empty_runq(rq);
+ goto continue_check_activities_to_run;
+ }
+
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+
/*
* Check for ERTS_RUNQ_FLG_SUSPENDED has to be done
* after trying to steal a task.
*/
- if (try_steal_task(rq)
- || (rq->flags & ERTS_RUNQ_FLG_SUSPENDED)) {
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ if (flags & ERTS_RUNQ_FLG_SUSPENDED) {
non_empty_runq(rq);
- goto continue_check_activities_to_run;
+ goto continue_check_activities_to_run_known_flags;
}
}
@@ -6875,6 +6854,7 @@ Process *schedule(Process *p, int calls)
erl_sys_schedule(1);
dt = erts_do_time_read_and_reset();
if (dt) erts_bump_timer(dt);
+
#ifdef ERTS_SMP
erts_smp_runq_lock(rq);
clear_sys_scheduling();
@@ -6888,14 +6868,14 @@ Process *schedule(Process *p, int calls)
exec_misc_ops(rq);
#ifdef ERTS_SMP
- wakeup_other.check(rq);
+ wakeup_other.check(rq, flags);
#endif
/*
* Find a new port to run.
*/
- if (rq->ports.info.len) {
+ if (RUNQ_READ_LEN(&rq->ports.info.len)) {
int have_outstanding_io;
have_outstanding_io = erts_port_task_execute(rq, &esdp->current_port);
if ((have_outstanding_io && fcalls > 2*input_reductions)
@@ -6922,167 +6902,130 @@ Process *schedule(Process *p, int calls)
/*
* Find a new process to run.
*/
- pick_next_process:
-
- ERTS_DBG_CHK_PROCS_RUNQ(rq);
-
- switch (rq->flags & ERTS_RUNQ_FLGS_PROCS_QMASK) {
- case MAX_BIT:
- case MAX_BIT|HIGH_BIT:
- case MAX_BIT|NORMAL_BIT:
- case MAX_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT:
- case MAX_BIT|HIGH_BIT|LOW_BIT:
- case MAX_BIT|NORMAL_BIT|LOW_BIT:
- case MAX_BIT|HIGH_BIT|NORMAL_BIT|LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_MAX];
- break;
- case HIGH_BIT:
- case HIGH_BIT|NORMAL_BIT:
- case HIGH_BIT|LOW_BIT:
- case HIGH_BIT|NORMAL_BIT|LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_HIGH];
- break;
- case NORMAL_BIT:
- rpq = &rq->procs.prio[PRIORITY_NORMAL];
- break;
- case LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_NORMAL];
- break;
- case NORMAL_BIT|LOW_BIT:
- rpq = &rq->procs.prio[PRIORITY_NORMAL];
- ASSERT(rpq->first != NULL);
- p = rpq->first;
- if (p->prio == PRIORITY_LOW) {
- if (p == rpq->last || p->skipped >= RESCHEDULE_LOW-1)
- p->skipped = 0;
- else {
- /* skip it */
- p->skipped++;
- rpq->first = p->next;
- rpq->first->prev = NULL;
- rpq->last->next = p;
- p->prev = rpq->last;
- p->next = NULL;
- rpq->last = p;
+ pick_next_process: {
+ int prio_q;
+ int qmask;
+
+ flags = ERTS_RUNQ_FLGS_GET_NOB(rq);
+ qmask = (int) (flags & ERTS_RUNQ_FLGS_PROCS_QMASK);
+ switch (qmask & -qmask) {
+ case MAX_BIT:
+ prio_q = PRIORITY_MAX;
+ break;
+ case HIGH_BIT:
+ prio_q = PRIORITY_HIGH;
+ break;
+ case NORMAL_BIT:
+ case LOW_BIT:
+ prio_q = PRIORITY_NORMAL;
+ if (check_requeue_process(rq, PRIORITY_NORMAL))
goto pick_next_process;
- }
+ break;
+ case 0: /* No process at all */
+ default:
+ ASSERT(qmask == 0);
+ goto check_activities_to_run;
}
- break;
- case 0: /* No process at all */
- default:
- ASSERT((rq->flags & ERTS_RUNQ_FLGS_PROCS_QMASK) == 0);
- ASSERT(rq->procs.len == 0);
- goto check_activities_to_run;
- }
-
- BM_START_TIMER(system);
-
- /*
- * Take the chosen process out of the queue.
- */
- ASSERT(rpq->first); /* Wrong qmask in rq->flags? */
- p = rpq->first;
-#ifdef ERTS_SMP
- ERTS_SMP_LC_ASSERT(rq == p->run_queue);
-#endif
- rpq->first = p->next;
- if (!rpq->first)
- rpq->last = NULL;
- else
- rpq->first->prev = NULL;
- p->next = p->prev = NULL;
+ BM_START_TIMER(system);
- if (--rq->procs.prio_info[p->prio].len == 0)
- rq->flags &= ~(1 << p->prio);
- ASSERT(rq->procs.len > 0);
- rq->procs.len--;
- ASSERT(rq->len > 0);
- rq->len--;
+ /*
+ * Take the chosen process out of the queue.
+ */
+ p = dequeue_process(rq, prio_q, &state);
- {
- Uint32 ee_flgs = (ERTS_RUNQ_FLG_EVACUATE(p->prio)
- | ERTS_RUNQ_FLG_EMIGRATE(p->prio));
+ ASSERT(p); /* Wrong qmask in rq->flags? */
- if ((rq->flags & (ERTS_RUNQ_FLG_SUSPENDED|ee_flgs)) == ee_flgs)
- ERTS_UNSET_RUNQ_FLG_EVACUATE(rq->flags, p->prio);
- }
+ while (1) {
+ erts_aint32_t exp, new, tmp;
+ tmp = new = exp = state;
+ new &= ~ERTS_PSFLG_IN_RUNQ;
+ tmp = state & (ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT);
+ if (tmp != ERTS_PSFLG_SUSPENDED)
+ new |= ERTS_PSFLG_RUNNING;
+ state = erts_smp_atomic32_cmpxchg_relb(&p->state, new, exp);
+ if (state == exp) {
+ tmp = state & (ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT);
+ if (tmp == ERTS_PSFLG_SUSPENDED)
+ goto pick_next_process;
+ state = new;
+ break;
+ }
+ }
- ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(rq, p);
+ rq->procs.context_switches++;
- rq->procs.context_switches++;
+ esdp->current_process = p;
- esdp->current_process = p;
+ }
#ifdef ERTS_SMP
- p->runq_flags |= ERTS_PROC_RUNQ_FLG_RUNNING;
erts_smp_runq_unlock(rq);
+ if (flags & ERTS_RUNQ_FLG_PROTECTED)
+ (void) ERTS_RUNQ_FLGS_UNSET(rq, ERTS_RUNQ_FLG_PROTECTED);
+
ERTS_SMP_CHK_NO_PROC_LOCKS;
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
if (erts_sched_stat.enabled) {
+ int prio;
UWord old = ERTS_PROC_SCHED_ID(p,
(ERTS_PROC_LOCK_MAIN
| ERTS_PROC_LOCK_STATUS),
(UWord) esdp->no);
int migrated = old && old != esdp->no;
+ prio = (int) (state & ERTS_PSFLG_PRIO_MASK);
+
erts_smp_spin_lock(&erts_sched_stat.lock);
- erts_sched_stat.prio[p->prio].total_executed++;
- erts_sched_stat.prio[p->prio].executed++;
+ erts_sched_stat.prio[prio].total_executed++;
+ erts_sched_stat.prio[prio].executed++;
if (migrated) {
- erts_sched_stat.prio[p->prio].total_migrated++;
- erts_sched_stat.prio[p->prio].migrated++;
+ erts_sched_stat.prio[prio].total_migrated++;
+ erts_sched_stat.prio[prio].migrated++;
}
erts_smp_spin_unlock(&erts_sched_stat.lock);
}
- p->status_flags |= ERTS_PROC_SFLG_RUNNING;
- p->status_flags &= ~ERTS_PROC_SFLG_INRUNQ;
if (ERTS_PROC_PENDING_EXIT(p)) {
erts_handle_pending_exit(p,
ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
+ state = erts_smp_atomic32_read_nob(&p->state);
}
ASSERT(!p->scheduler_data);
p->scheduler_data = esdp;
-
#endif
- ASSERT(p->status != P_SUSPENDED); /* Never run a suspended process */
+ /* Never run a suspended process */
+ ASSERT(!(ERTS_PSFLG_SUSPENDED & erts_smp_atomic32_read_nob(&p->state)));
reds = context_reds;
if (IS_TRACED(p)) {
- switch (p->status) {
- case P_EXITING:
+ if (state & ERTS_PSFLG_EXITING) {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_EXIT))
trace_sched(p, am_in_exiting);
- break;
- default:
+ }
+ else {
if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED))
trace_sched(p, am_in);
else if (ARE_TRACE_FLAGS_ON(p, F_TRACE_SCHED_PROCS))
trace_virtual_sched(p, am_in);
- break;
}
if (IS_TRACED_FL(p, F_TRACE_CALLS)) {
erts_schedule_time_break(p, ERTS_BP_CALL_TIME_SCHEDULE_IN);
}
}
- if (p->status != P_EXITING)
- p->status = P_RUNNING;
-
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
- if (!ERTS_PROC_IS_EXITING(p)
+ if (!(state & ERTS_PSFLG_EXITING)
&& ((FLAGS(p) & F_FORCE_GC)
|| (MSO(p).overhead > BIN_VHEAP_SZ(p)))) {
reds -= erts_garbage_collect(p, 0, p->arg_reg, p->arity);
@@ -7173,17 +7116,19 @@ erts_schedule_misc_op(void (*func)(void *), void *arg)
ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErtsRunQueue *rq = esdp ? esdp->run_queue : ERTS_RUNQ_IX(0);
ErtsMiscOpList *molp = misc_op_list_alloc();
+#ifdef ERTS_SMP
+ ErtsMigrationPaths *mpaths = erts_get_migration_paths();
- erts_smp_runq_lock(rq);
-
- while (rq->misc.evac_runq) {
- ErtsRunQueue *tmp_rq = rq->misc.evac_runq;
- erts_smp_runq_unlock(rq);
- rq = tmp_rq;
- erts_smp_runq_lock(rq);
+ if (!mpaths)
+ rq = ERTS_RUNQ_IX(0);
+ else {
+ ErtsRunQueue *erq = mpaths->mpath[rq->ix].misc_evac_runq;
+ if (erq)
+ rq = erq;
}
+#endif
- ASSERT(!(rq->flags & ERTS_RUNQ_FLG_SUSPENDED));
+ erts_smp_runq_lock(rq);
molp->next = NULL;
molp->func = func;
@@ -7193,7 +7138,9 @@ erts_schedule_misc_op(void (*func)(void *), void *arg)
else
rq->misc.start = molp;
rq->misc.end = molp;
+
erts_smp_runq_unlock(rq);
+
smp_notify_inc_runq(rq);
}
@@ -7277,156 +7224,75 @@ 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)
+void
+erts_free_proc(Process *p)
{
- Sint res;
- Sint p_prev;
-
-
- erts_smp_mtx_lock(&proc_tab_mtx);
-
- if (!set) {
- res = p_next < 0 ? -1 : (p_serial << p_serial_shift | p_next);
- }
- else {
-
- p_serial = (Sint) ((next >> p_serial_shift) & p_serial_mask);
- p_next = (Sint) (erts_process_tab_index_mask & next);
-
- if (p_next >= erts_max_processes) {
- p_next = 0;
- p_serial++;
- p_serial &= p_serial_mask;
- }
-
- p_prev = p_next;
-
- do {
- if (!process_tab[p_next])
- break;
- p_next++;
- if(p_next >= erts_max_processes) {
- p_next = 0;
- p_serial++;
- p_serial &= p_serial_mask;
- }
- } while (p_prev != p_next);
-
- res = process_tab[p_next] ? -1 : (p_serial << p_serial_shift | p_next);
-
- }
+#ifdef ERTS_SMP
+ erts_proc_lock_fin(p);
+#endif
+ erts_free(ERTS_ALC_T_PROC, (void *) p);
+}
- erts_smp_mtx_unlock(&proc_tab_mtx);
+typedef struct {
+ Process *proc;
+ erts_aint32_t state;
+ ErtsRunQueue *run_queue;
+} ErtsEarlyProcInit;
- return res;
+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);
-Uint erts_process_count(void)
-{
- erts_aint32_t res = erts_smp_atomic32_read_nob(&process_count);
- ASSERT(res >= 0);
- return (Uint) res;
-}
+#ifdef ERTS_SMP
+ RUNQ_SET_RQ(&proc->run_queue, arg->run_queue);
-void
-erts_free_proc(Process *p)
-{
-#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
- erts_lcnt_proc_lock_destroy(p);
+ erts_proc_lock_init(proc); /* All locks locked */
#endif
- erts_free(ERTS_ALC_T_PROC, (void *) p);
-}
+}
/*
** Allocate process and find out where to place next process.
*/
static Process*
-alloc_process(void)
+alloc_process(ErtsRunQueue *rq, erts_aint32_t state)
{
-#ifdef ERTS_SMP
- erts_pix_lock_t *pix_lock;
-#endif
- Process* p;
- int p_prev;
-
- erts_smp_mtx_lock(&proc_tab_mtx);
-
- if (p_next == -1) {
- p = NULL;
- goto error; /* Process table full! */
- }
+ 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 error; /* ENOMEM */
+ return NULL;
- p_last = p_next;
+ init_arg.proc = (Process *) p;
+ init_arg.run_queue = rq;
+ init_arg.state = state;
- erts_get_emu_time(&p->started);
+ ASSERT(((char *) p) == ((char *) &p->common));
-#ifdef ERTS_SMP
- pix_lock = ERTS_PIX2PIXLOCK(p_next);
- erts_pix_lock(pix_lock);
-#endif
- ASSERT(!process_tab[p_next]);
-
- process_tab[p_next] = p;
- erts_smp_atomic32_inc_nob(&process_count);
- p->id = make_internal_pid(p_serial << p_serial_shift | p_next);
- if (p->id == ERTS_INVALID_PID) {
- /* Do not use the invalid pid; change serial */
- p_serial++;
- p_serial &= p_serial_mask;
- p->id = make_internal_pid(p_serial << p_serial_shift | p_next);
- ASSERT(p->id != ERTS_INVALID_PID);
+ 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;
}
- ASSERT(internal_pid_serial(p->id) <= (erts_use_r9_pids_ports
- ? ERTS_MAX_PID_R9_SERIAL
- : ERTS_MAX_PID_SERIAL));
-
-#ifdef ERTS_SMP
- erts_proc_lock_init(p); /* All locks locked */
- erts_pix_unlock(pix_lock);
-#endif
- p->rstatus = P_FREE;
+ ASSERT(internal_pid_serial(p->common.id) <= ERTS_MAX_PID_SERIAL);
+
+ p->approx_started = erts_get_approx_time();
p->rcount = 0;
- /*
- * set p_next to the next available slot
- */
- p_prev = p_next;
-
- while (1) {
- p_next++;
- if(p_next >= erts_max_processes) {
- p_serial++;
- p_serial &= p_serial_mask;
- p_next = 0;
- }
-
- if (p_prev == p_next) {
- p_next = -1;
- break; /* Table full! */
- }
-
- if (!process_tab[p_next])
- break; /* found a free slot */
- }
-
- error:
-
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ ASSERT(p == (Process *) (erts_ptab_pix2intptr_nob(
+ &erts_proc,
+ internal_pid_index(p->common.id))));
return p;
-
}
Eterm
@@ -7436,13 +7302,15 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
Eterm args, /* Arguments for function (must be well-formed list). */
ErlSpawnOpts* so) /* Options for spawn. */
{
- ErtsRunQueue *rq, *notify_runq;
+ ErtsRunQueue *rq = NULL;
Process *p;
Sint arity; /* Number of arguments. */
Uint arg_size; /* Size of arguments. */
Uint sz; /* Needed words on heap. */
Uint heap_need; /* Size needed on heap. */
Eterm res = THE_NON_VALUE;
+ erts_aint32_t state = 0;
+ erts_aint32_t prio = (erts_aint32_t) PRIORITY_NORMAL;
#ifdef ERTS_SMP
erts_smp_proc_lock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -7456,8 +7324,24 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
so->error_code = BADARG;
goto error;
}
- p = alloc_process(); /* All proc locks are locked by this thread
- on success */
+
+ if (so->flags & SPO_USE_ARGS) {
+ if (so->scheduler) {
+ int ix = so->scheduler-1;
+ ASSERT(0 <= ix && ix < erts_no_run_queues);
+ rq = ERTS_RUNQ_IX(ix);
+ state |= ERTS_PSFLG_BOUND;
+ }
+ prio = (erts_aint32_t) so->priority;
+ }
+
+ state |= (prio & ERTS_PSFLG_PRIO_MASK);
+
+ if (!rq)
+ rq = erts_get_runq_proc(parent);
+
+ p = alloc_process(rq, state); /* All proc locks are locked by this thread
+ on success */
if (!p) {
erts_send_error_to_logger_str(parent->group_leader,
"Too many processes\n");
@@ -7477,22 +7361,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->flags = erts_default_process_flags;
- /* Scheduler queue mutex should be locked when changeing
- * prio. In this case we don't have to lock it, since
- * noone except us has access to the process.
- */
if (so->flags & SPO_USE_ARGS) {
p->min_heap_size = so->min_heap_size;
p->min_vheap_size = so->min_vheap_size;
- p->prio = so->priority;
p->max_gen_gcs = so->max_gen_gcs;
} else {
p->min_heap_size = H_MIN_SIZE;
p->min_vheap_size = BIN_VH_MIN_SIZE;
- p->prio = PRIORITY_NORMAL;
p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
}
- p->skipped = 0;
+ p->schedule_count = 0;
ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
p->initial[INITIAL_MOD] = mod;
@@ -7561,21 +7439,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 =
@@ -7584,7 +7462,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;
@@ -7594,9 +7472,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->msg_inq.first = NULL;
p->msg_inq.last = &p->msg_inq.first;
p->msg_inq.len = 0;
- p->bound_runq = NULL;
#endif
- p->bif_timers = NULL;
+ p->u.bif_timers = NULL;
p->mbuf = NULL;
p->mbuf_sz = 0;
p->psd = NULL;
@@ -7608,7 +7485,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
@@ -7616,18 +7495,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);
}
}
@@ -7640,27 +7520,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);
}
}
}
@@ -7673,16 +7553,13 @@ 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;
}
#ifdef ERTS_SMP
p->scheduler_data = NULL;
- p->is_exiting = 0;
- p->status_flags = 0;
- p->runq_flags = 0;
p->suspendee = NIL;
p->pending_suspenders = NULL;
p->pending_exit.reason = THE_NON_VALUE;
@@ -7693,36 +7570,17 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->fp_exception = 0;
#endif
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+
+ res = p->common.id;
+
/*
* Schedule process for execution.
*/
- if (!((so->flags & SPO_USE_ARGS) && so->scheduler))
- rq = erts_get_runq_proc(parent);
- else {
- int ix = so->scheduler-1;
- ASSERT(0 <= ix && ix < erts_no_run_queues);
- rq = ERTS_RUNQ_IX(ix);
- p->bound_runq = rq;
- }
-
- erts_smp_runq_lock(rq);
-
-#ifdef ERTS_SMP
- p->run_queue = rq;
-#endif
-
- p->status = P_WAITING;
- notify_runq = internal_add_to_runq(rq, p);
-
- erts_smp_runq_unlock(rq);
-
- smp_notify_inc_runq(notify_runq);
-
- res = p->id;
- erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
+ 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)) {
@@ -7756,15 +7614,11 @@ void erts_init_empty_process(Process *p)
p->max_gen_gcs = 0;
p->min_heap_size = 0;
p->min_vheap_size = 0;
- p->status = P_RUNABLE;
- p->gcstatus = P_RUNABLE;
- p->rstatus = P_RUNABLE;
p->rcount = 0;
- p->id = ERTS_INVALID_PID;
- p->prio = PRIORITY_NORMAL;
+ 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;
@@ -7777,15 +7631,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->bound_runq = 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;
@@ -7794,15 +7647,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;
@@ -7829,8 +7682,8 @@ void erts_init_empty_process(Process *p)
p->def_arg_reg[5] = 0;
p->parent = NIL;
- p->started.tv_sec = 0;
- p->started.tv_usec = 0;
+ p->approx_started = 0;
+ p->common.u.alive.started_interval = 0;
#ifdef HIPE
hipe_init_process(&p->hipe);
@@ -7844,12 +7697,10 @@ void erts_init_empty_process(Process *p)
p->last_old_htop = NULL;
#endif
+ erts_smp_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL);
#ifdef ERTS_SMP
p->scheduler_data = NULL;
- p->is_exiting = 0;
- p->status_flags = 0;
- p->runq_flags = 0;
p->msg_inq.first = NULL;
p->msg_inq.last = &p->msg_inq.first;
p->msg_inq.len = 0;
@@ -7859,7 +7710,7 @@ void erts_init_empty_process(Process *p)
p->pending_exit.bp = NULL;
erts_proc_lock_init(p);
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
- p->run_queue = ERTS_RUNQ_IX(0);
+ RUNQ_SET_RQ(&p->run_queue, ERTS_RUNQ_IX(0));
#endif
#if !defined(NO_FPE_SIGNALS) || defined(HIPE)
@@ -7878,25 +7729,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);
@@ -7937,8 +7788,8 @@ erts_cleanup_empty_process(Process* p)
free_message_buffer(p->mbuf);
p->mbuf = NULL;
}
-#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
- erts_lcnt_proc_lock_destroy(p);
+#ifdef ERTS_SMP
+ erts_proc_lock_fin(p);
#endif
#ifdef DEBUG
erts_debug_verify_clean_empty_process(p);
@@ -7953,7 +7804,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 */
@@ -8027,34 +7878,40 @@ delete_process(Process* p)
mp = next_mp;
}
- ASSERT(!p->monitors);
- ASSERT(!p->nlinks);
ASSERT(!p->nodes_monitors);
ASSERT(!p->suspend_monitors);
p->fvalue = NIL;
}
+static ERTS_INLINE erts_aint32_t
+set_proc_exiting_state(Process *p, erts_aint32_t state)
+{
+ erts_aint32_t a, n, e;
+ a = state;
+ while (1) {
+ n = e = a;
+ n &= ~(ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT);
+ n |= ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE;
+ if (!(a & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLG_RUNNING)))
+ n |= ERTS_PSFLG_IN_RUNQ;
+ a = erts_smp_atomic32_cmpxchg_relb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
+ return a;
+}
+
static ERTS_INLINE void
-set_proc_exiting(Process *p, Eterm reason, ErlHeapFragment *bp)
+set_proc_exiting(Process *p,
+ erts_aint32_t state,
+ Eterm reason,
+ ErlHeapFragment *bp)
{
-#ifdef ERTS_SMP
- erts_pix_lock_t *pix_lock = ERTS_PID2PIXLOCK(p->id);
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL);
- /*
- * You are required to have all proc locks and the pix lock when going
- * to status P_EXITING. This makes it is enough to take any lock when
- * looking up a process (pid2proc()) to prevent the looked up process
- * from exiting until the lock has been released.
- */
- erts_pix_lock(pix_lock);
- p->is_exiting = 1;
-#endif
- p->status = P_EXITING;
-#ifdef ERTS_SMP
- erts_pix_unlock(pix_lock);
-#endif
+ state = set_proc_exiting_state(p, state);
+
p->fvalue = reason;
if (bp)
erts_link_mbuf_to_proc(p, bp);
@@ -8067,6 +7924,14 @@ set_proc_exiting(Process *p, Eterm reason, ErlHeapFragment *bp)
KILL_CATCHES(p);
cancel_timer(p);
p->i = (BeamInstr *) beam_exit;
+
+ if (erts_system_profile_flags.runnable_procs
+ && !(state & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED))) {
+ profile_runnable_proc(p, am_active);
+ }
+
+ if (!(state & (ERTS_PSFLG_IN_RUNQ|ERTS_PSFLG_RUNNING)))
+ add2runq(p, state);
}
@@ -8079,8 +7944,8 @@ erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
ASSERT(is_value(c_p->pending_exit.reason));
ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == locks);
ERTS_SMP_LC_ASSERT(locks & ERTS_PROC_LOCK_MAIN);
- ERTS_SMP_LC_ASSERT(c_p->status != P_EXITING);
- ERTS_SMP_LC_ASSERT(c_p->status != P_FREE);
+ ERTS_SMP_LC_ASSERT(!((ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)
+ & erts_smp_atomic32_read_nob(&c_p->state)));
/* Ensure that all locks on c_p are locked before proceeding... */
if (locks == ERTS_PROC_LOCKS_ALL)
@@ -8093,7 +7958,10 @@ erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks)
}
}
- set_proc_exiting(c_p, c_p->pending_exit.reason, c_p->pending_exit.bp);
+ set_proc_exiting(c_p,
+ erts_smp_atomic32_read_acqb(&c_p->state),
+ c_p->pending_exit.reason,
+ c_p->pending_exit.bp);
c_p->pending_exit.reason = THE_NON_VALUE;
c_p->pending_exit.bp = NULL;
@@ -8104,16 +7972,19 @@ 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)
- && !(p->status_flags & ERTS_PROC_SFLG_RUNNING)) {
- ASSERT(p->status_flags & ERTS_PROC_SFLG_INRUNQ);
- ASSERT(ERTS_PROC_PENDING_EXIT(p));
- erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
+ 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);
+ erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL);
+ }
}
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL);
}
@@ -8137,11 +8008,10 @@ 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);
}
#endif
@@ -8185,7 +8055,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
@@ -8203,7 +8073,7 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
* SMP emulator). When the signal is received the receiver receives an
* 'EXIT' message if it is trapping exits; otherwise, it will either
* ignore the signal if the exit reason is normal, or go into an
- * exiting state (status P_EXITING). When a process has gone into the
+ * exiting state (ERTS_PSFLG_EXITING). When a process has gone into the
* exiting state it will not execute any more Erlang code, but it might
* take a while before it actually exits. The exit signal is being
* received when the 'EXIT' message is put in the message queue, the
@@ -8276,6 +8146,7 @@ send_exit_signal(Process *c_p, /* current process if and only
Uint32 flags /* flags */
)
{
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
Eterm rsn = reason == am_kill ? am_killed : reason;
ERTS_SMP_LC_ASSERT(*rp_locks == erts_proc_lc_my_proc_locks(rp));
@@ -8297,7 +8168,7 @@ send_exit_signal(Process *c_p, /* current process if and only
}
#endif
- if (ERTS_PROC_IS_TRAPPING_EXITS(rp)
+ if ((state & ERTS_PSFLG_TRAP_EXIT)
&& (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
if (is_not_nil(token)
#ifdef USE_VM_PROBES
@@ -8313,9 +8184,7 @@ send_exit_signal(Process *c_p, /* current process if and only
}
else if (reason != am_normal || (flags & ERTS_XSIG_FLG_NO_IGN_NORMAL)) {
#ifdef ERTS_SMP
- if (!ERTS_PROC_PENDING_EXIT(rp) && !rp->is_exiting) {
- ASSERT(rp->status != P_EXITING);
- ASSERT(rp->status != P_FREE);
+ if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))) {
ASSERT(!rp->pending_exit.bp);
if (rp == c_p && (*rp_locks & ERTS_PROC_LOCK_MAIN)) {
@@ -8331,9 +8200,9 @@ send_exit_signal(Process *c_p, /* current process if and only
}
*rp_locks = ERTS_PROC_LOCKS_ALL;
}
- set_proc_exiting(c_p, rsn, NULL);
+ set_proc_exiting(c_p, state, rsn, NULL);
}
- else if (!(rp->status_flags & ERTS_PROC_SFLG_RUNNING)) {
+ else if (!(state & ERTS_PSFLG_RUNNING)) {
/* Process not running ... */
ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL;
if (need_locks
@@ -8350,6 +8219,7 @@ send_exit_signal(Process *c_p, /* current process if and only
/* ...and we have all locks on it... */
*rp_locks = ERTS_PROC_LOCKS_ALL;
set_proc_exiting(rp,
+ state,
(is_immed(rsn)
? rsn
: copy_object(rsn, rp)),
@@ -8379,11 +8249,9 @@ send_exit_signal(Process *c_p, /* current process if and only
&bp->off_heap);
rp->pending_exit.bp = bp;
}
- ASSERT(ERTS_PROC_PENDING_EXIT(rp));
+ erts_smp_atomic32_read_bor_relb(&rp->state,
+ ERTS_PSFLG_PENDING_EXIT);
}
- if (!(rp->status_flags
- & (ERTS_PROC_SFLG_INRUNQ|ERTS_PROC_SFLG_RUNNING)))
- erts_add_to_runq(rp);
}
/* else:
*
@@ -8395,17 +8263,14 @@ send_exit_signal(Process *c_p, /* current process if and only
* exit or by itself before seeing the pending exit.
*/
#else /* !ERTS_SMP */
- if (c_p == rp) {
- rp->status = P_EXITING;
- c_p->fvalue = rsn;
- }
- else if (rp->status != P_EXITING) { /* No recursive process exits /PaN */
- Eterm old_status = rp->status;
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (!(state & ERTS_PSFLG_EXITING)) {
set_proc_exiting(rp,
- is_immed(rsn) ? rsn : copy_object(rsn, rp),
+ state,
+ (is_immed(rsn) || c_p == rp
+ ? rsn
+ : copy_object(rsn, rp)),
NULL);
- if (old_status != P_RUNABLE && old_status != P_RUNNING)
- erts_add_to_runq(rp);
}
#endif
return -1; /* Receiver will exit */
@@ -8481,7 +8346,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;
@@ -8516,7 +8381,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;
}
@@ -8532,13 +8397,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);
}
@@ -8603,21 +8468,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... */
@@ -8627,14 +8493,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,
@@ -8646,7 +8512,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);
}
}
}
@@ -8660,12 +8526,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);
@@ -8680,7 +8546,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);
@@ -8708,22 +8574,14 @@ resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p)
erts_destroy_suspend_monitor(smon);
}
-static void
-continue_exit_process(Process *p
-#ifdef ERTS_SMP
- , erts_pix_lock_t *pix_lock
-#endif
- );
-
/* this function fishishes a process and propagates exit messages - called
by process_main when a process dies */
void
erts_do_exit_process(Process* p, Eterm reason)
{
#ifdef ERTS_SMP
- erts_pix_lock_t *pix_lock = ERTS_PID2PIXLOCK(p->id);
+ erts_aint32_t state;
#endif
-
p->arity = 0; /* No live registers */
p->fvalue = reason;
@@ -8741,27 +8599,17 @@ erts_do_exit_process(Process* p, Eterm reason)
#ifdef ERTS_SMP
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
/* By locking all locks (main lock is already locked) when going
- to status P_EXITING, it is enough to take any lock when
+ to exiting state (ERTS_PSFLG_EXITING), it is enough to take any lock when
looking up a process (erts_pid2proc()) to prevent the looked up
process from exiting until the lock has been released. */
erts_smp_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
#endif
-
- if (erts_system_profile_flags.runnable_procs && (p->status != P_WAITING)) {
- profile_runnable_proc(p, am_inactive);
- }
-
-#ifdef ERTS_SMP
- erts_pix_lock(pix_lock);
- p->is_exiting = 1;
-#endif
-
- p->status = P_EXITING;
-
-#ifdef ERTS_SMP
- erts_pix_unlock(pix_lock);
- if (ERTS_PROC_PENDING_EXIT(p)) {
+#ifndef ERTS_SMP
+ set_proc_exiting_state(p, erts_smp_atomic32_read_nob(&p->state));
+#else
+ state = set_proc_exiting_state(p, erts_smp_atomic32_read_nob(&p->state));
+ if (state & ERTS_PSFLG_PENDING_EXIT) {
/* Process exited before pending exit was received... */
p->pending_exit.reason = THE_NON_VALUE;
if (p->pending_exit.bp) {
@@ -8783,46 +8631,30 @@ 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);
-#ifdef ERTS_SMP
- continue_exit_process(p, pix_lock);
-#else
- continue_exit_process(p);
-#endif
-}
+ /*
+ * The p->u.bif_timers of this process can *not* be used anymore;
+ * will be overwritten by misc termination data.
+ */
+ p->u.terminate = NULL;
-void
-erts_continue_exit_process(Process *c_p)
-{
-#ifdef ERTS_SMP
- continue_exit_process(c_p, ERTS_PID2PIXLOCK(c_p->id));
-#else
- continue_exit_process(c_p);
-#endif
+
+ erts_continue_exit_process(p);
}
-static void
-continue_exit_process(Process *p
-#ifdef ERTS_SMP
- , erts_pix_lock_t *pix_lock
-#endif
- )
+void
+erts_continue_exit_process(Process *p)
{
ErtsLink* lnk;
ErtsMonitor *mon;
@@ -8838,11 +8670,7 @@ continue_exit_process(Process *p
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(p));
-#ifdef DEBUG
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- ASSERT(p->status == P_EXITING);
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
-#endif
+ ASSERT(ERTS_PROC_IS_EXITING(p));
#ifdef ERTS_SMP
if (p->flags & F_HAVE_BLCKD_MSCHED) {
@@ -8893,9 +8721,9 @@ 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);
@@ -8909,49 +8737,33 @@ 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 pix;
/* Do *not* use erts_get_runq_proc() */
ErtsRunQueue *rq;
rq = erts_get_runq_current(ERTS_GET_SCHEDULER_DATA_FROM_PROC(p));
- ASSERT(internal_pid_index(p->id) < erts_max_processes);
- pix = internal_pid_index(p->id);
-
- erts_smp_mtx_lock(&proc_tab_mtx);
erts_smp_runq_lock(rq);
#ifdef ERTS_SMP
- erts_pix_lock(pix_lock);
-
ASSERT(p->scheduler_data);
ASSERT(p->scheduler_data->current_process == p);
ASSERT(p->scheduler_data->free_process == NULL);
p->scheduler_data->current_process = NULL;
p->scheduler_data->free_process = p;
- p->status_flags = 0;
#endif
- process_tab[pix] = NULL; /* Time of death! */
- ASSERT(erts_smp_atomic32_read_nob(&process_count) > 0);
- erts_smp_atomic32_dec_nob(&process_count);
-#ifdef ERTS_SMP
- erts_pix_unlock(pix_lock);
-#endif
- erts_smp_runq_unlock(rq);
+ /* Time of death! */
+ erts_ptab_delete_element(&erts_proc, &p->common);
- if (p_next < 0) {
- if (p_last >= p_next) {
- p_serial++;
- p_serial &= p_serial_mask;
- }
- p_next = pix;
- }
-
- ERTS_MAYBE_SAVE_TERMINATING_PROCESS(p);
-
- erts_smp_mtx_unlock(&proc_tab_mtx);
+ erts_smp_runq_unlock(rq);
}
/*
@@ -8961,12 +8773,20 @@ continue_exit_process(Process *p
* when the monitors and/or links hit.
*/
- mon = p->monitors;
- p->monitors = NULL; /* to avoid recursive deletion during traversal */
+ {
+ /* Inactivate and notify free */
+ erts_aint32_t n, e, a = erts_smp_atomic32_read_nob(&p->state);
+ while (1) {
+ n = e = a;
+ ASSERT(a & ERTS_PSFLG_EXITING);
+ n |= ERTS_PSFLG_FREE;
+ n &= ~ERTS_PSFLG_ACTIVE;
+ a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e);
+ if (a == e)
+ break;
+ }
+ }
- lnk = p->nlinks;
- p->nlinks = NULL;
- p->status = P_FREE;
dep = ((p->flags & F_DISTRIBUTION)
? ERTS_PROC_SET_DIST_ENTRY(p, ERTS_PROC_LOCKS_ALL, NULL)
: NULL);
@@ -8996,7 +8816,7 @@ 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);
@@ -9021,8 +8841,10 @@ continue_exit_process(Process *p
delete_process(p);
+#ifdef ERTS_SMP
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+#endif
return;
@@ -9035,8 +8857,6 @@ continue_exit_process(Process *p
ERTS_SMP_LC_ASSERT(curr_locks == erts_proc_lc_my_proc_locks(p));
ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN & curr_locks);
- ASSERT(p->status == P_EXITING);
-
p->i = (BeamInstr *) beam_continue_exit;
if (!(curr_locks & ERTS_PROC_LOCK_STATUS)) {
@@ -9044,8 +8864,6 @@ continue_exit_process(Process *p
curr_locks |= ERTS_PROC_LOCK_STATUS;
}
- erts_add_to_runq(p);
-
if (curr_locks != ERTS_PROC_LOCK_MAIN)
erts_smp_proc_unlock(p, ~ERTS_PROC_LOCK_MAIN & curr_locks);
@@ -9057,33 +8875,15 @@ continue_exit_process(Process *p
static void
timeout_proc(Process* p)
{
+ erts_aint32_t state;
BeamInstr** pi = (BeamInstr **) p->def_arg_reg;
p->i = *pi;
p->flags |= F_TIMO;
p->flags &= ~F_INSLPQUEUE;
- switch (p->status) {
- case P_GARBING:
- switch (p->gcstatus) {
- case P_SUSPENDED:
- goto suspended;
- case P_WAITING:
- goto waiting;
- default:
- break;
- }
- break;
- case P_WAITING:
- waiting:
- erts_add_to_runq(p);
- break;
- case P_SUSPENDED:
- suspended:
- p->rstatus = P_RUNABLE; /* MUST set resume status to runnable */
- break;
- default:
- break;
- }
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & ERTS_PSFLG_ACTIVE))
+ schedule_process(p, state, 0);
}
@@ -9093,9 +8893,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
}
@@ -9116,12 +8916,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,
@@ -9139,7 +8939,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);
@@ -9151,6 +8951,7 @@ erts_stack_dump(int to, void *to_arg, Process *p)
void
erts_program_counter_info(int to, void *to_arg, Process *p)
{
+ erts_aint32_t state;
int i;
erts_print(to, to_arg, "Program counter: %p (", p->i);
@@ -9159,7 +8960,8 @@ erts_program_counter_info(int to, void *to_arg, Process *p)
erts_print(to, to_arg, "CP: %p (", p->cp);
print_function_from_pc(to, to_arg, p->cp);
erts_print(to, to_arg, ")\n");
- if (!((p->status == P_RUNNING) || (p->status == P_GARBING))) {
+ state = erts_smp_atomic32_read_acqb(&p->state);
+ if (!(state & (ERTS_PSFLG_RUNNING|ERTS_PSFLG_GC))) {
erts_print(to, to_arg, "arity = %d\n",p->arity);
if (!ERTS_IS_CRASH_DUMPING) {
/*
@@ -9205,7 +9007,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++;
}
@@ -9225,1085 +9027,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, TVP) \
- debug_processes_check_found_pid((PBDP), (PID), (TVP), 1)
-# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, TVP) \
- debug_processes_check_found_pid((PBDP), (PID), (TVP), 0)
-#else
-# define ERTS_PROCS_DBG_CHK_PID_FOUND(PBDP, PID, TVP)
-# define ERTS_PROCS_DBG_CHK_PID_NOT_FOUND(PBDP, PID, TVP)
-#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 {
- SysTimeval time;
-} 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
- SysTimeval *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,
- SysTimeval *started,
- int pid_should_be_found);
-#endif
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
-static SysTimeval debug_tv_start;
-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_mtx_is_locked(&proc_tab_mtx));
-
- 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;
- erts_get_emu_time(&tpep->u.process.exited);
-
- saved_term_procs.end->next = tpep;
- saved_term_procs.end = tpep;
-
- ERTS_PROCS_DBG_CHK_TPLIST();
-
- ERTS_PROCS_ASSERT((tpep->prev->ix >= 0
- ? erts_cmp_timeval(&tpep->u.process.exited,
- &tpep->prev->u.process.exited)
- : erts_cmp_timeval(&tpep->u.process.exited,
- &tpep->prev->u.bif_invocation.time)) > 0);
-}
-
-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_mtx_lock(&proc_tab_mtx);
-
- 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_mtx_unlock(&proc_tab_mtx);
-
- }
- }
-
- 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_mtx_lock(&proc_tab_mtx);
- 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(SysTimeval)*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;
- erts_get_emu_time(&pbdp->bif_invocation->u.bif_invocation.time);
- 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;
- SysTimeval *invocation_timep;
-
- invocation_timep = (pbdp->bif_invocation
- ? &pbdp->bif_invocation->u.bif_invocation.time
- : NULL);
-
- ERTS_PROCS_ASSERT(is_nil(*res_accp));
- if (!locked) {
- erts_smp_mtx_lock(&proc_tab_mtx);
- locked = 1;
- }
-
- ERTS_SMP_LC_ASSERT(erts_lc_mtx_is_locked(&proc_tab_mtx));
- ERTS_PROCS_DBG_TRACE(p->id, processes_bif_engine, insp_table);
-
- if (cix != 0)
- erts_get_emu_time(&pbdp->chunk[cix].time);
- else if (pbdp->bif_invocation)
- pbdp->chunk[0].time = *invocation_timep;
- /* else: Time is irrelevant */
-
- if (end_ix >= erts_max_processes) {
- ERTS_PROCS_ASSERT(cix+1 == processes_bif_tab_chunks);
- end_ix = erts_max_processes;
- 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 = process_tab[ix];
- if (rp
- && (!invocation_timep
- || erts_cmp_timeval(&rp->started,
- invocation_timep) < 0)) {
- 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;
-#endif
-
- pbdp->pid_ix++;
- ERTS_PROCS_ASSERT(pbdp->pid_ix <= pbdp->pid_sz);
- }
- }
-
- pbdp->tix = end_ix;
-
- erts_smp_mtx_unlock(&proc_tab_mtx);
- 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_max_processes) {
- end_ix = erts_max_processes;
- 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;
- SysTimeval *invocation_timep;
- ErtsTermProcElement *tpep;
- ErtsTermProcElement *free_list = NULL;
-
- tpep = pbdp->bif_invocation;
- ERTS_PROCS_ASSERT(tpep);
- invocation_timep = &tpep->u.bif_invocation.time;
-
- 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_mtx_lock(&proc_tab_mtx);
- 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;
- SysTimeval *chunk_timep = &pbdp->chunk[cix].time;
- Eterm pid = tpep->u.process.pid;
- ERTS_PROCS_ASSERT(is_internal_pid(pid));
-
- if (erts_cmp_timeval(&tpep->u.process.spawned,
- invocation_timep) < 0) {
- if (erts_cmp_timeval(&tpep->u.process.exited,
- chunk_timep) < 0) {
- 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_mtx_unlock(&proc_tab_mtx);
-
- /*
- * 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(SysTimeval)*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_max_processes - 1)
- / ERTS_PROCESSES_BIF_TAB_CHUNK_SIZE)
- + 1);
-
- /* processes_trap/2 is a hidden BIF that the processes/0 BIF traps to. */
- sys_memset((void *) &processes_trap_export, 0, sizeof(Export));
- processes_trap_export.address = &processes_trap_export.code[3];
- processes_trap_export.code[0] = am_erlang;
- processes_trap_export.code[1] = am_processes_trap;
- processes_trap_export.code[2] = 2;
- processes_trap_export.code[3] = (BeamInstr) em_apply_bif;
- processes_trap_export.code[4] = (BeamInstr) &processes_trap;
-
-#if ERTS_PROCESSES_BIF_DEBUGLEVEL >= ERTS_PROCS_DBGLVL_CHK_TERM_PROC_LIST
- erts_get_emu_time(&debug_tv_start);
-#endif
-
-}
-
-/*
- * 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;
-#ifdef DEBUG
- Eterm *hp_end;
-#endif
-
- erts_smp_mtx_lock(&proc_tab_mtx);
-
- res = NIL;
- need = erts_process_count() * 2;
- hp = HAlloc(c_p, need); /* we need two heap words for each pid */
-#ifdef DEBUG
- hp_end = hp + need;
-#endif
-
- /* make the list by scanning bakward */
-
-
- for (i = erts_max_processes-1; i >= 0; i--) {
- if ((p = process_tab[i]) != NULL) {
- res = CONS(hp, process_tab[i]->id, res);
- hp += 2;
- }
- }
- ASSERT(hp == hp_end);
-
- erts_smp_mtx_unlock(&proc_tab_mtx);
-
- 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,
- SysTimeval *tvp,
- 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].tv_sec == tvp->tv_sec
- && pbdp->debug.pid_started[i].tv_usec == tvp->tv_usec) {
- 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_max_processes; tix++) {
- Process *rp = process_tab[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_mtx_is_locked(&proc_tab_mtx));
- if (!saved_term_procs.start)
- ERTS_PROCS_ASSERT(!saved_term_procs.end);
- else {
- SysTimeval tv_now;
- SysTimeval *prev_xtvp = NULL;
- ErtsTermProcElement *tpep;
- erts_get_emu_time(&tv_now);
-
- 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) {
- SysTimeval *tvp = &tpep->u.bif_invocation.time;
- ERTS_PROCS_ASSERT(erts_cmp_timeval(&debug_tv_start, tvp) < 0
- && erts_cmp_timeval(tvp, &tv_now) < 0);
- }
- else {
- SysTimeval *stvp = &tpep->u.process.spawned;
- SysTimeval *xtvp = &tpep->u.process.exited;
-
- ERTS_PROCS_ASSERT(erts_cmp_timeval(&debug_tv_start,
- stvp) < 0);
- ERTS_PROCS_ASSERT(erts_cmp_timeval(stvp, xtvp) < 0);
- if (prev_xtvp)
- ERTS_PROCS_ASSERT(erts_cmp_timeval(prev_xtvp, xtvp) < 0);
- prev_xtvp = xtvp;
- 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
@@ -10330,3 +9053,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 7c481a91dd..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))
@@ -112,28 +114,29 @@ extern int erts_sched_thread_suggested_stack_size;
#define PRIORITY_NORMAL 2
#define PRIORITY_LOW 3
#define ERTS_NO_PROC_PRIO_LEVELS 4
+#define ERTS_NO_PROC_PRIO_QUEUES 3
#define ERTS_PORT_PRIO_LEVEL ERTS_NO_PROC_PRIO_LEVELS
+#define ERTS_NO_PRIO_LEVELS (ERTS_NO_PROC_PRIO_LEVELS + 1)
#define ERTS_RUNQ_FLGS_PROCS_QMASK \
((((Uint32) 1) << ERTS_NO_PROC_PRIO_LEVELS) - 1)
-#define ERTS_NO_PRIO_LEVELS (ERTS_NO_PROC_PRIO_LEVELS + 1)
-#define ERTS_RUNQ_FLGS_MIGRATE_QMASK \
+#define ERTS_RUNQ_FLGS_QMASK \
((((Uint32) 1) << ERTS_NO_PRIO_LEVELS) - 1)
#define ERTS_RUNQ_FLGS_EMIGRATE_SHFT \
- ERTS_NO_PROC_PRIO_LEVELS
+ ERTS_NO_PRIO_LEVELS
#define ERTS_RUNQ_FLGS_IMMIGRATE_SHFT \
(ERTS_RUNQ_FLGS_EMIGRATE_SHFT + ERTS_NO_PRIO_LEVELS)
#define ERTS_RUNQ_FLGS_EVACUATE_SHFT \
(ERTS_RUNQ_FLGS_IMMIGRATE_SHFT + ERTS_NO_PRIO_LEVELS)
#define ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
- (ERTS_RUNQ_FLGS_MIGRATE_QMASK << ERTS_RUNQ_FLGS_EMIGRATE_SHFT)
+ (ERTS_RUNQ_FLGS_QMASK << ERTS_RUNQ_FLGS_EMIGRATE_SHFT)
#define ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
- (ERTS_RUNQ_FLGS_MIGRATE_QMASK << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)
+ (ERTS_RUNQ_FLGS_QMASK << ERTS_RUNQ_FLGS_IMMIGRATE_SHFT)
#define ERTS_RUNQ_FLGS_EVACUATE_QMASK \
- (ERTS_RUNQ_FLGS_MIGRATE_QMASK << ERTS_RUNQ_FLGS_EVACUATE_SHFT)
+ (ERTS_RUNQ_FLGS_QMASK << ERTS_RUNQ_FLGS_EVACUATE_SHFT)
#define ERTS_RUNQ_FLG_BASE2 \
(ERTS_RUNQ_FLGS_EVACUATE_SHFT + ERTS_NO_PRIO_LEVELS)
@@ -148,14 +151,18 @@ extern int erts_sched_thread_suggested_stack_size;
(((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 3))
#define ERTS_RUNQ_FLG_INACTIVE \
(((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 4))
+#define ERTS_RUNQ_FLG_NONEMPTY \
+ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 5))
+#define ERTS_RUNQ_FLG_PROTECTED \
+ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 6))
#define ERTS_RUNQ_FLGS_MIGRATION_QMASKS \
(ERTS_RUNQ_FLGS_EMIGRATE_QMASK \
| ERTS_RUNQ_FLGS_IMMIGRATE_QMASK \
| ERTS_RUNQ_FLGS_EVACUATE_QMASK)
+
#define ERTS_RUNQ_FLGS_MIGRATION_INFO \
- (ERTS_RUNQ_FLGS_MIGRATION_QMASKS \
- | ERTS_RUNQ_FLG_INACTIVE \
+ (ERTS_RUNQ_FLG_INACTIVE \
| ERTS_RUNQ_FLG_OUT_OF_WORK \
| ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK)
@@ -186,34 +193,24 @@ extern int erts_sched_thread_suggested_stack_size;
#define ERTS_UNSET_RUNQ_FLG_EVACUATE(FLGS, PRIO) \
((FLGS) &= ~ERTS_RUNQ_FLG_EVACUATE((PRIO)))
-#define ERTS_RUNQ_IFLG_SUSPENDED (((erts_aint32_t) 1) << 0)
-#define ERTS_RUNQ_IFLG_NONEMPTY (((erts_aint32_t) 1) << 1)
-
-
-#ifdef DEBUG
-# if defined(ARCH_64) && !HALFWORD_HEAP
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (*((char **) &(RQP)) = (char *) (0xdeadbeefdead0003 | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((Uint) (RQP)) & ((Uint) 0x3))) == ((Uint) 0)); \
- ASSERT((((Uint) (RQP)) & ~((Uint) 0xffff)) != ((Uint) 0xdeadbeefdead0000));\
-} while (0)
-# else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N) \
- (*((char **) &(RQP)) = (char *) (0xdead0003 | ((N) << 4)))
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP) \
-do { \
- ASSERT((RQP) != NULL); \
- ASSERT(((((UWord) (RQP)) & ((UWord) 1))) == ((UWord) 0)); \
- ASSERT((((UWord) (RQP)) & ~((UWord) 0xffff)) != ((UWord) 0xdead0000)); \
-} while (0)
-# endif
-#else
-# define ERTS_DBG_SET_INVALID_RUNQP(RQP, N)
-# define ERTS_DBG_VERIFY_VALID_RUNQP(RQP)
-#endif
+#define ERTS_RUNQ_FLGS_INIT(RQ, INIT) \
+ erts_smp_atomic32_init_nob(&(RQ)->flags, (erts_aint32_t) (INIT))
+#define ERTS_RUNQ_FLGS_SET(RQ, FLGS) \
+ ((Uint32) erts_smp_atomic32_read_bor_relb(&(RQ)->flags, \
+ (erts_aint32_t) (FLGS)))
+#define ERTS_RUNQ_FLGS_UNSET(RQ, FLGS) \
+ ((Uint32) erts_smp_atomic32_read_band_relb(&(RQ)->flags, \
+ (erts_aint32_t) ~(FLGS)))
+#define ERTS_RUNQ_FLGS_GET(RQ) \
+ ((Uint32) erts_smp_atomic32_read_acqb(&(RQ)->flags))
+#define ERTS_RUNQ_FLGS_GET_NOB(RQ) \
+ ((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_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,
@@ -258,14 +255,15 @@ typedef enum {
#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_ASYNC_READY (((erts_aint32_t) 1) << 5)
-#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 6)
-#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 7)
-#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 8)
-#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 9)
-#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 10)
-#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 11)
-#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 12)
+#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;
@@ -291,8 +289,9 @@ struct ErtsSchedulerSleepInfo_ {
typedef struct ErtsProcList_ ErtsProcList;
struct ErtsProcList_ {
Eterm pid;
- SysTimeval started;
+ Uint64 started_interval;
ErtsProcList* next;
+ ErtsProcList* prev;
};
typedef struct ErtsMiscOpList_ ErtsMiscOpList;
@@ -312,21 +311,39 @@ typedef struct ErtsSchedulerData_ ErtsSchedulerData;
typedef struct ErtsRunQueue_ ErtsRunQueue;
typedef struct {
- int len;
- int max_len;
+ erts_smp_atomic32_t len;
+ erts_aint32_t max_len;
int reds;
+} ErtsRunQueueInfo;
+
+#ifdef ERTS_SMP
+
+typedef struct {
+ Uint32 flags;
+ ErtsRunQueue *misc_evac_runq;
struct {
struct {
int this;
int other;
} limit;
ErtsRunQueue *runq;
- } migrate;
-} ErtsRunQueueInfo;
+ Uint32 flags;
+ } prio[ERTS_NO_PRIO_LEVELS];
+} ErtsMigrationPath;
+
+typedef struct ErtsMigrationPaths_ ErtsMigrationPaths;
+
+struct ErtsMigrationPaths_ {
+ void *block;
+ ErtsMigrationPaths *next;
+ ErtsThrPrgrVal thr_prgr;
+ ErtsMigrationPath mpath[1];
+};
+
+#endif /* ERTS_SMP */
struct ErtsRunQueue_ {
int ix;
- erts_smp_atomic32_t info_flags;
erts_smp_mtx_t mtx;
erts_smp_cnd_t cnd;
@@ -334,19 +351,18 @@ struct ErtsRunQueue_ {
ErtsSchedulerData *scheduler;
int waiting; /* < 0 in sys schedule; > 0 on cnd variable */
int woken;
- Uint32 flags;
+ erts_smp_atomic32_t flags;
int check_balance_reds;
int full_reds_history_sum;
int full_reds_history[ERTS_FULL_REDS_HISTORY_SIZE];
int out_of_work_count;
- int max_len;
- int len;
+ erts_aint32_t max_len;
+ erts_aint32_t len;
int wakeup_other;
int wakeup_other_reds;
int halt_in_progress;
struct {
- int len;
ErtsProcList *pending_exiters;
Uint context_switches;
Uint reductions;
@@ -361,13 +377,13 @@ struct ErtsRunQueue_ {
struct {
ErtsMiscOpList *start;
ErtsMiscOpList *end;
- ErtsRunQueue *evac_runq;
+ erts_smp_atomic_t evac_runq;
} misc;
struct {
ErtsRunQueueInfo info;
- struct port *start;
- struct port *end;
+ Port *start;
+ Port *end;
} ports;
};
@@ -381,7 +397,7 @@ extern ErtsAlignedRunQueue *erts_aligned_run_queues;
#define ERTS_PROC_REDUCTIONS_EXECUTED(RQ, PRIO, REDS, AREDS) \
do { \
(RQ)->procs.reductions += (AREDS); \
- (RQ)->procs.prio_info[p->prio].reds += (REDS); \
+ (RQ)->procs.prio_info[(PRIO)].reds += (REDS); \
(RQ)->check_balance_reds -= (REDS); \
(RQ)->wakeup_other_reds += (AREDS); \
} while (0)
@@ -427,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 {
@@ -473,7 +493,7 @@ 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 */
@@ -502,6 +522,90 @@ extern ErtsAlignedSchedulerData *erts_aligned_scheduler_data;
extern ErtsSchedulerData *erts_scheduler_data;
#endif
+#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
+int erts_smp_lc_runq_is_locked(ErtsRunQueue *);
+#endif
+
+#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
+
+/*
+ * Run queue locked during modifications. We use atomic ops since
+ * other threads peek at values without run queue lock.
+ */
+
+ERTS_GLB_INLINE void erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio);
+ERTS_GLB_INLINE void erts_smp_dec_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio);
+ERTS_GLB_INLINE void erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_smp_inc_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio)
+{
+ erts_aint32_t len;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+
+ len = erts_smp_atomic32_read_nob(&rqi->len);
+ ASSERT(len >= 0);
+ if (len == 0) {
+ ASSERT((erts_smp_atomic32_read_nob(&rq->flags)
+ & ((erts_aint32_t) (1 << prio))) == 0);
+ erts_smp_atomic32_read_bor_nob(&rq->flags,
+ (erts_aint32_t) (1 << prio));
+ }
+ len++;
+ if (rqi->max_len < len)
+ rqi->max_len = len;
+
+ erts_smp_atomic32_set_relb(&rqi->len, len);
+
+ rq->len++;
+ if (rq->max_len < rq->len)
+ rq->max_len = len;
+ ASSERT(rq->len > 0);
+}
+
+ERTS_GLB_INLINE void
+erts_smp_dec_runq_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi, int prio)
+{
+ erts_aint32_t len;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+
+ len = erts_smp_atomic32_read_nob(&rqi->len);
+ len--;
+ ASSERT(len >= 0);
+ if (len == 0) {
+ ASSERT((erts_smp_atomic32_read_nob(&rq->flags)
+ & ((erts_aint32_t) (1 << prio))));
+ erts_smp_atomic32_read_band_nob(&rq->flags,
+ ~((erts_aint32_t) (1 << prio)));
+ }
+ erts_smp_atomic32_set_relb(&rqi->len, len);
+
+ rq->len--;
+ ASSERT(rq->len >= 0);
+}
+
+ERTS_GLB_INLINE void
+erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
+{
+ erts_aint32_t len;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq));
+
+ len = erts_smp_atomic32_read_nob(&rqi->len);
+ ASSERT(rqi->max_len >= len);
+ rqi->max_len = len;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#define RUNQ_READ_LEN(X) erts_smp_atomic32_read_nob((X))
+
+#endif /* ERTS_INCLUDE_SCHEDULER_INTERNALS */
+
/*
* Process Specific Data.
*
@@ -614,6 +718,8 @@ struct ErtsPendingSuspend_ {
# define BIN_OLD_VHEAP(p) (p)->bin_old_vheap
struct process {
+ 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
@@ -656,17 +762,9 @@ struct process {
* Number of reductions left to execute.
* Only valid for the current process.
*/
- Uint32 status; /* process STATE */
- Uint32 gcstatus; /* process gc STATE */
- Uint32 rstatus; /* process resume STATE */
Uint32 rcount; /* suspend count */
- Eterm id; /* The pid of this process */
- int prio; /* Priority of process */
- int skipped; /* Times a low prio process has been rescheduled */
+ 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) */
@@ -675,11 +773,6 @@ struct process {
Eterm ftrace; /* Latest exception stack trace dump */
Process *next; /* Pointer to next process in run queue */
- Process *prev; /* Pointer to prev 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;
@@ -689,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 */
@@ -713,8 +809,7 @@ struct process {
* Information mainly for post-mortem use (erl crash dump).
*/
Eterm parent; /* Pid of process that created this process. */
- SysTimeval started; /* Time when started. */
-
+ erts_approx_time_t approx_started; /* Time when started. */
/* This is the place, where all fields that differs between memory
* architectures, have gone to.
@@ -736,28 +831,16 @@ 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;
-
- ErtsRunQueue *bound_runq;
+ 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;
- int is_exiting;
- Uint32 runq_flags;
- Uint32 status_flags;
- ErlMessageInQueue msg_inq;
Eterm suspendee;
ErtsPendingSuspend *pending_suspenders;
- ErtsPendExit pending_exit;
- ErtsRunQueue *run_queue;
+ erts_smp_atomic_t run_queue;
#ifdef HIPE
struct hipe_process_state_smp hipe_smp;
#endif
@@ -782,6 +865,8 @@ struct process {
#endif
};
+extern const Process erts_invalid_process;
+
#ifdef CHECK_FOR_HOLES
# define INIT_HOLE_CHECK(p) \
do { \
@@ -811,6 +896,29 @@ void erts_check_for_holes(Process* p);
#define SEQ_TRACE_TOKEN(p) ((p)->seq_trace_token)
+#if ERTS_NO_PROC_PRIO_LEVELS > 4
+# error "Need to increase ERTS_PSFLG_PRIO_SHIFT"
+#endif
+
+#define ERTS_PSFLG_PRIO_SHIFT 2
+
+#define ERTS_PSFLG_BIT(N) \
+ (((erts_aint32_t) 1) << (ERTS_PSFLG_PRIO_SHIFT + (N)))
+
+#define ERTS_PSFLG_PRIO_MASK (ERTS_PSFLG_BIT(0) - 1)
+
+#define ERTS_PSFLG_FREE ERTS_PSFLG_BIT(0)
+#define ERTS_PSFLG_EXITING ERTS_PSFLG_BIT(1)
+#define ERTS_PSFLG_PENDING_EXIT ERTS_PSFLG_BIT(2)
+#define ERTS_PSFLG_ACTIVE ERTS_PSFLG_BIT(3)
+#define ERTS_PSFLG_IN_RUNQ ERTS_PSFLG_BIT(4)
+#define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(5)
+#define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(6)
+#define ERTS_PSFLG_GC ERTS_PSFLG_BIT(7)
+#define ERTS_PSFLG_BOUND ERTS_PSFLG_BIT(8)
+#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(9)
+
+
/* The sequential tracing token is a tuple of size 5:
*
* {Flags, Label, Serial, Sender}
@@ -883,9 +991,6 @@ Eterm* erts_heap_alloc(Process* p, Uint need, Uint xtra);
Eterm* erts_set_hole_marker(Eterm* ptr, Uint sz);
#endif
-extern Process** process_tab;
-extern Uint erts_max_processes;
-extern Uint erts_process_tab_index_mask;
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),
@@ -914,16 +1019,8 @@ struct erts_system_profile_flags_t {
};
extern struct erts_system_profile_flags_t erts_system_profile_flags;
-#define INVALID_PID(p, pid) ((p) == NULL \
- || (p)->id != (pid) \
- || (p)->status == P_EXITING)
-
-#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_TRAPEXIT (1 << 0)
+#define F_HIBERNATE_SCHED (1 << 0) /* Schedule out after hibernate op */
#define F_INSLPQUEUE (1 << 1) /* Set if in timer queue */
#define F_TIMO (1 << 2) /* Set if timeout */
#define F_HEAP_GROW (1 << 3)
@@ -934,7 +1031,6 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
#define F_HAVE_BLCKD_MSCHED (1 << 8) /* Process has blocked multi-scheduling */
#define F_P2PNR_RESCHED (1 << 9) /* Process has been rescheduled via erts_pid2proc_not_running() */
#define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */
-#define F_HIBERNATE_SCHED (1 << 11) /* Schedule out after hibernate op */
/* process trace_flags */
#define F_SENSITIVE (1 << 0)
@@ -1001,67 +1097,10 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags)
#endif
-
-#ifdef ERTS_SMP
-/* Status flags ... */
-#define ERTS_PROC_SFLG_PENDADD2SCHEDQ (((Uint32) 1) << 0) /* Pending
- add to
- schedule q */
-#define ERTS_PROC_SFLG_INRUNQ (((Uint32) 1) << 1) /* Process is
- in run q */
-#define ERTS_PROC_SFLG_TRAPEXIT (((Uint32) 1) << 2) /* Process is
- trapping
- exit */
-#define ERTS_PROC_SFLG_RUNNING (((Uint32) 1) << 3) /* Process is
- running */
-/* Scheduler flags in process struct... */
-#define ERTS_PROC_RUNQ_FLG_RUNNING (((Uint32) 1) << 0) /* Process is
- running */
-
-#endif
-
-
-#ifdef ERTS_SMP
-#define ERTS_PROC_IS_TRAPPING_EXITS(P) \
- (ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((P)) \
- & ERTS_PROC_LOCK_STATUS), \
- (P)->status_flags & ERTS_PROC_SFLG_TRAPEXIT)
-
-#define ERTS_PROC_SET_TRAP_EXIT(P) \
- (ERTS_SMP_LC_ASSERT(((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS) \
- & erts_proc_lc_my_proc_locks((P))) \
- == (ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS)), \
- (P)->status_flags |= ERTS_PROC_SFLG_TRAPEXIT, \
- (P)->flags |= F_TRAPEXIT, \
- 1)
-
-#define ERTS_PROC_UNSET_TRAP_EXIT(P) \
- (ERTS_SMP_LC_ASSERT(((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS) \
- & erts_proc_lc_my_proc_locks((P))) \
- == (ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS)), \
- (P)->status_flags &= ~ERTS_PROC_SFLG_TRAPEXIT, \
- (P)->flags &= ~F_TRAPEXIT, \
- 0)
-#else
-#define ERTS_PROC_IS_TRAPPING_EXITS(P) ((P)->flags & F_TRAPEXIT)
-#define ERTS_PROC_SET_TRAP_EXIT(P) ((P)->flags |= F_TRAPEXIT, 1)
-#define ERTS_PROC_UNSET_TRAP_EXIT(P) ((P)->flags &= ~F_TRAPEXIT, 0)
-#endif
-
/* Option flags to erts_send_exit_signal() */
#define ERTS_XSIG_FLG_IGN_KILL (((Uint32) 1) << 0)
#define ERTS_XSIG_FLG_NO_IGN_NORMAL (((Uint32) 1) << 1)
-
-/* Process status values */
-#define P_FREE 0
-#define P_RUNABLE 1
-#define P_WAITING 2
-#define P_RUNNING 3
-#define P_EXITING 4
-#define P_GARBING 5
-#define P_SUSPENDED 6
-
#define CANCEL_TIMER(p) \
do { \
if ((p)->flags & (F_INSLPQUEUE)) \
@@ -1083,15 +1122,187 @@ void erts_early_init_scheduling(int);
void erts_init_scheduling(int, int);
Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable);
+Uint64 erts_get_proc_interval(void);
+Uint64 erts_ensure_later_proc_interval(Uint64);
+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
@@ -1118,6 +1329,10 @@ void erts_smp_notify_check_children_needed(void);
#if ERTS_USE_ASYNC_READY_Q
void erts_notify_check_async_ready_queue(void *);
#endif
+#ifdef ERTS_SMP
+void erts_notify_code_ix_activation(Process* p, ErtsThrPrgrVal later);
+void erts_notify_finish_breakpointing(Process* p);
+#endif
void erts_schedule_misc_aux_work(int sched_id,
void (*func)(void *),
void *arg);
@@ -1128,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 *);
@@ -1138,14 +1353,6 @@ Eterm erts_get_schedulers_binds(Process *c_p);
Eterm erts_set_cpu_topology(Process *c_p, Eterm term);
Eterm erts_bind_schedulers(Process *c_p, Eterm how);
ErtsRunQueue *erts_schedid2runq(Uint);
-#ifdef ERTS_SMP
-ErtsMigrateResult erts_proc_migrate(Process *,
- ErtsProcLocks *,
- ErtsRunQueue *,
- int *,
- ErtsRunQueue *,
- int *);
-#endif
Process *schedule(Process*, int);
void erts_schedule_misc_op(void (*)(void *), void *);
Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*);
@@ -1155,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);
@@ -1179,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 *);
@@ -1194,8 +1400,7 @@ int erts_send_exit_signal(Process *,
#ifdef ERTS_SMP
void erts_handle_pending_exit(Process *, ErtsProcLocks);
#define ERTS_PROC_PENDING_EXIT(P) \
- (ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((P)) & ERTS_PROC_LOCK_STATUS),\
- (P)->pending_exit.reason != THE_NON_VALUE)
+ (ERTS_PSFLG_PENDING_EXIT & erts_smp_atomic32_read_acqb(&(P)->state))
#else
#define ERTS_PROC_PENDING_EXIT(P) 0
#endif
@@ -1205,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);
@@ -1247,13 +1449,26 @@ ErtsSchedulerData *erts_get_scheduler_data(void)
#endif
#endif
+void erts_schedule_process(Process *, erts_aint32_t);
+
+ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p);
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+ERTS_GLB_INLINE void
+erts_proc_notify_new_message(Process *p)
+{
+ /* No barrier needed, due to msg lock */
+ erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
+ if (!(state & ERTS_PSFLG_ACTIVE))
+ erts_schedule_process(p, state);
+}
+#endif
+
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
#define ERTS_PROCESS_LOCK_ONLY_LOCK_CHECK_PROTO__
#include "erl_process_lock.h"
#undef ERTS_PROCESS_LOCK_ONLY_LOCK_CHECK_PROTO__
-int erts_smp_lc_runq_is_locked(ErtsRunQueue *);
#define ERTS_SMP_LC_CHK_RUNQ_LOCK(RQ, L) \
do { \
if ((L)) \
@@ -1379,13 +1594,91 @@ erts_proc_set_error_handler(Process *p, ErtsProcLocks plocks, Eterm handler)
#endif
+#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
+
#ifdef ERTS_SMP
-ErtsRunQueue *erts_prepare_emigrate(ErtsRunQueue *c_rq,
- ErtsRunQueueInfo *c_rqi,
- int prio);
+#include "erl_thr_progress.h"
+
+extern erts_atomic_t erts_migration_paths;
+
+ERTS_GLB_INLINE ErtsMigrationPaths *erts_get_migration_paths_managed(void);
+ERTS_GLB_INLINE ErtsMigrationPaths *erts_get_migration_paths(void);
ERTS_GLB_INLINE ErtsRunQueue *erts_check_emigration_need(ErtsRunQueue *c_rq,
int prio);
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE ErtsMigrationPaths *
+erts_get_migration_paths_managed(void)
+{
+ return (ErtsMigrationPaths *) erts_atomic_read_ddrb(&erts_migration_paths);
+}
+
+ERTS_GLB_INLINE ErtsMigrationPaths *
+erts_get_migration_paths(void)
+{
+ if (erts_thr_progress_is_managed_thread())
+ return erts_get_migration_paths_managed();
+ else
+ return NULL;
+}
+
+ERTS_GLB_INLINE ErtsRunQueue *
+erts_check_emigration_need(ErtsRunQueue *c_rq, int prio)
+{
+ ErtsMigrationPaths *mps = erts_get_migration_paths();
+ ErtsMigrationPath *mp;
+ Uint32 flags;
+
+ if (!mps)
+ return NULL;
+
+ mp = &mps->mpath[c_rq->ix];
+ flags = mp->flags;
+
+ if (ERTS_CHK_RUNQ_FLG_EMIGRATE(flags, prio)) {
+ int len;
+
+ if (ERTS_CHK_RUNQ_FLG_EVACUATE(flags, prio)) {
+ /* force emigration */
+ return mp->prio[prio].runq;
+ }
+
+ if (flags & ERTS_RUNQ_FLG_INACTIVE) {
+ /*
+ * Run queue was inactive at last balance. Verify that
+ * it still is before forcing emigration.
+ */
+ if (ERTS_RUNQ_FLGS_GET(c_rq) & ERTS_RUNQ_FLG_INACTIVE)
+ return mp->prio[prio].runq;
+ }
+
+
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&c_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&c_rq->procs.prio_info[prio].len);
+
+ if (len > mp->prio[prio].limit.this) {
+ ErtsRunQueue *n_rq = mp->prio[prio].runq;
+ if (n_rq) {
+ if (prio == ERTS_PORT_PRIO_LEVEL)
+ len = RUNQ_READ_LEN(&n_rq->ports.info.len);
+ else
+ len = RUNQ_READ_LEN(&n_rq->procs.prio_info[prio].len);
+
+ if (len < mp->prio[prio].limit.other)
+ return n_rq;
+ }
+ }
+ }
+ return NULL;
+}
+
+#endif
+
+#endif
+
#endif
ERTS_GLB_INLINE int erts_is_scheduler_bound(ErtsSchedulerData *esdp);
@@ -1408,29 +1701,6 @@ ERTS_GLB_INLINE void erts_smp_runqs_unlock(ErtsRunQueue *rq1, ErtsRunQueue *rq2)
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-#ifdef ERTS_SMP
-ERTS_GLB_INLINE ErtsRunQueue *
-erts_check_emigration_need(ErtsRunQueue *c_rq, int prio)
-{
- ErtsRunQueueInfo *c_rqi;
-
- if (!ERTS_CHK_RUNQ_FLG_EMIGRATE(c_rq->flags, prio))
- return NULL;
-
- if (prio == ERTS_PORT_PRIO_LEVEL)
- c_rqi = &c_rq->ports.info;
- else
- c_rqi = &c_rq->procs.prio_info[prio];
-
- if (!ERTS_CHK_RUNQ_FLG_EVACUATE(c_rq->flags, prio)
- && !(c_rq->flags & ERTS_RUNQ_FLG_INACTIVE)
- && c_rqi->len <= c_rqi->migrate.limit.this)
- return NULL;
-
- return erts_prepare_emigrate(c_rq, c_rqi, prio);
-}
-#endif
-
ERTS_GLB_INLINE
int erts_is_scheduler_bound(ErtsSchedulerData *esdp)
{
@@ -1451,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
@@ -1468,10 +1738,9 @@ Uint erts_get_scheduler_id(void)
ERTS_GLB_INLINE ErtsRunQueue *
erts_get_runq_proc(Process *p)
{
- ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p));
#ifdef ERTS_SMP
- ASSERT(p->run_queue);
- return p->run_queue;
+ ASSERT(ERTS_AINT_NULL != erts_atomic_read_nob(&p->run_queue));
+ return (ErtsRunQueue *) erts_atomic_read_nob(&p->run_queue);
#else
return ERTS_RUNQ_IX(0);
#endif
@@ -1642,25 +1911,13 @@ extern int erts_disable_proc_not_running_opt;
#ifdef DEBUG
#define ERTS_SMP_ASSERT_IS_NOT_EXITING(P) \
- do { ASSERT(!(P)->is_exiting); } while (0)
+ do { ASSERT(!ERTS_PROC_IS_EXITING((P))); } while (0)
#else
#define ERTS_SMP_ASSERT_IS_NOT_EXITING(P)
#endif
-/* NOTE: At least one process lock has to be held on P! */
-#ifdef ERTS_ENABLE_LOCK_CHECK
-#define ERTS_PROC_IS_EXITING(P) \
- (ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks((P)) != 0 \
- || erts_lc_pix_lock_is_locked(ERTS_PID2PIXLOCK((P)->id))),\
- (P)->is_exiting)
-#else
-#define ERTS_PROC_IS_EXITING(P) ((P)->is_exiting)
-#endif
-
#else /* !ERTS_SMP */
-#define ERTS_PROC_IS_EXITING(P) ((P)->status == P_EXITING)
-
#define ERTS_SMP_ASSERT_IS_NOT_EXITING(P)
#define erts_pid2proc_not_running erts_pid2proc
@@ -1668,11 +1925,15 @@ extern int erts_disable_proc_not_running_opt;
#endif
+#define ERTS_PROC_IS_EXITING(P) \
+ (ERTS_PSFLG_EXITING & erts_smp_atomic32_read_acqb(&(P)->state))
+
+
/* 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..a611b52af2 100644
--- a/erts/emulator/beam/erl_process_dict.c
+++ b/erts/emulator/beam/erl_process_dict.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
@@ -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 3550f1396c..a93229c473 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -60,19 +60,16 @@ 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++) {
- if ((process_tab[i] != NULL) && (process_tab[i]->i != ENULL)) {
- if (process_tab[i]->status != P_EXITING) {
- Process* p = process_tab[i];
-
- if (p->status != P_GARBING) {
- dump_process_info(to, to_arg, p);
- }
- }
+ 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);
+ if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_GC)))
+ dump_process_info(to, to_arg, p);
}
}
@@ -88,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))
@@ -103,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;
@@ -326,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 f7900317cc..2db5df06b4 100644
--- a/erts/emulator/beam/erl_process_lock.c
+++ b/erts/emulator/beam/erl_process_lock.c
@@ -66,11 +66,12 @@
#endif
#include "erl_process.h"
-
-const Process erts_proc_lock_busy;
+#include "erl_thr_progress.h"
#ifdef ERTS_SMP
+#if ERTS_PROC_LOCK_OWN_IMPL
+
#define ERTS_PROC_LOCK_SPIN_COUNT_MAX 2000
#define ERTS_PROC_LOCK_SPIN_COUNT_SCHED_INC 32
#define ERTS_PROC_LOCK_SPIN_COUNT_BASE 1000
@@ -90,6 +91,13 @@ 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
+static int proc_lock_spin_count;
+static int aux_thr_proc_lock_spin_count;
+
+static void cleanup_tse(void);
+
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
#ifdef ERTS_ENABLE_LOCK_CHECK
static struct {
Sint16 proc_lock_main;
@@ -101,10 +109,6 @@ static struct {
erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS];
-static int proc_lock_spin_count;
-static int aux_thr_proc_lock_spin_count;
-
-static void cleanup_tse(void);
void
erts_init_proc_lock(int cpus)
@@ -118,13 +122,8 @@ erts_init_proc_lock(int cpus)
erts_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock");
#endif
}
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_thr_install_exit_handler(cleanup_tse);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main");
- lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link");
- lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
- lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
-#endif
if (cpus > 1) {
proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_BASE;
proc_lock_spin_count += (ERTS_PROC_LOCK_SPIN_COUNT_SCHED_INC
@@ -141,8 +140,17 @@ erts_init_proc_lock(int cpus)
}
if (proc_lock_spin_count > ERTS_PROC_LOCK_SPIN_COUNT_MAX)
proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_MAX;
+#endif
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main");
+ lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link");
+ lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq");
+ lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status");
+#endif
}
+#if ERTS_PROC_LOCK_OWN_IMPL
+
#ifdef ERTS_ENABLE_LOCK_CHECK
#define CHECK_UNUSED_TSE(W) ERTS_LC_ASSERT((W)->uflgs == 0)
#else
@@ -164,13 +172,6 @@ tse_return(erts_tse_t *tse)
erts_tse_return(tse);
}
-void
-erts_proc_lock_prepare_proc_lock_waiter(void)
-{
- tse_return(tse_fetch(NULL));
-}
-
-
static void
cleanup_tse(void)
{
@@ -397,7 +398,7 @@ 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;
/* Acquire a waiter object on which this thread can wait. */
@@ -551,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);
@@ -560,6 +561,16 @@ erts_proc_unlock_failed(Process *p,
transfer_locks(p, wait_locks, pix_lock, 1); /* unlocks pix_lock */
}
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
+void
+erts_proc_lock_prepare_proc_lock_waiter(void)
+{
+#if ERTS_PROC_LOCK_OWN_IMPL
+ tse_return(tse_fetch(NULL));
+#endif
+}
+
/*
* proc_safelock() locks process locks on two processes. In order
* to avoid a deadlock, proc_safelock() unlocks those locks that
@@ -568,12 +579,11 @@ erts_proc_unlock_failed(Process *p,
*/
static void
-proc_safelock(Process *a_proc,
- erts_pix_lock_t *a_pix_lck,
+proc_safelock(int is_managed,
+ Process *a_proc,
ErtsProcLocks a_have_locks,
ErtsProcLocks a_need_locks,
Process *b_proc,
- erts_pix_lock_t *b_pix_lck,
ErtsProcLocks b_have_locks,
ErtsProcLocks b_need_locks)
{
@@ -581,7 +591,6 @@ proc_safelock(Process *a_proc,
#ifdef ERTS_ENABLE_LOCK_CHECK
Eterm pid1, pid2;
#endif
- erts_pix_lock_t *pix_lck1, *pix_lck2;
ErtsProcLocks need_locks1, have_locks1, need_locks2, have_locks2;
ErtsProcLocks unlock_mask;
int lock_no, refc1 = 0, refc2 = 0;
@@ -593,53 +602,47 @@ proc_safelock(Process *a_proc,
* 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
- pix_lck1 = a_pix_lck;
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
- pix_lck2 = b_pix_lck;
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
- pix_lck1 = b_pix_lck;
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
- pix_lck2 = a_pix_lck;
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
- pix_lck1 = a_pix_lck;
need_locks1 = a_need_locks | b_need_locks;
have_locks1 = a_have_locks | b_have_locks;
p2 = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
pid2 = 0;
#endif
- pix_lck2 = NULL;
need_locks2 = 0;
have_locks2 = 0;
}
@@ -647,16 +650,14 @@ proc_safelock(Process *a_proc,
else {
p1 = b_proc;
#ifdef ERTS_ENABLE_LOCK_CHECK
- pid1 = b_proc->id;
+ pid1 = b_proc->common.id;
#endif
- pix_lck1 = b_pix_lck;
need_locks1 = b_need_locks;
have_locks1 = b_have_locks;
p2 = NULL;
#ifdef ERTS_ENABLE_LOCK_CHECK
pid2 = 0;
#endif
- pix_lck2 = NULL;
need_locks2 = 0;
have_locks2 = 0;
#ifdef ERTS_ENABLE_LOCK_CHECK
@@ -704,21 +705,21 @@ proc_safelock(Process *a_proc,
if (unlock_locks) {
have_locks1 &= ~unlock_locks;
need_locks1 |= unlock_locks;
- if (!have_locks1) {
+ if (!is_managed && !have_locks1) {
refc1 = 1;
erts_smp_proc_inc_refc(p1);
}
- erts_smp_proc_unlock__(p1, pix_lck1, unlock_locks);
+ erts_smp_proc_unlock(p1, unlock_locks);
}
unlock_locks = unlock_mask & have_locks2;
if (unlock_locks) {
have_locks2 &= ~unlock_locks;
need_locks2 |= unlock_locks;
- if (!have_locks2) {
+ if (!is_managed && !have_locks2) {
refc2 = 1;
erts_smp_proc_inc_refc(p2);
}
- erts_smp_proc_unlock__(p2, pix_lck2, unlock_locks);
+ erts_smp_proc_unlock(p2, unlock_locks);
}
}
@@ -749,7 +750,7 @@ proc_safelock(Process *a_proc,
if (need_locks2 & lock)
lock_no--;
locks = need_locks1 & lock_mask;
- erts_smp_proc_lock__(p1, pix_lck1, locks);
+ erts_smp_proc_lock(p1, locks);
have_locks1 |= locks;
need_locks1 &= ~locks;
}
@@ -760,7 +761,7 @@ proc_safelock(Process *a_proc,
lock = (1 << ++lock_no);
}
locks = need_locks2 & lock_mask;
- erts_smp_proc_lock__(p2, pix_lck2, locks);
+ erts_smp_proc_lock(p2, locks);
have_locks2 |= locks;
need_locks2 &= ~locks;
}
@@ -795,10 +796,12 @@ proc_safelock(Process *a_proc,
}
#endif
- if (refc1)
- erts_smp_proc_dec_refc(p1);
- if (refc2)
- erts_smp_proc_dec_refc(p2);
+ if (!is_managed) {
+ if (refc1)
+ erts_smp_proc_dec_refc(p1);
+ if (refc2)
+ erts_smp_proc_dec_refc(p2);
+ }
}
void
@@ -809,77 +812,196 @@ erts_proc_safelock(Process *a_proc,
ErtsProcLocks b_have_locks,
ErtsProcLocks b_need_locks)
{
- proc_safelock(a_proc,
- a_proc ? ERTS_PID2PIXLOCK(a_proc->id) : NULL,
+ proc_safelock(erts_get_scheduler_id() != 0,
+ a_proc,
a_have_locks,
a_need_locks,
b_proc,
- b_proc ? ERTS_PID2PIXLOCK(b_proc->id) : NULL,
b_have_locks,
b_need_locks);
}
-/*
- * erts_pid2proc_safelock() is called from erts_pid2proc_opt() when
- * it wasn't possible to trylock all locks needed.
- * c_p - current process
- * c_p_have_locks - locks held on c_p
- * pid - process id of process we are looking up
- * proc - process struct of process we are looking
- * up (both in and out argument)
- * need_locks - all locks we need (including have_locks)
- * pix_lock - pix lock for process we are looking up
- * flags - option flags
- */
-void
-erts_pid2proc_safelock(Process *c_p,
- ErtsProcLocks c_p_have_locks,
- Process **proc,
- ErtsProcLocks need_locks,
- erts_pix_lock_t *pix_lock,
- int flags)
+Process *
+erts_pid2proc_opt(Process *c_p,
+ ErtsProcLocks c_p_have_locks,
+ Eterm pid,
+ ErtsProcLocks pid_need_locks,
+ int flags)
{
- Process *p = *proc;
- ERTS_LC_ASSERT(p->lock.refc > 0);
- ERTS_LC_ASSERT(process_tab[internal_pid_index(p->id)] == p);
- p->lock.refc++;
- erts_pix_unlock(pix_lock);
-
- proc_safelock(c_p,
- c_p ? ERTS_PID2PIXLOCK(c_p->id) : NULL,
- c_p_have_locks,
- c_p_have_locks,
- p,
- pix_lock,
- 0,
- need_locks);
+ Process *dec_refc_proc = NULL;
+ ErtsThrPrgrDelayHandle dhndl;
+ ErtsProcLocks need_locks;
+ Uint pix;
+ Process *proc;
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ ErtsProcLocks lcnt_locks;
+#endif
- erts_pix_lock(pix_lock);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ if (c_p) {
+ ErtsProcLocks might_unlock = c_p_have_locks & pid_need_locks;
+ if (might_unlock)
+ erts_proc_lc_might_unlock(c_p, might_unlock);
+ }
+#endif
- if (!p->is_exiting
- || ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- && process_tab[internal_pid_index(p->id)] == p)) {
- ERTS_LC_ASSERT(p->lock.refc > 1);
- p->lock.refc--;
+ if (is_not_internal_pid(pid))
+ return NULL;
+ pix = internal_pid_index(pid);
+
+ ERTS_LC_ASSERT((pid_need_locks & ERTS_PROC_LOCKS_ALL) == pid_need_locks);
+ need_locks = pid_need_locks;
+
+ 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)
+ && ERTS_PROC_IS_EXITING(c_p))
+ return NULL;
+ need_locks &= ~c_p_have_locks;
+ if (!need_locks) {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(c_p);
+ return c_p;
+ }
}
- else {
- /* No proc. Note, we need to keep refc until after process unlock */
- erts_pix_unlock(pix_lock);
- erts_smp_proc_unlock__(p, pix_lock, need_locks);
- *proc = NULL;
- erts_pix_lock(pix_lock);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- if (--p->lock.refc == 0) {
- erts_pix_unlock(pix_lock);
- erts_free_proc(p);
- erts_pix_lock(pix_lock);
+
+ dhndl = erts_thr_progress_unmanaged_delay();
+
+ proc = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, pix);
+
+ if (proc) {
+ if (proc->common.id != pid)
+ proc = NULL;
+ else if (!need_locks) {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(proc);
}
+ else {
+ int busy;
+
+#if ERTS_PROC_LOCK_OWN_IMPL
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ lcnt_locks = need_locks;
+ if (!(flags & ERTS_P2P_FLG_TRY_LOCK)) {
+ erts_lcnt_proc_lock(&proc->lock, need_locks);
+ }
+#endif
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ /* Make sure erts_pid2proc_safelock() is enough to handle
+ a potential lock order violation situation... */
+ busy = erts_proc_lc_trylock_force_busy(proc, need_locks);
+ if (!busy)
+#endif
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+ {
+ /* Try a quick trylock to grab all the locks we need. */
+ busy = (int) erts_smp_proc_raw_trylock__(proc, need_locks);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_CHECK)
+ erts_proc_lc_trylock(proc, need_locks, !busy);
+#endif
+#ifdef ERTS_PROC_LOCK_DEBUG
+ if (!busy)
+ erts_proc_lock_op_debug(proc, need_locks, 1);
+#endif
+ }
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ if (flags & ERTS_P2P_FLG_TRY_LOCK)
+ erts_lcnt_proc_trylock(&proc->lock, need_locks,
+ busy ? EBUSY : 0);
+#endif
+
+ if (!busy) {
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ erts_smp_proc_inc_refc(proc);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ /* all is great */
+ if (!(flags & ERTS_P2P_FLG_TRY_LOCK))
+ erts_lcnt_proc_lock_post_x(&proc->lock, lcnt_locks,
+ __FILE__, __LINE__);
+#endif
+
+ }
+ else {
+ 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);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_ENABLE_LOCK_COUNT)
+ erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
+#endif
+
+ 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;
+
+ /*
+ * We don't want to call
+ * erts_thr_progress_unmanaged_continue()
+ * again.
+ */
+ dhndl = ERTS_THR_PRGR_DHANDLE_MANAGED;
+ }
+
+ proc_safelock(managed,
+ c_p,
+ c_p_have_locks,
+ c_p_have_locks,
+ proc,
+ 0,
+ need_locks);
+ }
+ }
+ }
+ }
+
+ if (dhndl != ERTS_THR_PRGR_DHANDLE_MANAGED)
+ erts_thr_progress_unmanaged_continue(dhndl);
+
+ if (need_locks
+ && proc
+ && proc != ERTS_PROC_LOCK_BUSY
+ && (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
+ ? ERTS_PROC_IS_EXITING(proc)
+ : (proc
+ != (Process *) erts_ptab_pix2intptr_nob(&erts_proc, pix)))) {
+
+ erts_smp_proc_unlock(proc, need_locks);
+
+ if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
+ dec_refc_proc = proc;
+ proc = NULL;
+
}
+
+ if (dec_refc_proc)
+ erts_smp_proc_dec_refc(dec_refc_proc);
+
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_PROC_LOCK_DEBUG)
+ ERTS_LC_ASSERT(!proc
+ || proc == ERTS_PROC_LOCK_BUSY
+ || (pid_need_locks ==
+ (ERTS_PROC_LOCK_FLGS_READ_(&proc->lock)
+ & pid_need_locks)));
+#endif
+
+ return proc;
}
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
@@ -890,32 +1012,67 @@ erts_proc_lock_init(Process *p)
#endif
for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++)
p->lock.queue[i] = NULL;
- p->lock.refc = 1;
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_proc_lock_init(p);
- erts_lcnt_proc_lock(&(p->lock), ERTS_PROC_LOCKS_ALL);
- erts_lcnt_proc_lock_post_x(&(p->lock), ERTS_PROC_LOCKS_ALL, __FILE__, __LINE__);
-#endif
-
#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->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->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->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->common.id);
+ ethr_mutex_lock(&p->lock.status.mtx);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_trylock(1, &p->lock.status.lc);
+#endif
+#endif
+ erts_atomic32_init_nob(&p->lock.refc, 1);
#ifdef ERTS_PROC_LOCK_DEBUG
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);
+ erts_lcnt_proc_lock(&(p->lock), ERTS_PROC_LOCKS_ALL);
+ erts_lcnt_proc_lock_post_x(&(p->lock), ERTS_PROC_LOCKS_ALL, __FILE__, __LINE__);
+#endif
+}
+
+void
+erts_proc_lock_fin(Process *p)
+{
+#if ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_mtx_destroy(&p->lock.main);
+ erts_mtx_destroy(&p->lock.link);
+ erts_mtx_destroy(&p->lock.msgq);
+ erts_mtx_destroy(&p->lock.status);
+#endif
+#if defined(ERTS_ENABLE_LOCK_COUNT) && defined(ERTS_SMP)
+ erts_lcnt_proc_lock_destroy(p);
+#endif
}
/* --- Process lock counting ----------------------------------------------- */
-#ifdef ERTS_ENABLE_LOCK_COUNT
+#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);
@@ -1023,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))) {
@@ -1049,11 +1207,13 @@ void erts_lcnt_enable_proc_lock_count(int enable) {
#ifdef ERTS_ENABLE_LOCK_CHECK
+#if ERTS_PROC_LOCK_OWN_IMPL
+
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;
@@ -1077,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;
@@ -1101,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;
@@ -1121,11 +1281,14 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks)
}
}
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
void
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;
@@ -1143,13 +1306,24 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_main;
erts_lc_might_unlock(&lck);
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_lc_might_unlock(&p->lock.main.lc);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_lc_might_unlock(&p->lock.link.lc);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_lc_might_unlock(&p->lock.msgq.lc);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_lc_might_unlock(&p->lock.status.lc);
+#endif
}
void
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;
@@ -1167,13 +1341,24 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_status;
erts_lc_require_lock(&lck);
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_lc_require_lock(&p->lock.main.lc);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_lc_require_lock(&p->lock.link.lc);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_lc_require_lock(&p->lock.msgq.lc);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_lc_require_lock(&p->lock.status.lc);
+#endif
}
void
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;
@@ -1191,15 +1376,26 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks)
lck.id = lc_id.proc_lock_main;
erts_lc_unrequire_lock(&lck);
}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_lc_unrequire_lock(&p->lock.main.lc);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_lc_unrequire_lock(&p->lock.link.lc);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_lc_unrequire_lock(&p->lock.msgq.lc);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_lc_unrequire_lock(&p->lock.status.lc);
+#endif
}
+#if ERTS_PROC_LOCK_OWN_IMPL
int
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)
@@ -1218,42 +1414,61 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks)
return 0;
}
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
+
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
+ erts_lc_check_exact(&p->lock.main.lc, 1);
+#endif
}
+#if ERTS_PROC_LOCK_OWN_IMPL
#define ERTS_PROC_LC_EMPTY_LOCK_INIT \
ERTS_LC_LOCK_INIT(-1, THE_NON_VALUE, ERTS_LC_FLG_LT_PROCLOCK)
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
void
erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks)
{
int have_locks_len = 0;
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t have_locks[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
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];
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ have_locks[have_locks_len++] = p->lock.main.lc;
+ if (locks & ERTS_PROC_LOCK_LINK)
+ have_locks[have_locks_len++] = p->lock.link.lc;
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ have_locks[have_locks_len++] = p->lock.msgq.lc;
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ have_locks[have_locks_len++] = p->lock.status.lc;
+#endif
erts_lc_check(have_locks, have_locks_len, NULL, 0);
}
@@ -1262,6 +1477,7 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks)
{
int have_locks_len = 0;
int have_not_locks_len = 0;
+#if ERTS_PROC_LOCK_OWN_IMPL
erts_lc_lock_t have_locks[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
ERTS_PROC_LC_EMPTY_LOCK_INIT,
@@ -1273,36 +1489,57 @@ 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];
+ erts_lc_lock_t have_not_locks[4];
+
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ have_locks[have_locks_len++] = p->lock.main.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.main.lc;
+ if (locks & ERTS_PROC_LOCK_LINK)
+ have_locks[have_locks_len++] = p->lock.link.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.link.lc;
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ have_locks[have_locks_len++] = p->lock.msgq.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.msgq.lc;
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ have_locks[have_locks_len++] = p->lock.status.lc;
+ else
+ have_not_locks[have_not_locks_len++] = p->lock.status.lc;
+#endif
erts_lc_check(have_locks, have_locks_len,
have_not_locks, have_not_locks_len);
@@ -1312,20 +1549,26 @@ ErtsProcLocks
erts_proc_lc_my_proc_locks(Process *p)
{
int resv[4];
+ 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)};
-
- ErtsProcLocks res = 0;
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_lc_lock_t locks[4] = {p->lock.main.lc,
+ p->lock.link.lc,
+ p->lock.msgq.lc,
+ p->lock.status.lc};
+#endif
erts_lc_have_locks(resv, locks, 4);
if (resv[0])
@@ -1358,7 +1601,7 @@ erts_proc_lc_chk_no_proc_locks(char *file, int line)
#endif /* #ifdef ERTS_ENABLE_LOCK_CHECK */
-#ifdef ERTS_PROC_LOCK_HARD_DEBUG
+#if ERTS_PROC_LOCK_OWN_IMPL && defined(ERTS_PROC_LOCK_HARD_DEBUG)
void
check_queue(erts_proc_lock_t *lck)
{
@@ -1391,4 +1634,4 @@ check_queue(erts_proc_lock_t *lck)
}
#endif
-#endif /* ERTS_SMP (the whole file) */
+#endif /* ERTS_SMP */
diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h
index 062b8366d3..9dd503f3cb 100644
--- a/erts/emulator/beam/erl_process_lock.h
+++ b/erts/emulator/beam/erl_process_lock.h
@@ -37,10 +37,21 @@
#include "erl_smp.h"
+#if defined(VALGRIND) || defined(ETHR_DISABLE_NATIVE_IMPLS)
+# define ERTS_PROC_LOCK_OWN_IMPL 0
+#else
+# define ERTS_PROC_LOCK_OWN_IMPL 1
+#endif
+
#define ERTS_PROC_LOCK_ATOMIC_IMPL 0
#define ERTS_PROC_LOCK_SPINLOCK_IMPL 0
#define ERTS_PROC_LOCK_MUTEX_IMPL 0
+#if !ERTS_PROC_LOCK_OWN_IMPL
+#define ERTS_PROC_LOCK_RAW_MUTEX_IMPL 1
+#else
+#define ERTS_PROC_LOCK_RAW_MUTEX_IMPL 0
+
#if defined(ETHR_HAVE_32BIT_NATIVE_ATOMIC_OPS)
# undef ERTS_PROC_LOCK_ATOMIC_IMPL
# define ERTS_PROC_LOCK_ATOMIC_IMPL 1
@@ -52,27 +63,38 @@
# define ERTS_PROC_LOCK_MUTEX_IMPL 1
#endif
+#endif
+
#define ERTS_PROC_LOCK_MAX_BIT 3
typedef erts_aint32_t ErtsProcLocks;
typedef struct erts_proc_lock_t_ {
+#if ERTS_PROC_LOCK_OWN_IMPL
#if ERTS_PROC_LOCK_ATOMIC_IMPL
erts_smp_atomic32_t flags;
#else
ErtsProcLocks flags;
#endif
erts_tse_t *queue[ERTS_PROC_LOCK_MAX_BIT+1];
- Sint32 refc;
-#ifdef ERTS_PROC_LOCK_DEBUG
- erts_smp_atomic32_t locked[ERTS_PROC_LOCK_MAX_BIT+1];
-#endif
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_lock_t lcnt_main;
erts_lcnt_lock_t lcnt_link;
erts_lcnt_lock_t lcnt_msgq;
erts_lcnt_lock_t lcnt_status;
#endif
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ erts_mtx_t main;
+ erts_mtx_t link;
+ erts_mtx_t msgq;
+ erts_mtx_t status;
+#else
+# error "no implementation"
+#endif
+ erts_atomic32_t refc;
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_smp_atomic32_t locked[ERTS_PROC_LOCK_MAX_BIT+1];
+#endif
} erts_proc_lock_t;
/* Process lock flags */
@@ -105,11 +127,9 @@ typedef struct erts_proc_lock_t_ {
/*
* Status lock:
* Protects the following fields in the process structure:
- * * status
- * * rstatus
- * * status_flags
* * pending_suspenders
* * suspendee
+ * * ...
*/
#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << ERTS_PROC_LOCK_MAX_BIT)
@@ -141,14 +161,11 @@ typedef struct erts_proc_lock_t_ {
* Other rules regarding process locking:
*
* Exiting processes:
- * When changing status to P_EXITING on a process, you are required
- * to take all process locks (ERTS_PROC_LOCKS_ALL). Thus, by holding
- * at least one process lock (whichever one doesn't matter) you
- * are guaranteed that the process won't exit until the lock you are
- * holding has been released. Appart from all process locks also
- * the pix lock corresponding to the process has to be held.
- * At the same time as status is changed to P_EXITING, also the
- * field 'is_exiting' in the process structure is set to a value != 0.
+ * When changing state to exiting (ERTS_PSFLG_EXITING) on a process,
+ * you are required to take all process locks (ERTS_PROC_LOCKS_ALL).
+ * Thus, by holding at least one process lock (whichever one doesn't
+ * matter) you are guaranteed that the process won't exit until the
+ * lock you are holding has been released.
*
* Lock order:
* Process locks with low numeric values has to be locked before
@@ -159,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
@@ -186,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)
@@ -260,12 +277,10 @@ typedef struct {
} u;
} erts_pix_lock_t;
-#define ERTS_PIX2PIXLOCKIX(PIX) \
- ((PIX) & ((1 << ERTS_PIX_LOCKS_BITS) - 1))
-#define ERTS_PIX2PIXLOCK(PIX) \
- (&erts_pix_locks[ERTS_PIX2PIXLOCKIX((PIX))])
#define ERTS_PID2PIXLOCK(PID) \
- ERTS_PIX2PIXLOCK(internal_pid_data((PID)))
+ (&erts_pix_locks[(internal_pid_data((PID)) & ((1 << ERTS_PIX_LOCKS_BITS) - 1))])
+
+#if ERTS_PROC_LOCK_OWN_IMPL
#if ERTS_PROC_LOCK_ATOMIC_IMPL
@@ -335,11 +350,13 @@ erts_proc_lock_flags_cmpxchg(erts_proc_lock_t *lck, ErtsProcLocks new,
#define ERTS_PROC_LOCK_FLGS_READ_(L) ((L)->flags)
#endif /* end no opt atomic ops */
+#endif /* ERTS_PROC_LOCK_OWN_IMPL */
extern erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS];
void erts_init_proc_lock(int cpus);
void erts_proc_lock_prepare_proc_lock_waiter(void);
+#if ERTS_PROC_LOCK_OWN_IMPL
void erts_proc_lock_failed(Process *,
erts_pix_lock_t *,
ErtsProcLocks,
@@ -347,6 +364,7 @@ void erts_proc_lock_failed(Process *,
void erts_proc_unlock_failed(Process *,
erts_pix_lock_t *,
ErtsProcLocks);
+#endif
ERTS_GLB_INLINE void erts_pix_lock(erts_pix_lock_t *);
ERTS_GLB_INLINE void erts_pix_unlock(erts_pix_lock_t *);
@@ -410,6 +428,7 @@ ERTS_GLB_INLINE int erts_lc_pix_lock_is_locked(erts_pix_lock_t *pixlck)
ERTS_GLB_INLINE ErtsProcLocks
erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
ErtsProcLocks expct_lflgs = 0;
while (1) {
@@ -429,8 +448,38 @@ erts_smp_proc_raw_trylock__(Process *p, ErtsProcLocks locks)
/* cmpxchg failed, try again (should be rare). */
expct_lflgs = lflgs;
}
-}
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ if (erts_mtx_trylock(&p->lock.main) == EBUSY)
+ goto busy_main;
+ if (locks & ERTS_PROC_LOCK_LINK)
+ if (erts_mtx_trylock(&p->lock.link) == EBUSY)
+ goto busy_link;
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ if (erts_mtx_trylock(&p->lock.msgq) == EBUSY)
+ goto busy_msgq;
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ if (erts_mtx_trylock(&p->lock.status) == EBUSY)
+ goto busy_status;
+
+ return 0;
+
+busy_status:
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_mtx_unlock(&p->lock.msgq);
+busy_msgq:
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_mtx_unlock(&p->lock.link);
+busy_link:
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_mtx_unlock(&p->lock.main);
+busy_main:
+
+ return EBUSY;
+#endif
+}
ERTS_GLB_INLINE void
#ifdef ERTS_ENABLE_LOCK_COUNT
@@ -444,10 +493,13 @@ erts_smp_proc_lock__(Process *p,
ErtsProcLocks locks)
#endif
{
+#if ERTS_PROC_LOCK_OWN_IMPL
+
ErtsProcLocks old_lflgs;
#if !ERTS_PROC_LOCK_ATOMIC_IMPL
erts_pix_lock(pix_lck);
#endif
+
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_proc_lock(&(p->lock), locks);
#endif
@@ -471,12 +523,14 @@ erts_smp_proc_lock__(Process *p,
erts_pix_unlock(pix_lck);
}
#endif
+
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_proc_lock_post_x(&(p->lock), locks, file, line);
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_proc_lc_lock(p, locks);
#endif
+
#ifdef ERTS_PROC_LOCK_DEBUG
erts_proc_lock_op_debug(p, locks, 1);
#endif
@@ -484,6 +538,22 @@ erts_smp_proc_lock__(Process *p,
#if ERTS_PROC_LOCK_ATOMIC_IMPL
ETHR_COMPILER_BARRIER;
#endif
+
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_mtx_lock(&p->lock.main);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_mtx_lock(&p->lock.link);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_mtx_lock(&p->lock.msgq);
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_mtx_lock(&p->lock.status);
+
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_proc_lock_op_debug(p, locks, 1);
+#endif
+
+#endif
}
ERTS_GLB_INLINE void
@@ -491,6 +561,7 @@ erts_smp_proc_unlock__(Process *p,
erts_pix_lock_t *pix_lck,
ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
ErtsProcLocks old_lflgs;
#if ERTS_PROC_LOCK_ATOMIC_IMPL
@@ -555,6 +626,23 @@ erts_smp_proc_unlock__(Process *p,
break;
}
+
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_proc_lock_op_debug(p, locks, 0);
+#endif
+
+ if (locks & ERTS_PROC_LOCK_STATUS)
+ erts_mtx_unlock(&p->lock.status);
+ if (locks & ERTS_PROC_LOCK_MSGQ)
+ erts_mtx_unlock(&p->lock.msgq);
+ if (locks & ERTS_PROC_LOCK_LINK)
+ erts_mtx_unlock(&p->lock.link);
+ if (locks & ERTS_PROC_LOCK_MAIN)
+ erts_mtx_unlock(&p->lock.main);
+#endif
+
}
ERTS_GLB_INLINE int
@@ -562,6 +650,7 @@ erts_smp_proc_trylock__(Process *p,
erts_pix_lock_t *pix_lck,
ErtsProcLocks locks)
{
+#if ERTS_PROC_LOCK_OWN_IMPL
int res;
#ifdef ERTS_ENABLE_LOCK_CHECK
@@ -573,6 +662,7 @@ erts_smp_proc_trylock__(Process *p,
else
#endif
{
+
#if !ERTS_PROC_LOCK_ATOMIC_IMPL
erts_pix_lock(pix_lck);
#endif
@@ -611,8 +701,18 @@ erts_smp_proc_trylock__(Process *p,
#if ERTS_PROC_LOCK_ATOMIC_IMPL
ETHR_COMPILER_BARRIER;
#endif
-
return res;
+
+#elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL
+ if (erts_smp_proc_raw_trylock__(p, locks) != 0)
+ return EBUSY;
+ else {
+#ifdef ERTS_PROC_LOCK_DEBUG
+ erts_proc_lock_op_debug(p, locks, 1);
+#endif
+ return 0;
+ }
+#endif
}
#ifdef ERTS_PROC_LOCK_DEBUG
@@ -667,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)
@@ -675,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*/
@@ -689,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
@@ -705,50 +805,34 @@ 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
}
-
ERTS_GLB_INLINE void erts_smp_proc_inc_refc(Process *p)
{
#ifdef ERTS_SMP
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- p->lock.refc++;
- erts_pix_unlock(pixlck);
+ erts_ptab_inc_refc(&p->common);
#endif
}
ERTS_GLB_INLINE void erts_smp_proc_dec_refc(Process *p)
{
#ifdef ERTS_SMP
- Process *fp;
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- fp = --p->lock.refc == 0 ? p : NULL;
- erts_pix_unlock(pixlck);
- if (fp)
- erts_free_proc(fp);
+ int referred = erts_ptab_dec_test_refc(&p->common);
+ if (!referred)
+ erts_free_proc(p);
#endif
}
-ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 refc)
+ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 add_refc)
{
#ifdef ERTS_SMP
- Process *fp;
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- ERTS_LC_ASSERT(p->lock.refc > 0);
- p->lock.refc += refc;
- fp = p->lock.refc == 0 ? p : NULL;
- erts_pix_unlock(pixlck);
- if (fp)
- erts_free_proc(fp);
+ int referred = erts_ptab_add_test_refc(&p->common, add_refc);
+ if (!referred)
+ erts_free_proc(p);
#endif
}
@@ -756,6 +840,7 @@ ERTS_GLB_INLINE void erts_smp_proc_add_refc(Process *p, Sint32 refc)
#ifdef ERTS_SMP
void erts_proc_lock_init(Process *);
+void erts_proc_lock_fin(Process *);
void erts_proc_safelock(Process *a_proc,
ErtsProcLocks a_have_locks,
ErtsProcLocks a_need_locks,
@@ -782,217 +867,71 @@ 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)
-ERTS_GLB_INLINE Process *
-erts_pid2proc_opt(Process *, ErtsProcLocks, Eterm, ErtsProcLocks, int);
-#ifdef ERTS_SMP
-void
-erts_pid2proc_safelock(Process *c_p,
- ErtsProcLocks c_p_have_locks,
- Process **proc,
- ErtsProcLocks need_locks,
- erts_pix_lock_t *pix_lock,
- int flags);
-ERTS_GLB_INLINE Process *erts_pid2proc_unlocked_opt(Eterm pid, int flags);
-#define erts_pid2proc_unlocked(PID) erts_pid2proc_unlocked_opt((PID), 0)
-#else
-#define erts_pid2proc_unlocked_opt(PID, FLGS) \
- erts_pid2proc_opt(NULL, 0, (PID), 0, FLGS)
-#define erts_pid2proc_unlocked(PID) erts_pid2proc_opt(NULL, 0, (PID), 0, 0)
+ERTS_GLB_INLINE Process *erts_pix2proc(int ix);
+ERTS_GLB_INLINE Process *erts_proc_lookup_raw(Eterm pid);
+ERTS_GLB_INLINE Process *erts_proc_lookup(Eterm pid);
+
+#ifndef ERTS_SMP
+ERTS_GLB_INLINE
#endif
+Process *erts_pid2proc_opt(Process *, ErtsProcLocks, Eterm, ErtsProcLocks, int);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE Process *
-#ifdef ERTS_SMP
-erts_pid2proc_unlocked_opt(Eterm pid, int flags)
-#else
-erts_pid2proc_opt(Process *c_p_unused,
- ErtsProcLocks c_p_have_locks_unused,
- Eterm pid,
- ErtsProcLocks pid_need_locks_unused,
- int flags)
-#endif
+ERTS_GLB_INLINE Process *erts_pix2proc(int ix)
{
- Uint pix;
Process *proc;
+ 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;
+
+ ERTS_SMP_LC_ASSERT(erts_thr_progress_lc_is_delaying());
if (is_not_internal_pid(pid))
return NULL;
- pix = internal_pid_index(pid);
- if(pix >= erts_max_processes)
+
+ proc = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc,
+ internal_pid_index(pid));
+ if (proc && proc->common.id != pid)
return NULL;
- proc = process_tab[pix];
- if (proc) {
- if (proc->id != pid
- || (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- && proc->status == P_EXITING))
- proc = NULL;
- }
return proc;
}
-#ifdef ERTS_SMP
+ERTS_GLB_INLINE Process *erts_proc_lookup(Eterm pid)
+{
+ Process *proc = erts_proc_lookup_raw(pid);
+ if (proc && ERTS_PROC_IS_EXITING(proc))
+ return NULL;
+ return proc;
+}
+#ifndef ERTS_SMP
ERTS_GLB_INLINE Process *
-erts_pid2proc_opt(Process *c_p,
- ErtsProcLocks c_p_have_locks,
+erts_pid2proc_opt(Process *c_p_unused,
+ ErtsProcLocks c_p_have_locks_unused,
Eterm pid,
- ErtsProcLocks pid_need_locks,
+ ErtsProcLocks pid_need_locks_unused,
int flags)
{
- erts_pix_lock_t *pix_lock;
- ErtsProcLocks need_locks;
- Uint pix;
- Process *proc;
-#ifdef ERTS_ENABLE_LOCK_COUNT
- ErtsProcLocks lcnt_locks;
-#endif
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- if (c_p) {
- ErtsProcLocks might_unlock = c_p_have_locks & pid_need_locks;
- if (might_unlock)
- erts_proc_lc_might_unlock(c_p, might_unlock);
- }
-#endif
- if (is_not_internal_pid(pid)) {
- proc = NULL;
- goto done;
- }
- pix = internal_pid_index(pid);
- if(pix >= erts_max_processes) {
- proc = NULL;
- goto done;
- }
-
- ERTS_LC_ASSERT((pid_need_locks & ERTS_PROC_LOCKS_ALL) == pid_need_locks);
- need_locks = pid_need_locks;
-
- pix_lock = ERTS_PIX2PIXLOCK(pix);
-
- if (c_p && c_p->id == pid) {
- ASSERT(c_p->id != ERTS_INVALID_PID);
- ASSERT(c_p == process_tab[pix]);
- if (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X) && c_p->is_exiting) {
- proc = NULL;
- goto done;
- }
- need_locks &= ~c_p_have_locks;
- if (!need_locks) {
- proc = c_p;
- erts_pix_lock(pix_lock);
- if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
- proc->lock.refc++;
- erts_pix_unlock(pix_lock);
- goto done;
- }
- }
-
- erts_pix_lock(pix_lock);
-
- proc = process_tab[pix];
- if (proc) {
- if (proc->id != pid || (!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- && ERTS_PROC_IS_EXITING(proc))) {
- proc = NULL;
- }
- else if (!need_locks) {
- if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
- proc->lock.refc++;
- }
- else {
- int busy;
-
-#ifdef ERTS_ENABLE_LOCK_COUNT
- lcnt_locks = need_locks;
- if (!(flags & ERTS_P2P_FLG_TRY_LOCK)) {
- erts_lcnt_proc_lock(&proc->lock, need_locks);
- }
-#endif
-
-#ifdef ERTS_ENABLE_LOCK_CHECK
- /* Make sure erts_pid2proc_safelock() is enough to handle
- a potential lock order violation situation... */
- busy = erts_proc_lc_trylock_force_busy(proc, need_locks);
- if (!busy)
-#endif
- {
- /* Try a quick trylock to grab all the locks we need. */
- busy = (int) erts_smp_proc_raw_trylock__(proc, need_locks);
-#ifdef ERTS_ENABLE_LOCK_CHECK
- erts_proc_lc_trylock(proc, need_locks, !busy);
-#endif
-#ifdef ERTS_PROC_LOCK_DEBUG
- if (!busy)
- erts_proc_lock_op_debug(proc, need_locks, 1);
-#endif
- }
-
-#ifdef ERTS_ENABLE_LOCK_COUNT
- if (flags & ERTS_P2P_FLG_TRY_LOCK) {
- if (busy) {
- erts_lcnt_proc_trylock(&proc->lock, need_locks, EBUSY);
- } else {
- erts_lcnt_proc_trylock(&proc->lock, need_locks, 0);
- }
- }
-#endif
- if (!busy) {
- if (flags & ERTS_P2P_FLG_SMP_INC_REFC)
- proc->lock.refc++;
-#ifdef ERTS_ENABLE_LOCK_COUNT
- /* all is great */
- if (!(flags & ERTS_P2P_FLG_TRY_LOCK)) {
- erts_lcnt_proc_lock_post_x(&proc->lock, lcnt_locks, __FILE__, __LINE__);
- }
-#endif
- }
- else {
- if (flags & ERTS_P2P_FLG_TRY_LOCK)
- proc = ERTS_PROC_LOCK_BUSY;
- else {
-#ifdef ERTS_ENABLE_LOCK_COUNT
- erts_lcnt_proc_lock_unaquire(&proc->lock, lcnt_locks);
-#endif
- erts_pid2proc_safelock(c_p,
- c_p_have_locks,
- &proc,
- pid_need_locks,
- pix_lock,
- flags);
- if (proc && (flags & ERTS_P2P_FLG_SMP_INC_REFC))
- proc->lock.refc++;
- }
- }
- }
- }
-
- erts_pix_unlock(pix_lock);
-#ifdef ERTS_PROC_LOCK_DEBUG
- ERTS_LC_ASSERT(!proc
- || proc == ERTS_PROC_LOCK_BUSY
- || (pid_need_locks ==
- (ERTS_PROC_LOCK_FLGS_READ_(&proc->lock)
- & pid_need_locks)));
-#endif
-
-
- done:
-
-#if ERTS_PROC_LOCK_ATOMIC_IMPL
- ETHR_COMPILER_BARRIER;
-#endif
-
- return proc;
+ Process *proc = erts_proc_lookup_raw(pid);
+ return ((!(flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
+ && proc
+ && ERTS_PROC_IS_EXITING(proc))
+ ? NULL
+ : proc);
}
-#endif /* ERTS_SMP */
+#endif /* !ERTS_SMP */
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
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_smp.h b/erts/emulator/beam/erl_smp.h
index a32e9d9d7c..ecb5525022 100644
--- a/erts/emulator/beam/erl_smp.h
+++ b/erts/emulator/beam/erl_smp.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-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
@@ -259,6 +259,9 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_dw_atomic_read_wb erts_dw_atomic_read_wb
#define erts_smp_dw_atomic_cmpxchg_wb erts_dw_atomic_cmpxchg_wb
+#define erts_smp_dw_atomic_set_dirty erts_dw_atomic_set_dirty
+#define erts_smp_dw_atomic_read_dirty erts_dw_atomic_read_dirty
+
/* Word size atomics */
#define erts_smp_atomic_init_nob erts_atomic_init_nob
@@ -274,6 +277,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 +292,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 +307,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 +322,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 +337,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 +352,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 +367,10 @@ 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
+
+#define erts_smp_atomic_set_dirty erts_atomic_set_dirty
+#define erts_smp_atomic_read_dirty erts_atomic_read_dirty
/* 32-bit atomics */
@@ -374,6 +387,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 +402,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 +417,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 +432,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 +447,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 +462,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 +477,10 @@ 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
+
+#define erts_smp_atomic32_set_dirty erts_atomic32_set_dirty
+#define erts_smp_atomic32_read_dirty erts_atomic32_read_dirty
#else /* !ERTS_SMP */
@@ -498,6 +521,9 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig);
#define erts_smp_dw_atomic_read_wb erts_no_dw_atomic_read
#define erts_smp_dw_atomic_cmpxchg_wb erts_no_dw_atomic_cmpxchg
+#define erts_smp_dw_atomic_set_dirty erts_no_dw_atomic_set
+#define erts_smp_dw_atomic_read_dirty erts_no_dw_atomic_read
+
/* Word size atomics */
#define erts_smp_atomic_init_nob erts_no_atomic_set
@@ -513,6 +539,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 +554,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 +569,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 +584,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 +599,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 +614,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 +629,10 @@ 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
+
+#define erts_smp_atomic_set_dirty erts_no_atomic_set
+#define erts_smp_atomic_read_dirty erts_no_atomic_read
/* 32-bit atomics */
@@ -613,6 +649,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 +664,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 +679,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 +694,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 +709,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 +724,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 +739,10 @@ 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
+
+#define erts_smp_atomic32_set_dirty erts_no_atomic32_set
+#define erts_smp_atomic32_read_dirty erts_no_atomic32_read
#endif /* !ERTS_SMP */
diff --git a/erts/emulator/beam/erl_sys_driver.h b/erts/emulator/beam/erl_sys_driver.h
index d429d0ce96..dab4a94a9b 100644
--- a/erts/emulator/beam/erl_sys_driver.h
+++ b/erts/emulator/beam/erl_sys_driver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,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..2f206ffbec 100644
--- a/erts/emulator/beam/erl_term.c
+++ b/erts/emulator/beam/erl_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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..953edf79ea 100644
--- a/erts/emulator/beam/erl_term.h
+++ b/erts/emulator/beam/erl_term.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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..1292cb0678 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2011-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
@@ -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,46 @@ 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);
+ intrnl->misc.data.umrefc_ix.waiting
+ = tpd->leader_state.umrefc_ix.waiting;
+ 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 +845,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 +902,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 +936,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 +983,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 +1101,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 +1116,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_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c
index f07964a265..f8ca87ddcc 100644
--- a/erts/emulator/beam/erl_thr_queue.c
+++ b/erts/emulator/beam/erl_thr_queue.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2011-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
@@ -113,6 +113,11 @@ sl_element_free(ErtsThrQElement_t *p)
#endif
+#define ErtsThrQDirtyReadEl(A) \
+ ((ErtsThrQElement_t *) erts_atomic_read_dirty((A)))
+#define ErtsThrQDirtySetEl(A, V) \
+ erts_atomic_set_dirty((A), (erts_aint_t) (V))
+
typedef union {
ErtsThrQ_t q;
char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsThrQ_t))];
@@ -137,7 +142,7 @@ erts_thr_q_initialize(ErtsThrQ_t *q, ErtsThrQInit_t *qi)
q->last = NULL;
q->q.blk = NULL;
#else
- erts_atomic_init_nob(&q->tail.data.marker.next.atmc, ERTS_AINT_NULL);
+ erts_atomic_init_nob(&q->tail.data.marker.next, ERTS_AINT_NULL);
q->tail.data.marker.data.ptr = NULL;
erts_atomic_init_nob(&q->tail.data.last,
(erts_aint_t) &q->tail.data.marker);
@@ -150,7 +155,7 @@ erts_thr_q_initialize(ErtsThrQ_t *q, ErtsThrQInit_t *qi)
if (!q->tail.data.notify)
q->tail.data.notify = noop_callback;
- q->head.head.ptr = &q->tail.data.marker;
+ erts_atomic_init_nob(&q->head.head, (erts_aint_t) &q->tail.data.marker);
q->head.live = qi->live.objects;
q->head.first = &q->tail.data.marker;
q->head.unref_end = &q->tail.data.marker;
@@ -296,17 +301,17 @@ element_free(ErtsThrQ_t *q, ErtsThrQElement_t *el)
#ifdef USE_THREADS
static ERTS_INLINE ErtsThrQElement_t *
-enqueue_managed(ErtsThrQ_t *q, ErtsThrQElement_t *this, int want_last)
+enqueue_managed(ErtsThrQ_t *q, ErtsThrQElement_t *this)
{
erts_aint_t ilast, itmp;
- erts_atomic_init_nob(&this->next.atmc, ERTS_AINT_NULL);
+ erts_atomic_init_nob(&this->next, ERTS_AINT_NULL);
/* Enqueue at end of list... */
ilast = erts_atomic_read_nob(&q->tail.data.last);
while (1) {
ErtsThrQElement_t *last = (ErtsThrQElement_t *) ilast;
- itmp = erts_atomic_cmpxchg_mb(&last->next.atmc,
+ itmp = erts_atomic_cmpxchg_mb(&last->next,
(erts_aint_t) this,
ERTS_AINT_NULL);
if (itmp == ERTS_AINT_NULL)
@@ -316,31 +321,57 @@ enqueue_managed(ErtsThrQ_t *q, ErtsThrQElement_t *this, int want_last)
/* Move last pointer forward... */
while (1) {
- if (want_last) {
- if (erts_atomic_read_rb(&this->next.atmc) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- ilast = erts_atomic_read_rb(&q->tail.data.last);
- return (ErtsThrQElement_t *) ilast;
- }
- }
- else {
- if (erts_atomic_read_nob(&this->next.atmc) != ERTS_AINT_NULL) {
- /* Someone else will move it forward */
- return NULL;
- }
+ if (erts_atomic_read_rb(&this->next) != ERTS_AINT_NULL) {
+ /* Someone else will move it forward */
+ ilast = erts_atomic_read_rb(&q->tail.data.last);
+ return (ErtsThrQElement_t *) ilast;
}
itmp = erts_atomic_cmpxchg_mb(&q->tail.data.last,
(erts_aint_t) this,
ilast);
if (ilast == itmp)
- return want_last ? this : NULL;
+ return this;
ilast = itmp;
}
}
+static ERTS_INLINE ErtsThrQElement_t *
+enqueue_marker(ErtsThrQ_t *q, ErtsThrQElement_t **headp)
+{
+ int maybe_notify;
+ erts_aint_t inext;
+ ErtsThrQElement_t *last, *head;
+
+ if (headp)
+ head = *headp;
+ else
+ head = ErtsThrQDirtyReadEl(&q->head.head);
+
+ ASSERT(!q->head.used_marker);
+ q->head.used_marker = 1;
+ last = enqueue_managed(q, &q->tail.data.marker);
+ maybe_notify = &q->tail.data.marker == last;
+ inext = erts_atomic_read_acqb(&head->next);
+ if (inext == (erts_aint_t) &q->tail.data.marker) {
+ ErtsThrQDirtySetEl(&q->head.head, &q->tail.data.marker);
+ if (headp)
+ *headp = &q->tail.data.marker;
+ }
+ else if (maybe_notify) {
+ /*
+ * We need to notify; otherwise, we might loose a notification
+ * for a concurrently inserted element.
+ */
+ q->head.notify(q->head.arg);
+ }
+ return last;
+}
+
+
static ErtsThrQCleanState_t
clean(ErtsThrQ_t *q, int max_ops, int do_notify)
{
+ ErtsThrQElement_t *head;
erts_aint_t ilast;
int um_refc_ix;
int ops;
@@ -349,7 +380,8 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify)
ErtsThrQElement_t *tmp;
restart:
ASSERT(q->head.first);
- if (q->head.first == q->head.head.ptr) {
+ head = ErtsThrQDirtyReadEl(&q->head.head);
+ if (q->head.first == head) {
q->head.clean_reached_head_count++;
if (q->head.clean_reached_head_count
>= ERTS_THR_Q_MAX_CLEAN_REACHED_HEAD_COUNT) {
@@ -362,19 +394,20 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify)
break;
if (q->head.first == &q->tail.data.marker) {
q->head.used_marker = 0;
- q->head.first = q->head.first->next.ptr;
+ q->head.first = ErtsThrQDirtyReadEl(&q->head.first->next);
goto restart;
}
tmp = q->head.first;
- q->head.first = q->head.first->next.ptr;
+ q->head.first = ErtsThrQDirtyReadEl(&q->head.first->next);
if (q->head.deq_fini.automatic)
element_free(q, tmp);
else {
tmp->data.ptr = (void *) (UWord) q->head.live;
if (!q->head.deq_fini.start)
q->head.deq_fini.start = tmp;
- else if (q->head.deq_fini.end->next.ptr == &q->tail.data.marker)
- q->head.deq_fini.end->next.ptr = tmp;
+ else if (ErtsThrQDirtyReadEl(&q->head.deq_fini.end->next)
+ == &q->tail.data.marker)
+ ErtsThrQDirtySetEl(&q->head.deq_fini.end->next, tmp);
q->head.deq_fini.end = tmp;
}
}
@@ -401,21 +434,8 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify)
q->head.unref_end = q->head.next.unref_end;
if (!q->head.used_marker
- && q->head.unref_end == (ErtsThrQElement_t *) ilast) {
- q->head.used_marker = 1;
- ilast = (erts_aint_t) enqueue_managed(q,
- &q->tail.data.marker,
- 1);
- if (q->head.head.ptr == q->head.unref_end) {
- ErtsThrQElement_t *next;
- next = ((ErtsThrQElement_t *)
- erts_atomic_read_acqb(&q->head.head.ptr->next.atmc));
- if (next == &q->tail.data.marker) {
- q->head.head.ptr->next.ptr = &q->tail.data.marker;
- q->head.head.ptr = &q->tail.data.marker;
- }
- }
- }
+ && q->head.unref_end == (ErtsThrQElement_t *) ilast)
+ ilast = (erts_aint_t) enqueue_marker(q, NULL);
if (q->head.unref_end == (ErtsThrQElement_t *) ilast)
ERTS_SMP_MEMORY_BARRIER;
@@ -436,20 +456,16 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify)
}
#endif
- if (q->head.first == q->head.head.ptr) {
+ head = ErtsThrQDirtyReadEl(&q->head.head);
+ if (q->head.first == head) {
inspect_head:
if (!q->head.used_marker) {
erts_aint_t inext;
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
+ inext = erts_atomic_read_acqb(&head->next);
if (inext == ERTS_AINT_NULL) {
- q->head.used_marker = 1;
- (void) enqueue_managed(q, &q->tail.data.marker, 0);
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
- if (inext == (erts_aint_t) &q->tail.data.marker) {
- q->head.head.ptr->next.ptr = &q->tail.data.marker;
- q->head.head.ptr = &q->tail.data.marker;
+ enqueue_marker(q, &head);
+ if (head == &q->tail.data.marker)
goto check_thr_progress;
- }
}
}
@@ -506,26 +522,27 @@ erts_thr_q_inspect(ErtsThrQ_t *q, int ensure_empty)
#ifndef USE_THREADS
return ERTS_THR_Q_CLEAN;
#else
+ ErtsThrQElement_t *head = ErtsThrQDirtyReadEl(&q->head.head);
if (ensure_empty) {
erts_aint_t inext;
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
+ inext = erts_atomic_read_acqb(&head->next);
if (inext != ERTS_AINT_NULL) {
if (&q->tail.data.marker != (ErtsThrQElement_t *) inext)
return ERTS_THR_Q_DIRTY;
else {
- q->head.head.ptr->next.ptr = (ErtsThrQElement_t *) inext;
- q->head.head.ptr = (ErtsThrQElement_t *) inext;
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
+ head = (ErtsThrQElement_t *) inext;
+ ErtsThrQDirtySetEl(&q->head.head, head);
+ inext = erts_atomic_read_acqb(&head->next);
if (inext != ERTS_AINT_NULL)
return ERTS_THR_Q_DIRTY;
}
}
}
- if (q->head.first == q->head.head.ptr) {
+ if (q->head.first == head) {
if (!q->head.used_marker) {
erts_aint_t inext;
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
+ inext = erts_atomic_read_acqb(&head->next);
if (inext == ERTS_AINT_NULL)
return ERTS_THR_Q_DIRTY;
}
@@ -553,11 +570,11 @@ enqueue(ErtsThrQ_t *q, void *data, ErtsThrQElement_t *this)
#ifndef USE_THREADS
ASSERT(data);
- this->next.ptr = NULL;
+ this->next = NULL;
this->data.ptr = data;
if (q->last)
- q->last->next.ptr = this;
+ q->last->next = this;
else {
q->first = q->last = this;
q->init.notify(q->init.arg);
@@ -595,7 +612,7 @@ enqueue(ErtsThrQ_t *q, void *data, ErtsThrQElement_t *this)
}
}
- notify = this == enqueue_managed(q, this, 1);
+ notify = this == enqueue_managed(q, this);
#ifdef ERTS_SMP
@@ -638,17 +655,17 @@ erts_thr_q_get_finalize_dequeue_data(ErtsThrQ_t *q, ErtsThrQFinDeQ_t *fdp)
ErtsThrQElement_t *e = q->head.deq_fini.start;
ErtsThrQElement_t *end = q->head.deq_fini.end;
while (e != end) {
- ASSERT(q->head.head.ptr != e);
+ ASSERT(ErtsThrQDirtyReadEl(&q->head.head) != e);
ASSERT(q->head.first != e);
ASSERT(q->head.unref_end != e);
- e = e->next.ptr;
+ e = ErtsThrQDirtyReadEl(&e->next);
}
}
#endif
fdp->start = q->head.deq_fini.start;
fdp->end = q->head.deq_fini.end;
if (fdp->end)
- fdp->end->next.ptr = NULL;
+ ErtsThrQDirtySetEl(&fdp->end->next, NULL);
q->head.deq_fini.start = NULL;
q->head.deq_fini.end = NULL;
return fdp->start != NULL;
@@ -662,7 +679,7 @@ erts_thr_q_append_finalize_dequeue_data(ErtsThrQFinDeQ_t *fdp0,
#ifdef USE_THREADS
if (fdp1->start) {
if (fdp0->end)
- fdp0->end->next.ptr = fdp1->start;
+ ErtsThrQDirtySetEl(&fdp0->end->next, fdp1->start);
else
fdp0->start = fdp1->start;
fdp0->end = fdp1->end;
@@ -683,7 +700,7 @@ int erts_thr_q_finalize_dequeue(ErtsThrQFinDeQ_t *state)
if (!start)
break;
tmp = start;
- start = start->next.ptr;
+ start = ErtsThrQDirtyReadEl(&start->next);
live = (ErtsThrQLive_t) (UWord) tmp->data.ptr;
element_live_free(live, tmp);
}
@@ -724,7 +741,7 @@ erts_thr_q_dequeue(ErtsThrQ_t *q)
return NULL;
tmp = q->first;
res = tmp->data.ptr;
- q->first = tmp->next.ptr;
+ q->first = tmp->next;
if (!q->first)
q->last = NULL;
@@ -732,24 +749,26 @@ erts_thr_q_dequeue(ErtsThrQ_t *q)
return res;
#else
+ ErtsThrQElement_t *head;
erts_aint_t inext;
void *res;
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
+ head = ErtsThrQDirtyReadEl(&q->head.head);
+ inext = erts_atomic_read_acqb(&head->next);
if (inext == ERTS_AINT_NULL)
return NULL;
- q->head.head.ptr->next.ptr = (ErtsThrQElement_t *) inext;
- q->head.head.ptr = (ErtsThrQElement_t *) inext;
- if (q->head.head.ptr == &q->tail.data.marker) {
- inext = erts_atomic_read_acqb(&q->head.head.ptr->next.atmc);
+ head = (ErtsThrQElement_t *) inext;
+ ErtsThrQDirtySetEl(&q->head.head, head);
+ if (head == &q->tail.data.marker) {
+ inext = erts_atomic_read_acqb(&head->next);
if (inext == ERTS_AINT_NULL)
return NULL;
- q->head.head.ptr->next.ptr = (ErtsThrQElement_t *) inext;
- q->head.head.ptr = (ErtsThrQElement_t *) inext;
+ head = (ErtsThrQElement_t *) inext;
+ ErtsThrQDirtySetEl(&q->head.head, head);
}
- res = q->head.head.ptr->data.ptr;
+ res = head->data.ptr;
#if ERTS_THR_Q_DBG_CHK_DATA
- q->head.head.ptr->data.ptr = NULL;
+ head->data.ptr = NULL;
if (!res)
erl_exit(ERTS_ABORT_EXIT, "Missing data in dequeue\n");
#endif
diff --git a/erts/emulator/beam/erl_thr_queue.h b/erts/emulator/beam/erl_thr_queue.h
index edcf2c3823..13af758b3f 100644
--- a/erts/emulator/beam/erl_thr_queue.h
+++ b/erts/emulator/beam/erl_thr_queue.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-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
@@ -76,13 +76,12 @@ typedef struct {
typedef struct ErtsThrQElement_t_ ErtsThrQElement_t;
typedef struct ErtsThrQElement_t ErtsThrQPrepEnQ_t;
-typedef union {
- erts_atomic_t atmc;
- ErtsThrQElement_t *ptr;
-} ErtsThrQPtr_t;
-
struct ErtsThrQElement_t_ {
- ErtsThrQPtr_t next;
+#ifdef USE_THREADS
+ erts_atomic_t next;
+#else
+ ErtsThrQElement_t *next;
+#endif
union {
erts_atomic_t atmc;
void *ptr;
@@ -130,7 +129,7 @@ struct ErtsThrQ_t_ {
* thread dequeuing.
*/
struct {
- ErtsThrQPtr_t head;
+ erts_atomic_t head;
ErtsThrQLive_t live;
ErtsThrQElement_t *first;
ErtsThrQElement_t *unref_end;
diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h
index ee47c98009..759c8f4c33 100644
--- a/erts/emulator/beam/erl_threads.h
+++ b/erts/emulator/beam/erl_threads.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -258,10 +258,6 @@
#include "sys.h"
-typedef struct { SWord sint[2]; } erts_no_dw_atomic_t;
-typedef SWord erts_no_atomic_t;
-typedef Sint32 erts_no_atomic32_t;
-
#ifdef USE_THREADS
#define ETHR_TRY_INLINE_FUNCS
@@ -411,12 +407,15 @@ typedef struct {
typedef int erts_rwmtx_t;
typedef int erts_tsd_key_t;
typedef int erts_tse_t;
-#define erts_dw_aint_t erts_no_dw_atomic_t
-#define erts_dw_atomic_t erts_no_dw_atomic_t
-#define erts_aint_t SWord
-#define erts_atomic_t erts_no_atomic_t
-#define erts_aint32_t Sint32
-#define erts_atomic32_t erts_no_atomic32_t
+
+typedef struct { SWord sint[2]; } erts_dw_aint_t;
+typedef SWord erts_aint_t;
+typedef Sint32 erts_aint32_t;
+
+#define erts_dw_atomic_t erts_dw_aint_t
+#define erts_atomic_t erts_aint_t
+#define erts_atomic32_t erts_aint32_t
+
#if __GNUC__ > 2
typedef struct { } erts_spinlock_t;
typedef struct { } erts_rwlock_t;
@@ -425,6 +424,14 @@ typedef struct { int gcc_is_buggy; } erts_spinlock_t;
typedef struct { int gcc_is_buggy; } erts_rwlock_t;
#endif
+#ifdef WORDS_BIGENDIAN
+#define ERTS_DW_AINT_LOW_WORD 1
+#define ERTS_DW_AINT_HIGH_WORD 0
+#else
+#define ERTS_DW_AINT_LOW_WORD 0
+#define ERTS_DW_AINT_HIGH_WORD 1
+#endif
+
#define ERTS_MTX_INITER 0
#define ERTS_CND_INITER 0
#define ERTS_THR_INIT_DATA_DEF_INITER 0
@@ -433,6 +440,10 @@ typedef struct { int gcc_is_buggy; } erts_rwlock_t;
#endif /* #ifdef USE_THREADS */
+#define erts_no_dw_atomic_t erts_dw_aint_t
+#define erts_no_atomic_t erts_aint_t
+#define erts_no_atomic32_t erts_aint32_t
+
#define ERTS_AINT_NULL ((erts_aint_t) NULL)
#define ERTS_AINT_T_MAX (~(((erts_aint_t) 1) << (sizeof(erts_aint_t)*8-1)))
@@ -522,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);
@@ -542,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,
@@ -601,6 +618,91 @@ 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
+
+ERTS_GLB_INLINE void
+erts_dw_atomic_set_dirty(erts_dw_atomic_t *var, erts_dw_aint_t *val);
+ERTS_GLB_INLINE void
+erts_dw_atomic_read_dirty(erts_dw_atomic_t *var, erts_dw_aint_t *val);
+ERTS_GLB_INLINE void
+erts_atomic_set_dirty(erts_atomic_t *var, erts_aint_t val);
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_dirty(erts_atomic_t *var);
+ERTS_GLB_INLINE void
+erts_atomic32_set_dirty(erts_atomic32_t *var, erts_aint32_t val);
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_dirty(erts_atomic32_t *var);
+
/*
* See "Documentation of atomics and memory barriers" at the top
* of this file for info on atomics.
@@ -643,6 +745,26 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_dw_atomic_read_wb ethr_dw_atomic_read_wb
#define erts_dw_atomic_cmpxchg_wb ethr_dw_atomic_cmpxchg_wb
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_dw_atomic_set_dirty(erts_dw_atomic_t *var, erts_dw_aint_t *val)
+{
+ ethr_sint_t *sint = ethr_dw_atomic_addr(var);
+ sint[0] = val->sint[0];
+ sint[1] = val->sint[1];
+}
+
+ERTS_GLB_INLINE void
+erts_dw_atomic_read_dirty(erts_dw_atomic_t *var, erts_dw_aint_t *val)
+{
+ ethr_sint_t *sint = ethr_dw_atomic_addr(var);
+ val->sint[0] = sint[0];
+ val->sint[1] = sint[1];
+}
+
+#endif
+
/* Word size atomics */
#define erts_atomic_init_nob ethr_atomic_init
@@ -659,6 +781,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
@@ -673,6 +808,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
@@ -687,6 +835,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
@@ -701,6 +862,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
@@ -715,6 +889,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
@@ -729,6 +916,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
@@ -743,6 +943,39 @@ 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
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_atomic_set_dirty(erts_atomic_t *var, erts_aint_t val)
+{
+ ethr_sint_t *sint = ethr_atomic_addr(var);
+ *sint = val;
+}
+
+ERTS_GLB_INLINE erts_aint_t
+erts_atomic_read_dirty(erts_atomic_t *var)
+{
+ ethr_sint_t *sint = ethr_atomic_addr(var);
+ return *sint;
+}
+
+#endif
+
/* 32-bit atomics */
#define erts_atomic32_init_nob ethr_atomic32_init
@@ -759,6 +992,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
@@ -773,6 +1019,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
@@ -787,6 +1046,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
@@ -801,6 +1073,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
@@ -815,6 +1100,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
@@ -829,6 +1127,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
@@ -843,6 +1154,41 @@ 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__
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_atomic32_set_dirty(erts_atomic32_t *var, erts_aint32_t val)
+{
+ ethr_sint32_t *sint = ethr_atomic32_addr(var);
+ *sint = val;
+}
+
+ERTS_GLB_INLINE erts_aint32_t
+erts_atomic32_read_dirty(erts_atomic32_t *var)
+{
+ ethr_sint32_t *sint = ethr_atomic32_addr(var);
+ return *sint;
+}
+
+#endif
+
#else /* !USE_THREADS */
/* Double word size atomics */
@@ -882,6 +1228,9 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig);
#define erts_dw_atomic_read_wb erts_no_dw_atomic_read
#define erts_dw_atomic_cmpxchg_wb erts_no_dw_atomic_cmpxchg
+#define erts_dw_atomic_set_dirty erts_no_dw_atomic_set
+#define erts_dw_atomic_read_dirty erts_no_dw_atomic_read
+
/* Word size atomics */
#define erts_atomic_init_nob erts_no_atomic_set
@@ -897,6 +1246,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
@@ -911,6 +1261,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
@@ -925,6 +1276,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
@@ -939,6 +1291,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
@@ -953,6 +1306,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
@@ -967,6 +1321,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
@@ -981,6 +1336,10 @@ 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
+
+#define erts_atomic_set_dirty erts_no_atomic_set
+#define erts_atomic_read_dirty erts_no_atomic_read
/* 32-bit atomics */
@@ -997,6 +1356,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
@@ -1011,6 +1371,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
@@ -1025,6 +1386,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
@@ -1039,6 +1401,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
@@ -1053,6 +1416,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
@@ -1067,6 +1431,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
@@ -1081,6 +1446,10 @@ 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
+
+#define erts_atomic32_set_dirty erts_no_atomic32_set
+#define erts_atomic32_read_dirty erts_no_atomic32_read
#endif /* !USE_THREADS */
@@ -1845,6 +2214,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
@@ -1932,6 +2312,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.h b/erts/emulator/beam/erl_time.h
index 6c6e193818..4bbdcaa3e3 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -118,9 +118,11 @@ ERTS_GLB_INLINE void erts_do_time_add(erts_short_time_t elapsed)
void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec);
#endif
+typedef UWord erts_approx_time_t;
+erts_approx_time_t erts_get_approx_time(void);
+
void erts_get_timeval(SysTimeval *tv);
erts_time_t erts_get_time(void);
-void erts_get_emu_time(SysTimeval *);
ERTS_GLB_INLINE int erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p);
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index 04147408d5..3272a5326d 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -91,6 +91,41 @@ static SysTimeval then; /* Used in get_now */
static SysTimeval last_emu_time; /* Used in erts_get_emu_time() */
SysTimeval erts_first_emu_time; /* Used in erts_get_emu_time() */
+union {
+ erts_smp_atomic_t time;
+ char align[ERTS_CACHE_LINE_SIZE];
+} approx erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+static void
+init_approx_time(void)
+{
+ erts_smp_atomic_init_nob(&approx.time, 0);
+}
+
+static ERTS_INLINE erts_approx_time_t
+get_approx_time(void)
+{
+ return (erts_approx_time_t) erts_smp_atomic_read_nob(&approx.time);
+}
+
+static ERTS_INLINE void
+update_approx_time(SysTimeval *tv)
+{
+ erts_approx_time_t new_secs = (erts_approx_time_t) tv->tv_sec;
+ erts_approx_time_t old_secs = get_approx_time();
+ if (old_secs != new_secs)
+ erts_smp_atomic_set_nob(&approx.time, new_secs);
+}
+
+/*
+ * erts_get_approx_time() returns an *approximate* time
+ * in seconds. NOTE that this time may jump backwards!!!
+ */
+erts_approx_time_t
+erts_get_approx_time(void)
+{
+ return get_approx_time();
+}
#ifdef HAVE_GETHRTIME
@@ -398,6 +433,8 @@ erts_init_time_sup(void)
{
erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday");
+ init_approx_time();
+
last_emu_time.tv_sec = 0;
last_emu_time.tv_usec = 0;
@@ -417,7 +454,7 @@ erts_init_time_sup(void)
gtv = inittv;
then.tv_sec = then.tv_usec = 0;
- erts_get_emu_time(&erts_first_emu_time);
+ erts_deliver_time();
return CLOCK_RESOLUTION;
}
@@ -883,6 +920,8 @@ get_now(Uint* megasec, Uint* sec, Uint* microsec)
*megasec = (Uint) (now.tv_sec / 1000000);
*sec = (Uint) (now.tv_sec % 1000000);
*microsec = (Uint) (now.tv_usec);
+
+ update_approx_time(&now);
}
void
@@ -895,6 +934,8 @@ get_sys_now(Uint* megasec, Uint* sec, Uint* microsec)
*megasec = (Uint) (now.tv_sec / 1000000);
*sec = (Uint) (now.tv_sec % 1000000);
*microsec = (Uint) (now.tv_usec);
+
+ update_approx_time(&now);
}
@@ -911,6 +952,8 @@ void erts_deliver_time(void) {
do_erts_deliver_time(&now);
erts_smp_mtx_unlock(&erts_timeofday_mtx);
+
+ update_approx_time(&now);
}
/* get *real* time (not ticks) remaining until next timeout - if there
@@ -959,6 +1002,7 @@ void erts_get_timeval(SysTimeval *tv)
erts_smp_mtx_lock(&erts_timeofday_mtx);
get_tolerant_timeofday(tv);
erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ update_approx_time(tv);
}
erts_time_t
@@ -971,7 +1015,9 @@ erts_get_time(void)
get_tolerant_timeofday(&sys_tv);
erts_smp_mtx_unlock(&erts_timeofday_mtx);
-
+
+ update_approx_time(&sys_tv);
+
return sys_tv.tv_sec;
}
@@ -987,38 +1033,3 @@ void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) {
*sec = (Uint)(tp.tv_sec % 1000000);
}
#endif
-
-
-/*
- * erts_get_emu_time() is similar to get_now(). You will
- * always get different times from erts_get_emu_time(), but they
- * may equal a time from get_now().
- *
- * erts_get_emu_time() is only used internally in the emulator in
- * order to order emulator internal events.
- */
-
-void
-erts_get_emu_time(SysTimeval *this_emu_time_p)
-{
- erts_smp_mtx_lock(&erts_timeofday_mtx);
-
- get_tolerant_timeofday(this_emu_time_p);
-
- /* Make sure time is later than last */
- if (last_emu_time.tv_sec > this_emu_time_p->tv_sec ||
- (last_emu_time.tv_sec == this_emu_time_p->tv_sec
- && last_emu_time.tv_usec >= this_emu_time_p->tv_usec)) {
- *this_emu_time_p = last_emu_time;
- this_emu_time_p->tv_usec++;
- }
- /* Check for carry from above + general reasonability */
- if (this_emu_time_p->tv_usec >= 1000000) {
- this_emu_time_p->tv_usec = 0;
- this_emu_time_p->tv_sec++;
- }
-
- last_emu_time = *this_emu_time_p;
-
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
-}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 009ca1eb52..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. */
@@ -64,7 +64,7 @@ int erts_cpu_timestamp;
#endif
static erts_smp_mtx_t smq_mtx;
-static erts_smp_mtx_t sys_trace_mtx;
+static erts_smp_rwmtx_t sys_trace_rwmtx;
enum ErtsSysMsgType {
SYS_MSG_TYPE_UNDEFINED,
@@ -91,7 +91,12 @@ static void init_sys_msg_dispatcher(void);
#endif
void erts_init_trace(void) {
- erts_smp_mtx_init(&sys_trace_mtx, "sys_tracers");
+ 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(&sys_trace_rwmtx, &rwmtx_opts, "sys_tracers");
+
#ifdef HAVE_ERTS_NOW_CPU
erts_cpu_timestamp = 0;
#endif
@@ -151,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) = process_tab[internal_pid_index((TPID))]; \
- if (INVALID_PID((RES), (TPID)) || !((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; \
@@ -169,10 +174,10 @@ erts_system_profile_setup_active_schedulers(void)
active_sched = erts_active_schedulers();
}
-void
-erts_trace_check_exiting(Eterm exiting)
+static void
+exiting_reset(Eterm exiting)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
if (exiting == default_tracer) {
default_tracer = NIL;
default_trace_flags &= TRACEE_FLAGS;
@@ -202,29 +207,49 @@ erts_trace_check_exiting(Eterm exiting)
erts_system_profile_clear(NULL);
#endif
}
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
+}
+
+void
+erts_trace_check_exiting(Eterm exiting)
+{
+ int reset = 0;
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
+ if (exiting == default_tracer)
+ reset = 1;
+ else if (exiting == system_seq_tracer)
+ reset = 1;
+ else if (exiting == system_monitor)
+ reset = 1;
+ else if (exiting == system_profile)
+ reset = 1;
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
+ if (reset)
+ exiting_reset(exiting);
+}
+
+static ERTS_INLINE int
+is_valid_tracer(Eterm tracer)
+{
+ return erts_proc_lookup(tracer) || erts_is_valid_tracer_port(tracer);
}
Eterm
erts_set_system_seq_tracer(Process *c_p, ErtsProcLocks c_p_locks, Eterm new)
{
- Eterm old = THE_NON_VALUE;
+ Eterm old;
- if (new != am_false) {
- if (!erts_pid2proc(c_p, c_p_locks, new, 0)
- && !erts_is_valid_tracer_port(new)) {
- return old;
- }
- }
+ if (new != am_false && !is_valid_tracer(new))
+ return THE_NON_VALUE;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
old = system_seq_tracer;
system_seq_tracer = new;
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "set seq tracer new=%T old=%T\n", new, old);
#endif
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
return old;
}
@@ -232,12 +257,12 @@ Eterm
erts_get_system_seq_tracer(void)
{
Eterm st;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
st = system_seq_tracer;
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "get seq tracer %T\n", st);
#endif
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
return st;
}
@@ -250,7 +275,7 @@ get_default_tracing(Uint *flagsp, Eterm *tracerp)
if (is_nil(default_tracer)) {
default_trace_flags &= ~TRACEE_FLAGS;
} else if (is_internal_pid(default_tracer)) {
- if (!erts_pid2proc(NULL, 0, default_tracer, 0)) {
+ if (!erts_proc_lookup(default_tracer)) {
reset_tracer:
default_trace_flags &= ~TRACEE_FLAGS;
default_tracer = NIL;
@@ -270,7 +295,7 @@ get_default_tracing(Uint *flagsp, Eterm *tracerp)
void
erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
if (flagsp) {
if (setflags)
default_trace_flags |= *flagsp;
@@ -280,48 +305,48 @@ erts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp)
if (tracerp)
default_tracer = *tracerp;
get_default_tracing(flagsp, tracerp);
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
void
erts_get_default_tracing(Uint *flagsp, Eterm *tracerp)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
get_default_tracing(flagsp, tracerp);
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
}
void
erts_set_system_monitor(Eterm monitor)
{
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
system_monitor = monitor;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
Eterm
erts_get_system_monitor(void)
{
Eterm monitor;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
monitor = system_monitor;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
return monitor;
}
/* Performance monitoring */
void erts_set_system_profile(Eterm profile) {
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
system_profile = profile;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
}
Eterm
erts_get_system_profile(void) {
Eterm profile;
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rlock(&sys_trace_rwmtx);
profile = system_profile;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_runlock(&sys_trace_rwmtx);
return profile;
}
@@ -384,13 +409,9 @@ 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);
-#ifndef ERTS_SMP
- erts_port_release(trace_port);
- }
-#endif
erts_free(ERTS_ALC_T_TMP, (void *) buffer);
}
@@ -420,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,
@@ -430,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,
@@ -465,13 +486,13 @@ send_to_port(Process *c_p, Eterm message,
trace_port = NULL;
#else
- if (is_not_internal_port(*tracer_pid))
- goto invalid_tracer_port;
- trace_port = &erts_port[internal_port_index(*tracer_pid)];
+ trace_port = erts_id2port_sflgs(*tracer_pid,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
- if (INVALID_TRACER_PORT(trace_port, *tracer_pid)) {
- invalid_tracer_port:
+ if (!trace_port) {
*tracee_flags &= ~TRACEE_FLAGS;
*tracer_pid = NIL;
return;
@@ -487,10 +508,11 @@ 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
+ erts_port_release(trace_port);
return;
}
@@ -521,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);
@@ -535,8 +557,11 @@ 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);
+
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
#endif
@@ -566,23 +591,27 @@ profile_send(Eterm from, Eterm message) {
Port *profiler_port = NULL;
/* not smp */
-
-
- profiler_port = &erts_port[internal_port_index(profiler)];
-
- do_send_to_port(profiler,
- profiler_port,
- NIL, /* or current process->id */
- SYS_MSG_TYPE_SYSPROF,
- message);
+
+ profiler_port = erts_id2port_sflgs(profiler,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ if (profiler_port) {
+ do_send_to_port(profiler,
+ profiler_port,
+ 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 = process_tab[internal_pid_index(profiler)];
+ profile_p = erts_proc_lookup(profiler);
- if (INVALID_PID(profile_p, profiler)) return;
+ if (!profile_p)
+ return;
sz = size_object(message);
hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0);
@@ -626,13 +655,11 @@ seq_trace_send_to_port(Process *c_p,
trace_port = NULL;
#else
- if (is_not_internal_port(seq_tracer))
- goto invalid_tracer_port;
-
- trace_port = &erts_port[internal_port_index(seq_tracer)];
-
- if (INVALID_TRACER_PORT(trace_port, seq_tracer)) {
- invalid_tracer_port:
+ trace_port = erts_id2port_sflgs(seq_tracer,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ if (!trace_port) {
system_seq_tracer = am_false;
#ifndef ERTS_SMP
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
@@ -645,11 +672,12 @@ 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);
#ifndef ERTS_SMP
+ erts_port_release(trace_port);
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
return;
}
@@ -675,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);
@@ -689,15 +717,20 @@ 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);
+
UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
#undef LOCAL_HEAP_SIZE
#endif
}
#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
@@ -732,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);
@@ -760,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;
@@ -780,22 +813,18 @@ 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)
-#ifndef ERTS_SMP
- || p->status == P_FREE
-#endif
- ) {
+ if (ERTS_PROC_IS_EXITING(p))
curr_func = 0;
- }
else {
if (!p->current)
p->current = find_function_from_pc(p->i);
@@ -824,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 {
@@ -833,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;
}
@@ -874,7 +903,7 @@ trace_send(Process *p, Eterm to, Eterm msg)
operation = am_send;
if (is_internal_pid(to)) {
- if (!erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, to, 0))
+ if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
}
else if(is_external_pid(to)
@@ -885,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);
@@ -907,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);
@@ -926,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);
}
}
@@ -950,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);
@@ -972,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);
@@ -984,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);
}
}
@@ -1003,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) =
@@ -1047,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;
}
@@ -1111,13 +1142,12 @@ 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
- tracer = process_tab[internal_pid_index(seq_tracer)];
- if (INVALID_PID(tracer, tracer->id)) {
+ tracer = erts_proc_lookup(seq_tracer);
+ if (!tracer) {
system_seq_tracer = am_false;
return; /* no need to send anything */
}
@@ -1226,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;
@@ -1246,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);
@@ -1259,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
@@ -1288,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 =>
@@ -1335,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) {
@@ -1355,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);
@@ -1378,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);
@@ -1419,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;
@@ -1465,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);
@@ -1487,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);
@@ -1512,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;
@@ -1566,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 =>
@@ -1592,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;
}
@@ -1650,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
@@ -1752,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);
@@ -1787,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
@@ -1925,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);
@@ -1963,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(
@@ -1984,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);
@@ -1995,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);
@@ -2007,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);
}
}
@@ -2037,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);
@@ -2045,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);
@@ -2063,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);
@@ -2078,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);
}
}
@@ -2107,187 +2140,6 @@ void save_calls(Process *p, Export *e)
}
}
-/*
- * Entry point called by the trace wrap functions in erl_bif_wrap.c
- *
- * The trace wrap functions are themselves called through the export
- * entries instead of the original BIF functions.
- */
-Eterm
-erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
-{
- Eterm result;
- int meta = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_META);
-
- ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
-
- if (!ARE_TRACE_FLAGS_ON(p, F_TRACE_CALLS) && (! meta)) {
- /* Warning! This is an Optimization.
- *
- * If neither meta trace is active nor process trace flags then
- * no tracing will occur. Doing the whole else branch will
- * also do nothing, only slower.
- */
- Eterm (*func)(Process*, Eterm*, BeamInstr*) = bif_table[bif_index].f;
- result = func(p, args, I);
- } else {
- Eterm (*func)(Process*, Eterm*, BeamInstr*);
- Export* ep = bif_export[bif_index];
- Uint32 flags = 0, flags_meta = 0;
- int global = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_GLOBAL);
- int local = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_LOCAL);
- int time = !!(erts_bif_trace_flags[bif_index] & BIF_TRACE_AS_CALL_TIME);
- Eterm meta_tracer_pid = NIL;
- int applying = (I == &(ep->code[3])); /* Yup, the apply code for a bif
- * is actually in the
- * export entry */
- BeamInstr *cp = p->cp;
-
- /*
- * Make continuation pointer OK, it is not during direct BIF calls,
- * but it is correct during apply of bif.
- */
- if (!applying) {
- p->cp = I;
- }
- if (global || local) {
- flags = erts_call_trace(p, ep->code, ep->match_prog_set, args,
- local, &p->tracer_proc);
- }
- if (meta) {
- flags_meta = erts_bif_mtrace(p, ep->code+3, args, local,
- &meta_tracer_pid);
- }
- if (time) {
- BpDataTime *bdt = NULL;
- BeamInstr *pc = (BeamInstr *)ep->code+3;
-
- bdt = (BpDataTime *) erts_get_time_break(p, pc);
- ASSERT(bdt);
-
- if (!bdt->pause) {
- erts_trace_time_break(p, pc, bdt, ERTS_BP_CALL_TIME_CALL);
- }
- }
- /* Restore original continuation pointer (if changed). */
- p->cp = cp;
-
- func = bif_table[bif_index].f;
-
- result = func(p, args, I);
-
- if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
- BeamInstr i_return_time_trace = beam_return_time_trace[0];
- Eterm *cpp;
- /* Maybe advance cp to skip trace stack frames */
- for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
- if (*cp == i_return_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 2; /* Skip return_trace parameters */
- } else if (*cp == i_return_time_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 1; /* Skip return_time_trace parameters */
- } else if (*cp == i_return_to_trace) {
- /* A return_to trace message is going to be generated
- * by normal means, so we do not have to.
- */
- cp = NULL;
- break;
- } else break;
- }
- }
-
- /* Try to get these in the order
- * they usually appear in normal code... */
- if (is_non_value(result)) {
- Uint reason = p->freason;
- if (reason != TRAP) {
- Eterm class;
- Eterm value = p->fvalue;
- DeclareTmpHeapNoproc(nocatch,3);
- UseTmpHeapNoproc(3);
- /* Expand error value like in handle_error() */
- if (reason & EXF_ARGLIST) {
- Eterm *tp;
- ASSERT(is_tuple(value));
- tp = tuple_val(value);
- value = tp[1];
- }
- if ((reason & EXF_THROWN) && (p->catches <= 0)) {
- value = TUPLE2(nocatch, am_nocatch, value);
- reason = EXC_ERROR;
- }
- /* Note: expand_error_value() could theoretically
- * allocate on the heap, but not for any error
- * returned by a BIF, and it would do no harm,
- * just be annoying.
- */
- value = expand_error_value(p, reason, value);
- class = exception_tag[GET_EXC_CLASS(reason)];
-
- if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, ep->code, class, value,
- &meta_tracer_pid);
- }
- if (flags & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, ep->code, class, value,
- &p->tracer_proc);
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
- /* can only happen if(local)*/
- Eterm *ptr = p->stop;
- ASSERT(is_CP(*ptr));
- ASSERT(ptr <= STACK_START(p));
- /* Search the nearest stack frame for a catch */
- while (++ptr < STACK_START(p)) {
- if (is_CP(*ptr)) break;
- if (is_catch(*ptr)) {
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into
- * calling function */
- erts_trace_return_to(p, 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_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- } else {
- if (flags_meta & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, ep->code, result, &meta_tracer_pid);
- }
- /* 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);
- }
- if (flags & MATCH_SET_RETURN_TO_TRACE) {
- /* can only happen if(local)*/
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- }
- ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- return result;
-}
-
/* Sends trace message:
* {trace_ts, Pid, What, Msg, Timestamp}
* or {trace, Pid, What, Msg}
@@ -2358,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;
@@ -2370,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,
@@ -2397,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
@@ -2449,12 +2302,10 @@ 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);
- monitor_p = process_tab[internal_pid_index(system_monitor)];
- if (INVALID_PID(monitor_p, system_monitor) || p == monitor_p) {
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p || p == monitor_p)
return;
- }
#endif
hsz = 0;
@@ -2476,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 */;
@@ -2484,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
@@ -2525,10 +2376,9 @@ monitor_large_heap(Process *p) {
#ifndef ERTS_SMP
- ASSERT(is_internal_pid(system_monitor)
- && internal_pid_index(system_monitor) < erts_max_processes);
- monitor_p = process_tab[internal_pid_index(system_monitor)];
- if (INVALID_PID(monitor_p, system_monitor) || p == monitor_p) {
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (monitor_p || p == monitor_p) {
return;
}
#endif
@@ -2552,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 */;
@@ -2560,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
@@ -2580,21 +2430,19 @@ 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);
- monitor_p = process_tab[internal_pid_index(system_monitor)];
- if (INVALID_PID(monitor_p, system_monitor) || p == monitor_p) {
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p || p == monitor_p)
return;
- }
#endif
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
@@ -2716,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);
@@ -2740,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);
}
@@ -2779,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);
@@ -2802,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);
}
}
@@ -2845,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);
@@ -2860,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);
@@ -2883,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);
@@ -2900,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);
}
}
@@ -2948,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);
}
@@ -3002,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);
}
@@ -3021,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);
}
}
@@ -3149,10 +3003,10 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver)
break;
case SYS_MSG_TYPE_SEQTRACE:
/* Reset seq_tracer if it hasn't changed */
- erts_smp_mtx_lock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwlock(&sys_trace_rwmtx);
if (system_seq_tracer == receiver)
system_seq_tracer = am_false;
- erts_smp_mtx_unlock(&sys_trace_mtx);
+ erts_smp_rwmtx_rwunlock(&sys_trace_rwmtx);
break;
case SYS_MSG_TYPE_SYSMON:
if (receiver == NIL
@@ -3374,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)
@@ -3401,18 +3255,16 @@ 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(receiver, NULL, 0);
- if (INVALID_TRACER_PORT(port, receiver)) {
- if (port)
- erts_port_release(port);
+ port = erts_thr_id2port_sflgs(receiver,
+ ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP);
+ if (!port)
goto failure;
- }
else {
write_sys_msg_to_port(receiver,
port,
@@ -3424,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 26ac231ddb..fa53fd0937 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -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
@@ -77,66 +77,25 @@ void erts_init_unicode(void)
{
max_loop_limit = CONTEXT_REDS * LOOP_FACTOR;
/* Non visual BIFs to trap to. */
- memset(&characters_to_utf8_trap_exp, 0, sizeof(Export));
- characters_to_utf8_trap_exp.address =
- &characters_to_utf8_trap_exp.code[3];
- characters_to_utf8_trap_exp.code[0] = am_erlang;
- characters_to_utf8_trap_exp.code[1] =
- am_atom_put("characters_to_utf8_trap",23);
- characters_to_utf8_trap_exp.code[2] = 3;
- characters_to_utf8_trap_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_utf8_trap_exp.code[4] =
- (BeamInstr) &characters_to_utf8_trap;
-
- memset(&characters_to_list_trap_1_exp, 0, sizeof(Export));
- characters_to_list_trap_1_exp.address =
- &characters_to_list_trap_1_exp.code[3];
- characters_to_list_trap_1_exp.code[0] = am_erlang;
- characters_to_list_trap_1_exp.code[1] =
- am_atom_put("characters_to_list_trap_1",25);
- characters_to_list_trap_1_exp.code[2] = 3;
- characters_to_list_trap_1_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_1_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_1;
-
- memset(&characters_to_list_trap_2_exp, 0, sizeof(Export));
- characters_to_list_trap_2_exp.address =
- &characters_to_list_trap_2_exp.code[3];
- characters_to_list_trap_2_exp.code[0] = am_erlang;
- characters_to_list_trap_2_exp.code[1] =
- am_atom_put("characters_to_list_trap_2",25);
- characters_to_list_trap_2_exp.code[2] = 3;
- characters_to_list_trap_2_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_2_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_2;
-
-
- memset(&characters_to_list_trap_3_exp, 0, sizeof(Export));
- characters_to_list_trap_3_exp.address =
- &characters_to_list_trap_3_exp.code[3];
- characters_to_list_trap_3_exp.code[0] = am_erlang;
- characters_to_list_trap_3_exp.code[1] =
- am_atom_put("characters_to_list_trap_3",25);
- characters_to_list_trap_3_exp.code[2] = 3;
- characters_to_list_trap_3_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_3_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_3;
-
- memset(&characters_to_list_trap_4_exp, 0, sizeof(Export));
- characters_to_list_trap_4_exp.address =
- &characters_to_list_trap_4_exp.code[3];
- characters_to_list_trap_4_exp.code[0] = am_erlang;
- characters_to_list_trap_4_exp.code[1] =
- am_atom_put("characters_to_list_trap_4",25);
- characters_to_list_trap_4_exp.code[2] = 1;
- characters_to_list_trap_4_exp.code[3] =
- (BeamInstr) em_apply_bif;
- characters_to_list_trap_4_exp.code[4] =
- (BeamInstr) &characters_to_list_trap_4;
+ erts_init_trap_export(&characters_to_utf8_trap_exp,
+ am_erlang, am_atom_put("characters_to_utf8_trap",23), 3,
+ &characters_to_utf8_trap);
+
+ erts_init_trap_export(&characters_to_list_trap_1_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_1",25), 3,
+ &characters_to_list_trap_1);
+
+ erts_init_trap_export(&characters_to_list_trap_2_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_2",25), 3,
+ &characters_to_list_trap_2);
+
+ erts_init_trap_export(&characters_to_list_trap_3_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_3",25), 3,
+ &characters_to_list_trap_3);
+
+ erts_init_trap_export(&characters_to_list_trap_4_exp,
+ am_erlang, am_atom_put("characters_to_list_trap_4",25), 1,
+ &characters_to_list_trap_4);
c_to_b_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_binary_int,2);
c_to_l_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_list_int,2);
@@ -1195,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;
@@ -1212,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) {
@@ -1229,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;
@@ -1246,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)
@@ -1316,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;
@@ -1853,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);
@@ -1885,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, ERTS_ATOM_ENC_LATIN1)) {
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, ERTS_ATOM_ENC_UTF8)) {
+ 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);
}
}
@@ -2619,8 +2573,20 @@ BIF_RETTYPE prim_file_internal_native2name_1(BIF_ALIST_1)
case ERL_FILENAME_UTF8:
bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc);
if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
+ Eterm *hp = HAlloc(BIF_P,3);
+ Eterm warn_type = NIL;
erts_free_aligned_binary_bytes(temp_alloc);
- goto noconvert;
+ switch (erts_get_filename_warning_type()) {
+ case ERL_FILENAME_WARNING_IGNORE:
+ warn_type = am_ignore;
+ break;
+ case ERL_FILENAME_WARNING_ERROR:
+ warn_type = am_error;
+ break;
+ default:
+ warn_type = am_warning;
+ }
+ BIF_RET(TUPLE2(hp,am_error,warn_type));
}
num_built = 0;
num_eaten = 0;
@@ -2653,9 +2619,8 @@ BIF_RETTYPE prim_file_internal_native2name_1(BIF_ALIST_1)
erts_free_aligned_binary_bytes(temp_alloc);
BIF_RET(ret);
default:
- goto noconvert;
+ break;
}
- noconvert:
BIF_RET(BIF_ARG_1);
}
@@ -2692,6 +2657,52 @@ BIF_RETTYPE prim_file_internal_normalize_utf8_1(BIF_ALIST_1)
BIF_RET(ret);
}
+BIF_RETTYPE prim_file_is_translatable_1(BIF_ALIST_1)
+{
+ ERTS_DECLARE_DUMMY(Eterm real_bin);
+ ERTS_DECLARE_DUMMY(Uint offset);
+ Uint size;
+ Uint num_chars;
+ Uint bitsize;
+ ERTS_DECLARE_DUMMY(Uint bitoffs);
+ byte *temp_alloc = NULL;
+ byte *bytes;
+ byte *err_pos;
+ int status;
+
+ if (is_not_binary(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ size = binary_size(BIF_ARG_1);
+ ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, offset, bitoffs, bitsize);
+ if (bitsize != 0) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ if (size == 0) {
+ BIF_RET(am_true);
+ }
+
+ /*
+ * If the encoding is latin1, the pathname is always translatable.
+ */
+ switch (erts_get_native_filename_encoding()) {
+ case ERL_FILENAME_LATIN1:
+ BIF_RET(am_true);
+ case ERL_FILENAME_WIN_WCHAR:
+ if (erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) {
+ BIF_RET(am_true);
+ }
+ }
+
+ /*
+ * Check whether the binary contains legal UTF-8 sequences.
+ */
+ bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc);
+ status = erts_analyze_utf8(bytes, size, &err_pos, &num_chars, NULL);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_RET(status == ERTS_UTF8_OK ? am_true : am_false);
+}
+
BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0)
{
switch (erts_get_native_filename_encoding()) {
@@ -2711,3 +2722,36 @@ BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0)
}
}
+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;
+}
+
+BIF_RETTYPE io_printable_range_0(BIF_ALIST_0)
+{
+ if (erts_get_printable_characters() == ERL_PRINTABLE_CHARACTERS_UNICODE) {
+ BIF_RET(am_unicode);
+ } else {
+ BIF_RET(am_latin1);
+ }
+}
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/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
index bdfde58845..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.
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index fb0ee99119..b0f08d8245 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -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
@@ -32,98 +32,162 @@
#define EXPORT_HASH(m,f,a) ((m)*(f)+(a))
-static IndexTable export_table; /* Not locked. */
-static Hash secondary_export_table; /* Locked. */
+#ifdef DEBUG
+# define IF_DEBUG(x) x
+#else
+# define IF_DEBUG(x)
+#endif
-#include "erl_smp.h"
+static IndexTable export_tables[ERTS_NUM_CODE_IX]; /* Active not locked */
-static erts_smp_rwmtx_t export_table_lock; /* Locks the secondary export table. */
+static erts_smp_atomic_t total_entries_bytes;
+
+#include "erl_smp.h"
-#define export_read_lock() erts_smp_rwmtx_rlock(&export_table_lock)
-#define export_read_unlock() erts_smp_rwmtx_runlock(&export_table_lock)
-#define export_write_lock() erts_smp_rwmtx_rwlock(&export_table_lock)
-#define export_write_unlock() erts_smp_rwmtx_rwunlock(&export_table_lock)
+/* This lock protects the staging export table from concurrent access
+ * AND it protects the staging table from becoming active.
+ */
+erts_smp_mtx_t export_staging_lock;
extern BeamInstr* em_call_error_handler;
extern BeamInstr* em_call_traced_function;
+struct export_entry
+{
+ IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */
+ Export* ep;
+};
+
+/* Helper struct that brings things together in one allocation
+*/
+struct export_blob
+{
+ Export exp;
+ struct export_entry entryv[ERTS_NUM_CODE_IX];
+ /* Note that entryv is not indexed by "code_ix".
+ */
+};
+
+/* Helper struct only used as template
+*/
+struct export_templ
+{
+ struct export_entry entry;
+ Export exp;
+};
+
+static struct export_blob* entry_to_blob(struct export_entry* ee)
+{
+ return (struct export_blob*)
+ ((char*)ee->ep - offsetof(struct export_blob,exp));
+}
+
void
export_info(int to, void *to_arg)
{
#ifdef ERTS_SMP
int lock = !ERTS_IS_CRASH_DUMPING;
if (lock)
- export_read_lock();
+ export_staging_lock();
#endif
- index_info(to, to_arg, &export_table);
- hash_info(to, to_arg, &secondary_export_table);
+ index_info(to, to_arg, &export_tables[erts_active_code_ix()]);
+ hash_info(to, to_arg, &export_tables[erts_staging_code_ix()].htable);
#ifdef ERTS_SMP
if (lock)
- export_read_unlock();
+ export_staging_unlock();
#endif
}
static HashValue
-export_hash(Export* x)
+export_hash(struct export_entry* ee)
{
+ Export* x = ee->ep;
return EXPORT_HASH(x->code[0], x->code[1], x->code[2]);
}
static int
-export_cmp(Export* tmpl, Export* obj)
+export_cmp(struct export_entry* tmpl_e, struct export_entry* obj_e)
{
+ Export* tmpl = tmpl_e->ep;
+ Export* obj = obj_e->ep;
return !(tmpl->code[0] == obj->code[0] &&
tmpl->code[1] == obj->code[1] &&
tmpl->code[2] == obj->code[2]);
}
-static Export*
-export_alloc(Export* tmpl)
+static struct export_entry*
+export_alloc(struct export_entry* tmpl_e)
{
- Export* obj = (Export*) erts_alloc(ERTS_ALC_T_EXPORT, sizeof(Export));
-
- obj->fake_op_func_info_for_hipe[0] = 0;
- obj->fake_op_func_info_for_hipe[1] = 0;
- obj->code[0] = tmpl->code[0];
- obj->code[1] = tmpl->code[1];
- obj->code[2] = tmpl->code[2];
- obj->slot.index = -1;
- obj->address = obj->code+3;
- obj->code[3] = (BeamInstr) em_call_error_handler;
- obj->code[4] = 0;
- obj->match_prog_set = NULL;
- return obj;
+ struct export_blob* blob;
+ unsigned ix;
+
+ if (tmpl_e->slot.index == -1) { /* Template, allocate blob */
+ Export* tmpl = tmpl_e->ep;
+ Export* obj;
+
+ blob = (struct export_blob*) erts_alloc(ERTS_ALC_T_EXPORT, sizeof(*blob));
+ erts_smp_atomic_add_nob(&total_entries_bytes, sizeof(*blob));
+ obj = &blob->exp;
+ obj->fake_op_func_info_for_hipe[0] = 0;
+ obj->fake_op_func_info_for_hipe[1] = 0;
+ obj->code[0] = tmpl->code[0];
+ obj->code[1] = tmpl->code[1];
+ obj->code[2] = tmpl->code[2];
+ obj->code[3] = (BeamInstr) em_call_error_handler;
+ obj->code[4] = 0;
+
+ for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) {
+ obj->addressv[ix] = obj->code+3;
+
+ blob->entryv[ix].slot.index = -1;
+ blob->entryv[ix].ep = &blob->exp;
+ }
+ ix = 0;
+ }
+ else { /* Existing entry in another table, use free entry in blob */
+ blob = entry_to_blob(tmpl_e);
+ for (ix = 0; blob->entryv[ix].slot.index >= 0; ix++) {
+ ASSERT(ix < ERTS_NUM_CODE_IX);
+ }
+ }
+ return &blob->entryv[ix];
}
-
-static void
-export_free(Export* obj)
+static void
+export_free(struct export_entry* obj)
{
- erts_free(ERTS_ALC_T_EXPORT, (void*) obj);
+ struct export_blob* blob = entry_to_blob(obj);
+ int i;
+ obj->slot.index = -1;
+ for (i=0; i < ERTS_NUM_CODE_IX; i++) {
+ if (blob->entryv[i].slot.index >= 0) {
+ return;
+ }
+ }
+ erts_free(ERTS_ALC_T_EXPORT, blob);
+ erts_smp_atomic_add_nob(&total_entries_bytes, -sizeof(*blob));
}
-
void
init_export_table(void)
{
HashFunctions f;
- erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
- rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
- rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
+ int i;
- erts_smp_rwmtx_init_opt(&export_table_lock, &rwmtx_opt, "export_tab");
+ erts_smp_mtx_init(&export_staging_lock, "export_tab");
+ erts_smp_atomic_init_nob(&total_entries_bytes, 0);
f.hash = (H_FUN) export_hash;
f.cmp = (HCMP_FUN) export_cmp;
f.alloc = (HALLOC_FUN) export_alloc;
f.free = (HFREE_FUN) export_free;
- erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_table, "export_list",
- EXPORT_INITIAL_SIZE, EXPORT_LIMIT, f);
- hash_init(ERTS_ALC_T_EXPORT_TABLE, &secondary_export_table,
- "secondary_export_table", 50, f);
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ erts_index_init(ERTS_ALC_T_EXPORT_TABLE, &export_tables[i], "export_list",
+ EXPORT_INITIAL_SIZE, EXPORT_LIMIT, f);
+ }
}
/*
@@ -138,29 +202,42 @@ init_export_table(void)
* called through such an export entry.
* 3) This function is suitable for the implementation of erlang:apply/3.
*/
+extern Export* /* inline-helper */
+erts_find_export_entry(Eterm m, Eterm f, unsigned int a,ErtsCodeIndex code_ix);
Export*
-erts_find_export_entry(Eterm m, Eterm f, unsigned int a)
+erts_find_export_entry(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
HashValue hval = EXPORT_HASH((BeamInstr) m, (BeamInstr) f, (BeamInstr) a);
int ix;
HashBucket* b;
- ix = hval % export_table.htable.size;
- b = export_table.htable.bucket[ix];
+ ix = hval % export_tables[code_ix].htable.size;
+ b = export_tables[code_ix].htable.bucket[ix];
/*
* Note: We have inlined the code from hash.c for speed.
*/
while (b != (HashBucket*) 0) {
- Export* ep = (Export *) b;
+ Export* ep = ((struct export_entry*) b)->ep;
if (ep->code[0] == m && ep->code[1] == f && ep->code[2] == a) {
- break;
+ return ep;
}
b = b->next;
}
- return (Export*)b;
+ return NULL;
+}
+
+static struct export_entry* init_template(struct export_templ* templ,
+ Eterm m, Eterm f, unsigned a)
+{
+ templ->entry.ep = &templ->exp;
+ templ->entry.slot.index = -1;
+ templ->exp.code[0] = m;
+ templ->exp.code[1] = f;
+ templ->exp.code[2] = a;
+ return &templ->entry;
}
@@ -176,47 +253,42 @@ erts_find_export_entry(Eterm m, Eterm f, unsigned int a)
*/
Export*
-erts_find_function(Eterm m, Eterm f, unsigned int a)
+erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
- Export e;
- Export* ep;
-
- e.code[0] = m;
- e.code[1] = f;
- e.code[2] = a;
-
- ep = hash_get(&export_table.htable, (void*) &e);
- if (ep != NULL && ep->address == ep->code+3 &&
- ep->code[3] != (BeamInstr) em_call_traced_function) {
- ep = NULL;
+ struct export_templ templ;
+ struct export_entry* ee;
+
+ ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
+ if (ee == NULL ||
+ (ee->ep->addressv[code_ix] == ee->ep->code+3 &&
+ ee->ep->code[3] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) {
+ return NULL;
}
- return ep;
+ return ee->ep;
}
/*
* Returns a pointer to an existing export entry for a MFA,
* or creates a new one and returns the pointer.
*
- * This function provides unlocked write access to the main export
- * table. It should only be used during start up or when
- * all other threads are blocked.
+ * This function acts on the staging export table. It should only be used
+ * to load new code.
*/
Export*
erts_export_put(Eterm mod, Eterm func, unsigned int arity)
{
- Export e;
- int ix;
+ ErtsCodeIndex code_ix = erts_staging_code_ix();
+ struct export_templ templ;
+ struct export_entry* ee;
- ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_smp_thr_progress_is_blocking());
ASSERT(is_atom(mod));
ASSERT(is_atom(func));
- e.code[0] = mod;
- e.code[1] = func;
- e.code[2] = arity;
- ix = index_put(&export_table, (void*) &e);
- return (Export*) erts_index_lookup(&export_table, ix);
+ export_staging_lock();
+ ee = (struct export_entry*) index_put_entry(&export_tables[code_ix],
+ init_template(&templ, mod, func, arity));
+ export_staging_unlock();
+ return ee->ep;
}
/*
@@ -224,77 +296,122 @@ erts_export_put(Eterm mod, Eterm func, unsigned int arity)
* export entry (making a call through it will cause the error_handler to
* be called).
*
- * Stub export entries will be placed in the secondary export table.
- * erts_export_consolidate() will move all stub export entries into the
- * main export table (will be done the next time code is loaded).
+ * Stub export entries will be placed in the loader export table.
*/
Export*
erts_export_get_or_make_stub(Eterm mod, Eterm func, unsigned int arity)
{
- Export e;
+ ErtsCodeIndex code_ix;
Export* ep;
+ IF_DEBUG(int retrying = 0;)
ASSERT(is_atom(mod));
ASSERT(is_atom(func));
-
- e.code[0] = mod;
- e.code[1] = func;
- e.code[2] = arity;
- ep = erts_find_export_entry(mod, func, arity);
- if (ep == 0) {
- /*
- * The code is not loaded (yet). Put the export in the secondary
- * export table, to avoid having to lock the main export table.
- */
- export_write_lock();
- ep = (Export *) hash_put(&secondary_export_table, (void*) &e);
- export_write_unlock();
- }
+
+ do {
+ code_ix = erts_active_code_ix();
+ ep = erts_find_export_entry(mod, func, arity, code_ix);
+ if (ep == 0) {
+ /*
+ * The code is not loaded (yet). Put the export in the staging
+ * export table, to avoid having to lock the active export table.
+ */
+ export_staging_lock();
+ if (erts_active_code_ix() == code_ix) {
+ struct export_templ templ;
+ struct export_entry* entry;
+
+ IndexTable* tab = &export_tables[erts_staging_code_ix()];
+ init_template(&templ, mod, func, arity);
+ entry = (struct export_entry *) index_put_entry(tab, &templ.entry);
+ ep = entry->ep;
+ ASSERT(ep);
+ }
+ else { /* race */
+ ASSERT(!retrying);
+ IF_DEBUG(retrying = 1);
+ }
+ export_staging_unlock();
+ }
+ } while (!ep);
return ep;
}
-/*
- * To be called before loading code (with other threads blocked).
- * This function will move all export entries from the secondary
- * export table into the primary.
- */
-void
-erts_export_consolidate(void)
+Export *export_list(int i, ErtsCodeIndex code_ix)
{
-#ifdef DEBUG
- HashInfo hi;
-#endif
-
- ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_smp_thr_progress_is_blocking());
-
- export_write_lock();
- erts_index_merge(&secondary_export_table, &export_table);
- erts_hash_merge(&secondary_export_table, &export_table.htable);
- export_write_unlock();
-#ifdef DEBUG
- hash_get_info(&hi, &export_table.htable);
- ASSERT(export_table.entries == hi.objs);
-#endif
+ return ((struct export_entry*) erts_index_lookup(&export_tables[code_ix], i))->ep;
}
-Export *export_list(int i)
+int export_list_size(ErtsCodeIndex code_ix)
{
- return (Export*) erts_index_lookup(&export_table, i);
+ return export_tables[code_ix].entries;
}
-int export_list_size(void)
+int export_table_sz(void)
{
- return export_table.entries;
+ int i, bytes = 0;
+
+ export_staging_lock();
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ bytes += index_table_sz(&export_tables[i]);
+ }
+ export_staging_unlock();
+ return bytes;
+}
+int export_entries_sz(void)
+{
+ return erts_smp_atomic_read_nob(&total_entries_bytes);
}
+Export *export_get(Export *e)
+{
+ struct export_entry ee;
+ struct export_entry* entry;
-int export_table_sz(void)
+ ee.ep = e;
+ entry = (struct export_entry*)hash_get(&export_tables[erts_active_code_ix()].htable, &ee);
+ return entry ? entry->ep : NULL;
+}
+
+IF_DEBUG(static ErtsCodeIndex debug_start_load_ix = 0;)
+
+
+void export_start_staging(void)
{
- return index_table_sz(&export_table);
+ ErtsCodeIndex dst_ix = erts_staging_code_ix();
+ ErtsCodeIndex src_ix = erts_active_code_ix();
+ IndexTable* dst = &export_tables[dst_ix];
+ IndexTable* src = &export_tables[src_ix];
+ struct export_entry* src_entry;
+#ifdef DEBUG
+ struct export_entry* dst_entry;
+#endif
+ int i;
+
+ ASSERT(dst_ix != src_ix);
+ ASSERT(debug_start_load_ix == -1);
+
+ export_staging_lock();
+ /*
+ * Insert all entries in src into dst table
+ */
+ for (i = 0; i < src->entries; i++) {
+ src_entry = (struct export_entry*) erts_index_lookup(src, i);
+ src_entry->ep->addressv[dst_ix] = src_entry->ep->addressv[src_ix];
+#ifdef DEBUG
+ dst_entry = (struct export_entry*)
+#endif
+ index_put_entry(dst, src_entry);
+ ASSERT(entry_to_blob(src_entry) == entry_to_blob(dst_entry));
+ }
+ export_staging_unlock();
+
+ IF_DEBUG(debug_start_load_ix = dst_ix);
}
-Export *export_get(Export *e)
+void export_end_staging(int commit)
{
- return hash_get(&export_table.htable, e);
+ ASSERT(debug_start_load_ix == erts_staging_code_ix());
+ IF_DEBUG(debug_start_load_ix = -1);
}
+
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index c604fdf7c3..61a54de59f 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2010. 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
@@ -28,14 +28,15 @@
#include "index.h"
#endif
+#include "code_ix.h"
+
/*
** Export entry
*/
+
typedef struct export
{
- IndexSlot slot; /* MUST BE LOCATED AT TOP OF STRUCT!!! */
- void* address; /* Pointer to code for function. */
- struct binary* match_prog_set; /* Match program for tracing. */
+ void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */
BeamInstr fake_op_func_info_for_hipe[2]; /* MUST be just before code[] */
/*
@@ -44,12 +45,12 @@ typedef struct export
* code[2]: Arity (untagged integer).
* code[3]: This entry is 0 unless the 'address' field points to it.
* Threaded code instruction to load function
- * (em_call_error_handler), execute BIF (em_apply_bif,
- * em_apply_apply), or call a traced function
- * (em_call_traced_function).
- * code[4]: Function pointer to BIF function (for BIFs only)
+ * (em_call_error_handler), execute BIF (em_apply_bif),
+ * or a breakpoint instruction (op_i_generic_breakpoint).
+ * code[4]: Function pointer to BIF function (for BIFs only),
* or pointer to threaded code if the module has an
- * on_load function that has not been run yet.
+ * on_load function that has not been run yet, or pointer
+ * to code for function code[3] is a breakpont instruction.
* Otherwise: 0.
*/
BeamInstr code[5];
@@ -59,21 +60,38 @@ typedef struct export
void init_export_table(void);
void export_info(int, void *);
-Export* erts_find_export_entry(Eterm m, Eterm f, unsigned int a);
+ERTS_GLB_INLINE Export* erts_active_export_entry(Eterm m, Eterm f, unsigned a);
Export* erts_export_put(Eterm mod, Eterm func, unsigned int arity);
-
Export* erts_export_get_or_make_stub(Eterm, Eterm, unsigned);
-void erts_export_consolidate(void);
-Export *export_list(int);
-int export_list_size(void);
+Export *export_list(int,ErtsCodeIndex);
+int export_list_size(ErtsCodeIndex);
int export_table_sz(void);
+int export_entries_sz(void);
Export *export_get(Export*);
+void export_start_staging(void);
+void export_end_staging(int commit);
+
+extern erts_smp_mtx_t export_staging_lock;
+#define export_staging_lock() erts_smp_mtx_lock(&export_staging_lock)
+#define export_staging_unlock() erts_smp_mtx_unlock(&export_staging_lock)
#include "beam_load.h" /* For em_* extern declarations */
#define ExportIsBuiltIn(EntryPtr) \
-(((EntryPtr)->address == (EntryPtr)->code + 3) && \
+(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->code + 3) && \
((EntryPtr)->code[3] == (BeamInstr) em_apply_bif))
-#endif
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Export*
+erts_active_export_entry(Eterm m, Eterm f, unsigned int a)
+{
+ extern Export* erts_find_export_entry(Eterm m, Eterm f, unsigned a, ErtsCodeIndex);
+ return erts_find_export_entry(m, f, a, erts_active_code_ix());
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* __EXPORT_H__ */
+
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 52f45b924f..5ce0d97c74 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -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
@@ -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;
@@ -926,7 +948,7 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2)
Eterm res;
Eterm *hp;
Eterm *hendp;
- Uint hsz;
+ Sint hsz;
ErtsDistExternal ede;
Eterm *tp;
Eterm real_bin;
@@ -972,7 +994,7 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2)
if (hsz < 0)
goto badarg;
- hp = HAlloc(BIF_P, hsz);
+ hp = HAlloc(BIF_P, (Uint) hsz);
hendp = hp + hsz;
res = erts_decode_dist_ext(&hp, &MSO(BIF_P), &ede);
@@ -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,56 @@ 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));
+ len = a->len;
+ if (utf8_atoms || a->latin1_chars < 0) {
+ 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;
+ if (len == a->latin1_chars) {
+ sys_memcpy(ep+1, a->name, len);
+ }
+ else {
+ len = erts_utf8_to_latin1(ep+1, a->name, len);
+ ASSERT(len == a->latin1_chars);
+ }
+ put_int8(len, ep);
+ ep++;
+ }
+ else {
+ *ep++ = ATOM_EXT;
+ if (len == a->latin1_chars) {
+ sys_memcpy(ep+2, a->name, len);
+ }
+ else {
+ len = erts_utf8_to_latin1(ep+2, a->name, 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 +1524,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;
}
@@ -1483,6 +1535,7 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
{
Uint len;
int n;
+ ErtsAtomEncoding char_enc;
switch (*ep++) {
case ATOM_CACHE_REF:
@@ -1498,17 +1551,32 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp)
case ATOM_EXT:
len = get_int16(ep),
ep += 2;
+ char_enc = ERTS_ATOM_ENC_LATIN1;
goto dec_atom_common;
case SMALL_ATOM_EXT:
len = get_int8(ep);
ep++;
+ char_enc = ERTS_ATOM_ENC_LATIN1;
+ goto dec_atom_common;
+ case ATOM_UTF8_EXT:
+ len = get_int16(ep),
+ ep += 2;
+ char_enc = ERTS_ATOM_ENC_UTF8;
+ goto dec_atom_common;
+ case SMALL_ATOM_UTF8_EXT:
+ len = get_int8(ep),
+ ep++;
+ char_enc = ERTS_ATOM_ENC_UTF8;
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, char_enc)) {
goto error;
}
} else {
- *objp = am_atom_put((char*)ep, len);
+ Eterm atom = erts_atom_put(ep, len, char_enc, 0);
+ if (is_non_value(atom))
+ goto error;
+ *objp = atom;
}
ep += len;
break;
@@ -1770,7 +1838,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 +1855,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 +1918,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 +1936,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) {
@@ -2114,6 +2182,7 @@ dec_term(ErtsDistExternal *edep, Eterm** hpp, byte* ep, ErlOffHeap* off_heap, Et
{
Eterm* hp_saved = *hpp;
int n;
+ ErtsAtomEncoding char_enc;
register Eterm* hp = *hpp; /* Please don't take the address of hp */
Eterm* next = objp;
@@ -2199,17 +2268,32 @@ 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;
+ char_enc = ERTS_ATOM_ENC_LATIN1;
+ goto dec_term_atom_common;
case SMALL_ATOM_EXT:
n = get_int8(ep);
ep++;
+ char_enc = ERTS_ATOM_ENC_LATIN1;
+ goto dec_term_atom_common;
+ case ATOM_UTF8_EXT:
+ n = get_int16(ep);
+ ep += 2;
+ char_enc = ERTS_ATOM_ENC_UTF8;
+ goto dec_term_atom_common;
+ case SMALL_ATOM_UTF8_EXT:
+ n = get_int8(ep);
+ ep++;
+ char_enc = ERTS_ATOM_ENC_UTF8;
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, char_enc)) {
goto error;
}
} else {
- *objp = am_atom_put((char*)ep, n);
+ Eterm atom = erts_atom_put(ep, n, char_enc, 0);
+ if (is_non_value(atom))
+ goto error;
+ *objp = atom;
}
ep += n;
break;
@@ -2564,7 +2648,7 @@ dec_term_atom_common:
goto error;
}
if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) {
- if (!erts_find_export_entry(mod, name, arity))
+ if (!erts_active_export_entry(mod, name, arity))
goto error;
}
*objp = make_export(hp);
@@ -2869,7 +2953,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;
}
@@ -2878,17 +2962,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:
@@ -2969,7 +3058,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;
@@ -3058,6 +3147,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)
{
@@ -3125,21 +3225,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..e37d47919e 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -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
@@ -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 2c20e3da3b..012c1c7e6a 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -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
@@ -28,6 +28,7 @@
#include "hash.h"
#include "index.h"
#include "atom.h"
+#include "code_ix.h"
#include "export.h"
#include "module.h"
#include "register.h"
@@ -38,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 */
{
@@ -89,158 +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;
-#ifdef ERTS_SMP
- erts_smp_atomic_t refc;
- 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);
- 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;
- }
-#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
@@ -292,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 */
@@ -344,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);
@@ -524,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;
@@ -695,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);
@@ -753,17 +493,43 @@ 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,
Eterm,
Eterm,
Eterm);
+void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
+ Eterm (*bif)(Process*,Eterm*));
void erts_init_bif(void);
Eterm erl_send(Process *p, Eterm to, Eterm msg);
@@ -771,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 */
@@ -786,24 +545,33 @@ typedef struct {
Eterm* fname_ptr; /* Pointer to fname table */
} FunctionInfo;
-struct LoaderState* erts_alloc_loader_state(void);
-Eterm erts_prepare_loading(struct LoaderState*, Process *c_p,
+Binary* erts_alloc_loader_state(void);
+Eterm erts_module_for_prepared_code(Binary* magic);
+Eterm erts_prepare_loading(Binary* loader_state, Process *c_p,
Eterm group_leader, Eterm* modp,
byte* code, Uint size);
-Eterm erts_finish_loading(struct LoaderState* stp, Process* c_p,
+Eterm erts_finish_loading(Binary* loader_state, Process* c_p,
ErtsProcLocks c_p_locks, Eterm* modp);
-Eterm erts_load_module(Process *c_p, ErtsProcLocks c_p_locks,
- Eterm group_leader, Eterm* mod, byte* code, Uint size);
+Eterm erts_preload_module(Process *c_p, ErtsProcLocks c_p_locks,
+ Eterm group_leader, Eterm* mod, byte* code, Uint size);
void init_load(void);
BeamInstr* find_function_from_pc(BeamInstr* pc);
Eterm* erts_build_mfa_item(FunctionInfo* fi, Eterm* hp,
Eterm args, Eterm* mfa_p);
-void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
void erts_set_current_function(FunctionInfo* fi, BeamInstr* current);
Eterm erts_module_info_0(Process* p, Eterm module);
Eterm erts_module_info_1(Process* p, Eterm module, Eterm what);
Eterm erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info);
+/* beam_ranges.c */
+void erts_init_ranges(void);
+void erts_start_staging_ranges(void);
+void erts_end_staging_ranges(int commit);
+void erts_update_ranges(BeamInstr* code, Uint size);
+void erts_remove_from_ranges(BeamInstr* code);
+UWord erts_ranges_sz(void);
+void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
+
/* break.c */
void init_break_handler(void);
void erts_set_ignore_break(void);
@@ -944,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;
@@ -957,363 +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);
Port *erts_get_heart_port(void);
-#ifdef ERTS_SMP
-void erts_smp_xports_unlock(Port *);
-#endif
-
#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)
-{
-#ifdef ERTS_SMP
- int res;
-
- ASSERT(erts_smp_atomic_read_nob(&prt->refc) > 0);
- erts_smp_atomic_inc_nob(&prt->refc);
- res = erts_smp_mtx_trylock(prt->lock);
- if (res == EBUSY) {
- erts_smp_atomic_dec_nob(&prt->refc);
- }
-
- return res;
-#else /* !ERTS_SMP */
- return 0;
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_port_lock(Port *prt)
-{
-#ifdef ERTS_SMP
- ASSERT(erts_smp_atomic_read_nob(&prt->refc) > 0);
- erts_smp_atomic_inc_nob(&prt->refc);
- erts_smp_mtx_lock(prt->lock);
-#endif
-}
-
-ERTS_GLB_INLINE void
-erts_smp_port_unlock(Port *prt)
-{
-#ifdef ERTS_SMP
- erts_aint_t refc;
- erts_smp_mtx_unlock(prt->lock);
- refc = erts_smp_atomic_dec_read_nob(&prt->refc);
- ASSERT(refc >= 0);
- if (refc == 0)
- erts_port_cleanup(prt);
-#endif
-}
-
-#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;
- }
-#ifdef ERTS_SMP
- else {
- erts_smp_atomic_inc_nob(&prt->refc);
- erts_smp_port_state_unlock(prt);
-
- 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)
-{
-#ifdef ERTS_SMP
- erts_smp_port_unlock(prt);
-#else
- if (prt->status & ERTS_PORT_SFLGS_DEAD)
- erts_port_cleanup(prt);
-#endif
-}
-
-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 */
-
-/*
- * 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);
-Uint erts_fit_in_bits(Uint);
-int list_length(Eterm);
-Export* erts_find_function(Eterm, Eterm, unsigned int);
-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[]);
+Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
Eterm store_external_or_ref_in_proc_(Process *, Eterm);
Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm);
@@ -1328,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*);
@@ -1389,89 +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);
+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 */
@@ -1490,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 *);
@@ -1537,39 +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(Eterm* mfa, int specified,
- Binary* match_prog_set, Binary *meta_match_prog_set,
- int on, struct trace_pattern_flags,
- Eterm meta_tracer_pid);
-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);
-void erts_bif_trace_init(void);
/*
** Call_trace uses this API for the parameter matching functions
@@ -1615,20 +920,19 @@ extern void erts_match_prog_foreach_offheap(Binary *b,
breakpoint functions */
#define MATCH_SET_EXCEPTION_TRACE (0x4) /* exception trace requested */
#define MATCH_SET_RX_TRACE (MATCH_SET_RETURN_TRACE|MATCH_SET_EXCEPTION_TRACE)
-/*
- * Flag values when tracing bif
- * Future note: flag field is 8 bits
- */
-#define BIF_TRACE_AS_LOCAL (0x1)
-#define BIF_TRACE_AS_GLOBAL (0x2)
-#define BIF_TRACE_AS_META (0x4)
-#define BIF_TRACE_AS_CALL_TIME (0x8)
extern erts_driver_t vanilla_driver;
extern erts_driver_t spawn_driver;
extern erts_driver_t fd_driver;
/* Should maybe be placed in erl_message.h, but then we get an include mess. */
+ERTS_GLB_INLINE Eterm *
+erts_alloc_message_heap_state(Uint size,
+ ErlHeapFragment **bpp,
+ ErlOffHeap **ohpp,
+ Process *receiver,
+ ErtsProcLocks *receiver_locks,
+ erts_aint32_t *statep);
ERTS_GLB_INLINE Eterm *
erts_alloc_message_heap(Uint size,
@@ -1651,16 +955,22 @@ erts_alloc_message_heap(Uint size,
*/
ERTS_GLB_INLINE Eterm *
-erts_alloc_message_heap(Uint size,
- ErlHeapFragment **bpp,
- ErlOffHeap **ohpp,
- Process *receiver,
- ErtsProcLocks *receiver_locks)
+erts_alloc_message_heap_state(Uint size,
+ ErlHeapFragment **bpp,
+ ErlOffHeap **ohpp,
+ Process *receiver,
+ ErtsProcLocks *receiver_locks,
+ erts_aint32_t *statep)
{
Eterm *hp;
+ erts_aint32_t state;
#ifdef ERTS_SMP
int locked_main = 0;
- ErtsProcLocks ulocks = *receiver_locks & ERTS_PROC_LOCKS_MSG_SEND;
+ state = erts_smp_atomic32_read_acqb(&receiver->state);
+ if (statep)
+ *statep = state;
+ if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ goto allocate_in_mbuf;
#endif
if (size > (Uint) INT_MAX)
@@ -1676,20 +986,19 @@ erts_alloc_message_heap(Uint size,
#ifdef ERTS_SMP
try_allocate_on_heap:
#endif
- if (ERTS_PROC_IS_EXITING(receiver)
+ state = erts_smp_atomic32_read_nob(&receiver->state);
+ if (statep)
+ *statep = state;
+ if ((state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
|| HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) {
#ifdef ERTS_SMP
- if (locked_main)
- ulocks |= ERTS_PROC_LOCK_MAIN;
+ if (locked_main) {
+ *receiver_locks &= ~ERTS_PROC_LOCK_MAIN;
+ erts_smp_proc_unlock(receiver, ERTS_PROC_LOCK_MAIN);
+ }
#endif
goto allocate_in_mbuf;
}
-#ifdef ERTS_SMP
- if (ulocks) {
- erts_smp_proc_unlock(receiver, ulocks);
- *receiver_locks &= ~ulocks;
- }
-#endif
hp = HEAP_TOP(receiver);
HEAP_TOP(receiver) = hp + size;
*bpp = NULL;
@@ -1705,12 +1014,6 @@ erts_alloc_message_heap(Uint size,
else {
ErlHeapFragment *bp;
allocate_in_mbuf:
-#ifdef ERTS_SMP
- if (ulocks) {
- *receiver_locks &= ~ulocks;
- erts_smp_proc_unlock(receiver, ulocks);
- }
-#endif
bp = new_message_buffer(size);
hp = bp->mem;
*bpp = bp;
@@ -1720,6 +1023,17 @@ erts_alloc_message_heap(Uint size,
return hp;
}
+ERTS_GLB_INLINE Eterm *
+erts_alloc_message_heap(Uint size,
+ ErlHeapFragment **bpp,
+ ErlOffHeap **ohpp,
+ Process *receiver,
+ ErtsProcLocks *receiver_locks)
+{
+ return erts_alloc_message_heap_state(size, bpp, ohpp, receiver,
+ receiver_locks, NULL);
+}
+
#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
#if !HEAP_ON_C_STACK
@@ -1802,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 75d0d598a2..79c3ecf1b3 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -68,14 +68,14 @@ erts_index_init(ErtsAlcType_t type, IndexTable* t, char* name,
return t;
}
-int
-index_put(IndexTable* t, void* tmpl)
+IndexSlot*
+index_put_entry(IndexTable* t, void* tmpl)
{
int ix;
IndexSlot* p = (IndexSlot*) hash_put(&t->htable, tmpl);
if (p->index >= 0) {
- return p->index;
+ return p;
}
ix = t->entries;
@@ -93,7 +93,7 @@ index_put(IndexTable* t, void* tmpl)
t->entries++;
p->index = ix;
t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
- return ix;
+ return p;
}
int index_get(IndexTable* t, void* tmpl)
@@ -136,3 +136,18 @@ void erts_index_merge(Hash* src, IndexTable* dst)
}
}
}
+
+void index_erase_latest_from(IndexTable* t, Uint from_ix)
+{
+ if(from_ix < (Uint)t->entries) {
+ int ix;
+ for (ix = from_ix; ix < t->entries; ix++) {
+ IndexSlot* obj = t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK];
+ hash_erase(&t->htable, obj);
+ }
+ t->entries = from_ix;
+ }
+ else {
+ ASSERT(from_ix == t->entries);
+ }
+}
diff --git a/erts/emulator/beam/index.h b/erts/emulator/beam/index.h
index 4eb9b1f992..537bc11056 100644
--- a/erts/emulator/beam/index.h
+++ b/erts/emulator/beam/index.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. 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
@@ -55,12 +55,24 @@ void index_info(int, void *, IndexTable*);
int index_table_sz(IndexTable *);
int index_get(IndexTable*, void*);
-int index_put(IndexTable*, void*);
+
+IndexSlot* index_put_entry(IndexTable*, void*);
void erts_index_merge(Hash*, IndexTable*);
+/* Erase all entries with index 'ix' and higher
+*/
+void index_erase_latest_from(IndexTable*, Uint ix);
+
+ERTS_GLB_INLINE int index_put(IndexTable*, void*);
ERTS_GLB_INLINE IndexSlot* erts_index_lookup(IndexTable*, Uint);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE int index_put(IndexTable* t, void* tmpl)
+{
+ return index_put_entry(t, tmpl)->index;
+}
+
ERTS_GLB_INLINE IndexSlot*
erts_index_lookup(IndexTable* t, Uint ix)
{
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 609fe9f5fb..8c67f731f4 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -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
@@ -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((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((Port), 0)
#define DRV_MONITOR_LOCK_PDL(Port) /* nothing */
#define DRV_MONITOR_UNLOCK_PDL(Port) /* nothing */
#endif
@@ -89,36 +97,10 @@ 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(drvport, 0);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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);
+ return &prt->ioq;
}
static ERTS_INLINE int
@@ -194,29 +176,31 @@ typedef struct line_buf_context {
\
dtrace_proc_str((PID), process_str); \
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.
-*/
+void
+dtrace_drvport_str(ErlDrvPort drvport, char *port_buf)
+{
+ Port *port = erts_drvport2port(drvport);
-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) */
+ if (port != ERTS_INVALID_ERL_DRV_PORT)
+ erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
+ port_channel_no(port->common.id),
+ port_number(port->common.id));
+ else
+ erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<INVALID>",
+ port_channel_no(port->common.id),
+ port_number(port->common.id));
+}
+#endif
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,148 +211,281 @@ 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;
+#endif
+ erts_port_task_init_sched(&prt->sched, id);
+}
+
+#if !ERTS_PORT_INIT_INSTR_NEED_ID
+static ERTS_INLINE void port_init_instr_abort(Port *prt)
+{
#ifdef ERTS_SMP
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 0);
- erts_smp_atomic_set_nob(&port->refc, 2); /* Port alive + lock */
-#endif
- erts_smp_port_state_unlock(port);
- return num & port_num_mask;
+ ASSERT(prt->drv_ptr && prt->lock);
+ if (!prt->drv_ptr->lock) {
+ erts_mtx_unlock(prt->lock);
+ erts_mtx_destroy(prt->lock);
+ }
+#endif
+ erts_port_task_fini_sched(&prt->sched);
}
+#endif
-/*
- * erts_test_next_port() is only used for testing.
- */
-Sint
-erts_test_next_port(int set, Uint next)
+static void insert_port_struct(void *vprt, Eterm data)
{
- Uint i, num;
- Sint res = -1;
+ 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);
+}
+
+#define ERTS_CREATE_PORT_FLAG_PARALLELISM (1 << 0)
+
+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
- erts_smp_spin_lock(&get_free_port_lck);
- if (set) {
- last_port_num = (next - 1) & port_num_mask;
+#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);
}
- num = last_port_num + 1;
+ else
+#endif
+ port_size = size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
- for (i=0; i < erts_max_ports && res<0; ++i, ++num) {
-
- Port* port = &erts_port[num & erts_port_tab_index_mask];
+ 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;
- erts_smp_port_state_lock(port);
+ size += sys_strlen(name) + 1;
- if (port->status & ERTS_PORT_SFLG_FREE) {
- last_port_num = num - 1;
- res = num & port_num_mask;
- }
- erts_smp_port_state_unlock(port);
+ p = erts_alloc_fnf(ERTS_ALC_T_PORT, size);
+ if (!p) {
+ if (enop)
+ *enop = ENOMEM;
+ return NULL;
}
- erts_smp_spin_unlock(&get_free_port_lck);
- return res;
-}
+ prt = (Port *) p;
+ p += port_size;
-static void port_cleanup(Port *prt);
+ if (!busy_port_queue_size)
+ busy_port_queue = NULL;
+ else {
+ busy_port_queue = (ErtsPortTaskBusyPortQ *) p;
+ p += busy_port_queue_size;
+ }
#ifdef ERTS_SMP
-
-static void
-sched_port_cleanup(void *vprt)
-{
- Port *prt = (Port *) vprt;
- erts_smp_mtx_lock(prt->lock);
- port_cleanup(prt);
-}
-
+ 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
+
+ erts_port_task_pre_init_sched(&prt->sched, busy_port_queue);
-void
-erts_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
- if (erts_smp_mtx_trylock(prt->lock) == EBUSY)
- erts_schedule_misc_op(sched_port_cleanup, (void *) prt);
- else
+ prt->common.u.alive.ptimer = NULL;
+#else
+ sys_memset(&prt->common.u.alive.tm, 0, sizeof(ErlTimer));
#endif
- port_cleanup(prt);
-}
+ erts_port_task_handle_init(&prt->timeout_task);
+ prt->psd = NULL;
+ prt->drv_data = (SWord) 0;
+ prt->os_pid = -1;
-void
-port_cleanup(Port *prt)
-{
+ /* Set default tracing */
+ erts_get_default_tracing(&ERTS_TRACE_FLAGS(prt), &ERTS_TRACER_PROC(prt));
+
+ ASSERT(((char *) prt) == ((char *) &prt->common));
+
+#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
+
+ 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
- Uint32 port_specific;
- erts_smp_mtx_t *mtx;
+ if (driver_lock)
+ erts_mtx_unlock(driver_lock);
#endif
- erts_driver_t *driver;
+ if (enop)
+ *enop = 0;
+ erts_free(ERTS_ALC_T_PORT, prt);
+ return NULL;
+ }
- erts_smp_port_state_lock(prt);
+ ASSERT(prt == (Port *) (erts_ptab_pix2intptr_nob(
+ &erts_port,
+ internal_port_index(prt->common.id))));
- ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- driver = prt->drv_ptr;
- prt->drv_ptr = NULL;
- ASSERT(driver);
+ initq(prt);
-#ifdef ERTS_SMP
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
- ASSERT(prt->status & ERTS_PORT_SFLG_FREE_SCHEDULED);
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&prt->refc) == 0);
+ if (erts_port_schedule_all_ops)
+ x_pts_flgs |= ERTS_PTS_FLG_FORCE_SCHED;
- port_specific = (prt->status & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK);
+ if (create_flags & ERTS_CREATE_PORT_FLAG_PARALLELISM)
+ x_pts_flgs |= ERTS_PTS_FLG_PARALLELISM;
- mtx = prt->lock;
- ASSERT(mtx);
+ if (x_pts_flgs)
+ erts_smp_atomic32_read_bor_nob(&prt->sched.flags, x_pts_flgs);
- prt->lock = NULL;
+ erts_atomic32_set_relb(&prt->state, state);
+ return prt;
+}
- ASSERT(prt->status & ERTS_PORT_SFLG_PORT_DEBUG);
- ASSERT(!(prt->status & ERTS_PORT_SFLG_FREE));
- prt->status = ERTS_PORT_SFLG_FREE;
+#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
- erts_smp_port_state_unlock(prt);
- erts_smp_mtx_unlock(mtx);
+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);
- if (port_specific) {
- erts_smp_mtx_destroy(mtx);
- erts_free(ERTS_ALC_T_PORT_LOCK, mtx);
- }
+#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
- if (driver->handle)
- erts_ddll_dereference_driver(driver->handle);
-}
+ 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.
@@ -416,94 +533,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;
}
/*
@@ -515,47 +559,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) {
@@ -599,61 +640,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;
-#ifdef ERTS_SMP
- ERTS_SMP_LC_ASSERT(erts_smp_atomic_read_nob(&port->refc) == 2);
- erts_smp_atomic_set_nob(&port->refc, 0);
-#endif
- 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);
@@ -666,56 +698,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)(ERTS_Port2ErlDrvPort(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
@@ -740,102 +779,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;
+ /* 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);
- if (!creator_port)
- return (ErlDrvTermData) -1;
+ if (creator_port == ERTS_INVALID_ERL_DRV_PORT)
+ 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);
}
- if ((port_num = get_free_port()) < 0) {
- errno = SYSTEM_LIMIT;
+
+#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;
+ }
+ 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 ERTS_Port2ErlDrvPort(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
@@ -870,8 +929,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;
@@ -986,7 +1045,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; \
@@ -1037,8 +1096,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;
@@ -1049,7 +1109,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 */
@@ -1109,7 +1169,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;
@@ -1124,56 +1184,744 @@ 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) || ERTS_IS_CRASH_DUMPING);
+ int reds_left_in;
+} 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;
+ int reds_left_in;
+ 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)
+ reds_left_in = CONTEXT_REDS/10;
+ else {
+ 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);
+
+ reds_left_in = ERTS_BIF_REDS_LEFT(c_p);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+
+ ASSERT(0 <= reds_left_in && reds_left_in <= CONTEXT_REDS);
+ sp->reds_left_in = reds_left_in;
+ prt->reds = CONTEXT_REDS - reds_left_in;
+
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)
+{
+ int reds;
+ Port *prt = sp->port;
+ Process *c_p = sp->c_p;
+
+ reds = prt->reds;
+ reds += 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 (reds != (CONTEXT_REDS - sp->reds_left_in)) {
+ int bump_reds = reds - (CONTEXT_REDS - sp->reds_left_in);
+ ASSERT(bump_reds > 0);
+ BUMP_REDS(c_p, bump_reds);
+ }
+
+ 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));
@@ -1182,210 +1930,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);
+ ASSERT(evp->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);
- }
-#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);
- if (erts_sys_getenv_raw("ERL_MAX_PORTS", maxports, &maxportssize) == 0)
- erts_max_ports = atoi(maxports);
+#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
+ }
+
+ 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;
- if (erts_max_ports > ERTS_MAX_PORTS)
- erts_max_ports = ERTS_MAX_PORTS;
- if (erts_max_ports < 1024)
- erts_max_ports = 1024;
+ 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);
+}
+
+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(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);
#ifdef ERTS_SMP
- erts_smp_atomic_init_nob(&erts_port[i].refc, 0);
- 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);
@@ -1394,28 +2701,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,14 +2940,15 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
{
Process *rp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
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 = erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = (scheduler
+ ? erts_proc_lookup(pid)
+ : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (rp) {
Eterm tuple;
@@ -1609,17 +2956,22 @@ 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);
- erts_smp_proc_dec_refc(rp);
+
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
+
}
}
@@ -1632,7 +2984,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)
{
@@ -1644,28 +2996,33 @@ static void deliver_read_message(Port* prt, Eterm to,
ErlHeapFragment *bp;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
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;
}
- rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+ rp = (scheduler
+ ? erts_proc_lookup(to)
+ : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
+
if (!rp)
return;
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;
@@ -1696,14 +3053,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
@@ -1711,15 +3068,18 @@ static void deliver_read_message(Port* prt, Eterm to,
, NIL
#endif
);
- erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (rp_locks)
+ erts_smp_proc_unlock(rp, rp_locks);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
/*
* 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)
{
@@ -1728,7 +3088,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));
}
@@ -1739,20 +3099,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),
@@ -1779,6 +3144,8 @@ deliver_vec_message(Port* prt, /* Port */
ErlHeapFragment *bp;
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;
@@ -1787,16 +3154,20 @@ deliver_vec_message(Port* prt, /* Port */
* Check arguments for validity.
*/
- rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+
+ rp = (scheduler
+ ? erts_proc_lookup(to)
+ : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
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;
@@ -1807,7 +3178,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--;
@@ -1860,7 +3231,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
@@ -1869,7 +3240,8 @@ deliver_vec_message(Port* prt, /* Port */
#endif
);
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
@@ -1892,7 +3264,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
@@ -1908,7 +3280,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
@@ -1923,11 +3295,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);
}
}
@@ -1939,29 +3312,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;
@@ -1969,7 +3342,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
@@ -1977,14 +3350,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){
@@ -2000,20 +3373,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
@@ -2033,7 +3407,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;
@@ -2087,7 +3461,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,
@@ -2123,11 +3497,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, set_state_flags;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
@@ -2147,66 +3523,73 @@ 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;
+
+ set_state_flags = ERTS_PORT_SFLG_EXITING;
+ if (send_closed)
+ set_state_flags |= ERTS_PORT_SFLG_SEND_CLOSED;
+
+ state = erts_atomic32_read_bor_mb(&p->state, set_state_flags);
+ state |= set_state_flags;
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(ERTS_Port2ErlDrvPort(p), 0);
- if (p->reg != NULL)
- (void) erts_unregister_name(NULL, 0, p, p->reg->name);
-
- erts_port_status_bor_set(p, ERTS_PORT_SFLG_EXITING);
+ if (p->common.u.alive.reg != NULL)
+ (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name);
{
- 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.
@@ -2222,234 +3605,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_opt(NULL, 0,
- port->connected, rp_locks,
- ERTS_P2P_FLG_SMP_INC_REFC);
- 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);
- erts_smp_proc_dec_refc(rp);
- }
+ /* 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 (n < 0) {
- return THE_NON_VALUE;
+ 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;
+
+ 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_bufp != &resp_buf[0]
+ && !(ret_flags & DRIVER_CALL_KEEP_BUFFER))
+ driver_free(resp_bufp);
+ 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 {
@@ -2470,39 +4732,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);
@@ -2516,109 +4778,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(dprt);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ 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(port_num);
+ if (prt != ERTS_INVALID_ERL_DRV_PORT)
+ 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_state(ix, &state);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ 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;
@@ -2655,25 +4951,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;
}
@@ -2681,12 +4970,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);
@@ -2701,7 +4990,7 @@ erts_stale_drv_select(Eterm port,
int deselect)
{
char *type;
- ErlDrvPort drv_port = internal_port_index(port);
+ ErlDrvPort drv_port = ERTS_Port2ErlDrvPort(erts_port_lookup_raw(port));
ErtsPortNames *pnp = erts_get_port_names(port);
erts_dsprintf_buf_t *dsbufp;
@@ -2741,16 +5030,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);
@@ -2766,17 +5055,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;
@@ -2794,7 +5076,6 @@ erts_get_port_names(Eterm id)
}
do_realloc = 0;
}
- erts_smp_port_state_unlock(prt);
} while (do_realloc);
}
return pnp;
@@ -2819,11 +5100,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)
@@ -2831,9 +5110,8 @@ 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);
Eterm* hp;
Eterm tuple;
Process *rp;
@@ -2841,13 +5119,21 @@ void driver_report_exit(int ix, int status)
ErlHeapFragment *bp = NULL;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
+ int scheduler = erts_get_scheduler_id() != 0;
+ Port* prt = erts_drvport2port(ix);
+
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return;
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 = erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC);
+
+ rp = (scheduler
+ ? erts_proc_lookup(pid)
+ : erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (!rp)
return;
@@ -2855,7 +5141,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
@@ -2864,29 +5150,8 @@ void driver_report_exit(int ix, int status)
);
erts_smp_proc_unlock(rp, rp_locks);
- 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;
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
#define ERTS_B2T_STATES_DEF_STATES_SZ 5
@@ -2976,10 +5241,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;
@@ -2995,6 +5257,7 @@ driver_deliver_term(ErlDrvPort port,
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
struct b2t_states__ b2t;
+ int scheduler = 1; /* Silence erroneous warning... */
init_b2t_states(&b2t);
@@ -3180,13 +5443,16 @@ 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;
- rp = erts_pid2proc_opt(NULL, 0, to, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC);
+ /*
+ * Increase refc on proc if done from a non-scheduler thread.
+ */
+ scheduler = erts_get_scheduler_id() != 0;
+ rp = (scheduler
+ ? erts_proc_lookup(to)
+ : erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC));
if (!rp) {
res = 0;
goto done;
@@ -3438,7 +5704,8 @@ driver_deliver_term(ErlDrvPort port,
if (rp) {
if (rp_locks)
erts_smp_proc_unlock(rp, rp_locks);
- erts_smp_proc_dec_refc(rp);
+ if (!scheduler)
+ erts_smp_proc_dec_refc(rp);
}
#endif
cleanup_b2t_states(&b2t);
@@ -3447,25 +5714,119 @@ 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
+ erts_aint32_t state;
+ Port *prt = erts_port_lookup_raw((Eterm) port_id);
+ if (!prt)
+ return -1;
+ state = erts_atomic32_read_nob(&prt->state);
+ if (state & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP
+ | ERTS_PORT_SFLG_CLOSING)) {
+ if (state & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP)
+ return -1;
+ else
+ return 0;
+ }
+ 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 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_state(drvport, &state);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return -1; /* invalid (dead) */
+ ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ if (state & ERTS_PORT_SFLG_CLOSING)
+ return 0;
- if (prt == NULL)
- return -1;
- return driver_deliver_term(ix, prt->connected, data, len);
+ 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_state(drvport, &state);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return -1; /* invalid (dead) */
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
+ if (state & ERTS_PORT_SFLG_CLOSING)
+ return 0;
+ }
+ return driver_deliver_term(to, data, len);
}
@@ -3477,26 +5838,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_state(ix, &state);
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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;
}
@@ -3511,21 +5873,21 @@ 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_state(ix, &state);
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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) {
if (len == 0)
return erts_net_message(prt,
prt->dist_entry,
@@ -3537,10 +5899,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;
}
@@ -3561,6 +5925,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;
@@ -3573,13 +5938,13 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen,
if (hlen < 0)
hlen = 0;
- prt = erts_drvport2port(ix);
- if (prt == NULL)
+ prt = erts_drvport2port_state(ix, &state);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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 ! */
@@ -3604,7 +5969,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;
}
@@ -3715,8 +6081,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) {
@@ -3812,6 +6177,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);
}
@@ -3850,15 +6216,17 @@ driver_pdl_create(ErlDrvPort dp)
{
ErlDrvPDL pdl;
Port *pp = erts_drvport2port(dp);
- if (!pp || pp->port_data_lock)
+ if (pp == ERTS_INVALID_ERL_DRV_PORT || 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;
}
@@ -4296,12 +6664,12 @@ 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)
@@ -4310,19 +6678,19 @@ int driver_set_timer(ErlDrvPort ix, unsigned long t)
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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,
@@ -4334,7 +6702,7 @@ int driver_set_timer(ErlDrvPort ix, unsigned long t)
int driver_cancel_timer(ErlDrvPort ix)
{
Port* prt = erts_drvport2port(ix);
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
drv_cancel_timer(prt);
@@ -4349,13 +6717,15 @@ driver_read_timer(ErlDrvPort ix, unsigned long* t)
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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;
}
@@ -4406,8 +6776,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);
@@ -4417,34 +6787,19 @@ 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;
+#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];
-
- DRV_MONITOR_LOCK_PDL(prt);
-
- 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);
- }
+#endif
- if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
- DRV_MONITOR_UNLOCK_PDL(prt);
+ prt = DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(drvport);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1;
- }
/* Now (in SMP) we should have either the port lock (if we have a scheduler) or the port data lock
(if we're a driver thread) */
@@ -4479,7 +6834,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;
}
@@ -4491,13 +6846,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);
@@ -4506,33 +6861,18 @@ 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;
+#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];
-
- DRV_MONITOR_LOCK_PDL(prt);
-
- 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);
- }
+#endif
- if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
- DRV_MONITOR_UNLOCK_PDL(prt);
+ prt = DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(drvport);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1;
- }
/* Now we should have either the port lock (if we have a scheduler) or the port data lock
(if we're a driver thread) */
@@ -4565,7 +6905,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;
}
@@ -4576,33 +6916,18 @@ 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;
+#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);
-
- 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);
- }
+#endif
- if (status & ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP) {
- DRV_MONITOR_UNLOCK_PDL(prt);
+ prt = DRV_MONITOR_LOOKUP_PORT_LOCK_PDL(drvport);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return driver_term_nil;
- }
/* Now we should have either the port lock (if we have a scheduler) or the port data lock
(if we're a driver thread) */
@@ -4644,7 +6969,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;
}
@@ -4654,7 +6979,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
@@ -4663,7 +6988,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);
@@ -4674,27 +6999,28 @@ 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_state(ix, &state);
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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;
}
@@ -4710,20 +7036,20 @@ int driver_exit(ErlDrvPort ix, int err)
Port* prt = erts_drvport2port(ix);
Process* rp;
ErtsLink *lnk, *rlnk = NULL;
+ Eterm connected;
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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)
@@ -4742,7 +7068,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);
}
}
@@ -4755,8 +7082,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)
@@ -4773,7 +7104,10 @@ 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;
}
@@ -4781,25 +7115,27 @@ ErlDrvTermData driver_mk_atom(char* string)
ErlDrvTermData driver_mk_port(ErlDrvPort ix)
{
Port* prt = erts_drvport2port(ix);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return (ErlDrvTermData) NIL;
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);
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
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);
ERTS_SMP_CHK_NO_PROC_LOCKS;
- if (prt == NULL)
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return NIL;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
return prt->caller;
@@ -4812,20 +7148,18 @@ int driver_lock_driver(ErlDrvPort ix)
ERTS_SMP_CHK_NO_PROC_LOCKS;
- erts_smp_mtx_lock(&erts_driver_list_lock);
-
- if (prt == NULL) {
- erts_smp_mtx_unlock(&erts_driver_list_lock);
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
return -1;
- }
+
+ erts_smp_rwmtx_rwlock(&erts_driver_list_lock);
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;
}
@@ -4835,7 +7169,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;
@@ -4843,7 +7177,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);
}
}
/*
@@ -5012,7 +7346,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(ERTS_Port2ErlDrvPort(prt), event, NULL);
}
static void
@@ -5020,7 +7354,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(ERTS_Port2ErlDrvPort(prt), event,
(ERL_DRV_READ | ERL_DRV_USE_NO_CALLBACK), 0);
}
@@ -5029,7 +7363,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(ERTS_Port2ErlDrvPort(prt), event,
(ERL_DRV_WRITE | ERL_DRV_USE_NO_CALLBACK), 0);
}
@@ -5064,13 +7398,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
);
}
@@ -5142,7 +7479,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;
@@ -5171,7 +7508,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;
}
@@ -5184,7 +7521,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)
@@ -5192,7 +7529,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;
}
@@ -5206,12 +7543,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;
}
@@ -5241,18 +7578,22 @@ erl_drv_getenv(char *key, char *value, size_t *value_size)
* - uses the fact that heart_port is registered when starting heart
*/
-Port *erts_get_heart_port() {
+Port *erts_get_heart_port(void)
+{
+ int ix, max = erts_ptab_max(&erts_port);
- Port* port;
- Uint ix;
+ for (ix = 0; ix < max; ix++) {
+ struct reg_proc *reg;
+ Port *port = erts_pix2port(ix);
- for(ix = 0; ix < erts_max_ports; ix++) {
- port = &erts_port[ix];
+ if (!port)
+ continue;
/* only examine undead or alive ports */
- if (port->status & ERTS_PORT_SFLGS_DEAD)
+ if (erts_atomic32_read_nob(&port->state) & ERTS_PORT_SFLGS_DEAD)
continue;
/* immediate atom compare */
- if (port->reg && port->reg->name == am_heart_port) {
+ reg = port->common.u.alive.reg;
+ if (reg && reg->name == am_heart_port) {
return port;
}
}
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index b93b1ad09a..daa6e136c5 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -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
@@ -26,21 +26,32 @@
#include "global.h"
#include "module.h"
+#ifdef DEBUG
+# define IF_DEBUG(x) x
+#else
+# define IF_DEBUG(x)
+#endif
+
#define MODULE_SIZE 50
#define MODULE_LIMIT (64*1024)
-static IndexTable module_table;
+static IndexTable module_tables[ERTS_NUM_CODE_IX];
-/*
- * SMP note: We don't need to look accesses to the module table because
- * there is one only scheduler thread when we update it.
+erts_smp_rwmtx_t the_old_code_rwlocks[ERTS_NUM_CODE_IX];
+
+static erts_smp_atomic_t tot_module_bytes;
+
+/* SMP note: Active module table lookup and current module instance can be
+ * read without any locks. Old module instances are protected by
+ * "the_old_code_rwlocks" as purging is done on active module table.
+ * Staging table is protected by the "code_ix lock".
*/
#include "erl_smp.h"
void module_info(int to, void *to_arg)
{
- index_info(to, to_arg, &module_table);
+ index_info(to, to_arg, &module_tables[erts_active_code_ix()]);
}
@@ -59,45 +70,67 @@ static int module_cmp(Module* tmpl, Module* obj)
static Module* module_alloc(Module* tmpl)
{
Module* obj = (Module*) erts_alloc(ERTS_ALC_T_MODULE, sizeof(Module));
+ erts_smp_atomic_add_nob(&tot_module_bytes, sizeof(Module));
obj->module = tmpl->module;
- obj->code = 0;
- obj->old_code = 0;
- obj->code_length = 0;
- obj->old_code_length = 0;
+ obj->curr.code = 0;
+ obj->old.code = 0;
+ obj->curr.code_length = 0;
+ obj->old.code_length = 0;
obj->slot.index = -1;
- obj->nif = NULL;
- obj->old_nif = NULL;
+ obj->curr.nif = NULL;
+ obj->old.nif = NULL;
+ obj->curr.num_breakpoints = 0;
+ obj->old.num_breakpoints = 0;
+ obj->curr.num_traced_exports = 0;
+ obj->old.num_traced_exports = 0;
return obj;
}
+static void module_free(Module* mod)
+{
+ erts_free(ERTS_ALC_T_MODULE, mod);
+ erts_smp_atomic_add_nob(&tot_module_bytes, -sizeof(Module));
+}
void init_module_table(void)
{
HashFunctions f;
+ int i;
f.hash = (H_FUN) module_hash;
f.cmp = (HCMP_FUN) module_cmp;
f.alloc = (HALLOC_FUN) module_alloc;
- f.free = 0;
+ f.free = (HFREE_FUN) module_free;
+
+ for (i = 0; i < ERTS_NUM_CODE_IX; i++) {
+ erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_tables[i], "module_code",
+ MODULE_SIZE, MODULE_LIMIT, f);
+ }
- erts_index_init(ERTS_ALC_T_MODULE_TABLE, &module_table, "module_code",
- MODULE_SIZE, MODULE_LIMIT, f);
+ for (i=0; i<ERTS_NUM_CODE_IX; i++) {
+ erts_smp_rwmtx_init_x(&the_old_code_rwlocks[i], "old_code", make_small(i));
+ }
+ erts_smp_atomic_init_nob(&tot_module_bytes, 0);
}
Module*
-erts_get_module(Eterm mod)
+erts_get_module(Eterm mod, ErtsCodeIndex code_ix)
{
Module e;
int index;
+ IndexTable* mod_tab;
ASSERT(is_atom(mod));
+
+ mod_tab = &module_tables[code_ix];
+
e.module = atom_val(mod);
- index = index_get(&module_table, (void*) &e);
+ index = index_get(mod_tab, (void*) &e);
if (index == -1) {
return NULL;
} else {
- return (Module*) erts_index_lookup(&module_table, index);
+ return (Module*) erts_index_lookup(mod_tab, index);
}
}
@@ -105,27 +138,101 @@ Module*
erts_put_module(Eterm mod)
{
Module e;
- int index;
+ IndexTable* mod_tab;
+ int oldsz, newsz;
+ Module* res;
ASSERT(is_atom(mod));
ERTS_SMP_LC_ASSERT(erts_initialized == 0
- || erts_smp_thr_progress_is_blocking());
+ || erts_has_code_write_permission());
+
+ mod_tab = &module_tables[erts_staging_code_ix()];
e.module = atom_val(mod);
- index = index_put(&module_table, (void*) &e);
- return (Module*) erts_index_lookup(&module_table, index);
+ oldsz = index_table_sz(mod_tab);
+ res = (Module*) index_put_entry(mod_tab, (void*) &e);
+ newsz = index_table_sz(mod_tab);
+ erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
+ return res;
}
-Module *module_code(int i)
+Module *module_code(int i, ErtsCodeIndex code_ix)
{
- return (Module*) erts_index_lookup(&module_table, i);
+ return (Module*) erts_index_lookup(&module_tables[code_ix], i);
}
-int module_code_size(void)
+int module_code_size(ErtsCodeIndex code_ix)
{
- return module_table.entries;
+ return module_tables[code_ix].entries;
}
int module_table_sz(void)
{
- return index_table_sz(&module_table);
+ return erts_smp_atomic_read_nob(&tot_module_bytes);
+}
+
+#ifdef DEBUG
+static ErtsCodeIndex dbg_load_code_ix = 0;
+#endif
+
+static int entries_at_start_staging = 0;
+
+void module_start_staging(void)
+{
+ IndexTable* src = &module_tables[erts_active_code_ix()];
+ IndexTable* dst = &module_tables[erts_staging_code_ix()];
+ Module* src_mod;
+ Module* dst_mod;
+ int i, oldsz, newsz;
+
+ ASSERT(dbg_load_code_ix == -1);
+ ASSERT(dst->entries <= src->entries);
+
+ /*
+ * Make sure our existing modules are up-to-date
+ */
+ for (i = 0; i < dst->entries; i++) {
+ src_mod = (Module*) erts_index_lookup(src, i);
+ dst_mod = (Module*) erts_index_lookup(dst, i);
+ ASSERT(src_mod->module == dst_mod->module);
+
+ dst_mod->curr = src_mod->curr;
+ dst_mod->old = src_mod->old;
+ }
+
+ /*
+ * Copy all new modules from active table
+ */
+ oldsz = index_table_sz(dst);
+ for (i = dst->entries; i < src->entries; i++) {
+ src_mod = (Module*) erts_index_lookup(src, i);
+ dst_mod = (Module*) index_put_entry(dst, src_mod);
+ ASSERT(dst_mod != src_mod);
+
+ dst_mod->curr = src_mod->curr;
+ dst_mod->old = src_mod->old;
+ }
+ newsz = index_table_sz(dst);
+ erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
+
+ entries_at_start_staging = dst->entries;
+ IF_DEBUG(dbg_load_code_ix = erts_staging_code_ix());
+}
+
+void module_end_staging(int commit)
+{
+ ASSERT(dbg_load_code_ix == erts_staging_code_ix());
+
+ if (!commit) { /* abort */
+ IndexTable* tab = &module_tables[erts_staging_code_ix()];
+ int oldsz, newsz;
+
+ ASSERT(entries_at_start_staging <= tab->entries);
+ oldsz = index_table_sz(tab);
+ index_erase_latest_from(tab, entries_at_start_staging);
+ newsz = index_table_sz(tab);
+ erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
+ }
+
+ IF_DEBUG(dbg_load_code_ix = -1);
}
+
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 694e4ab72f..5235528e98 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2010. 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
@@ -24,28 +24,72 @@
#include "index.h"
#endif
+struct erl_module_instance {
+ BeamInstr* code;
+ int code_length; /* Length of loaded code in bytes. */
+ unsigned catches;
+ struct erl_module_nif* nif;
+ int num_breakpoints;
+ int num_traced_exports;
+};
typedef struct erl_module {
IndexSlot slot; /* Must be located at top of struct! */
int module; /* Atom index for module (not tagged). */
- BeamInstr* code;
- BeamInstr* old_code;
- int code_length; /* Length of loaded code in bytes. */
- int old_code_length; /* Length of old loaded code in bytes */
- unsigned catches, old_catches;
- struct erl_module_nif* nif;
- struct erl_module_nif* old_nif;
+ struct erl_module_instance curr;
+ struct erl_module_instance old; /* protected by "old_code" rwlock */
} Module;
-Module* erts_get_module(Eterm mod);
+Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix);
Module* erts_put_module(Eterm mod);
void init_module_table(void);
+void module_start_staging(void);
+void module_end_staging(int commit);
void module_info(int, void *);
-Module *module_code(int);
-int module_code_size(void);
+Module *module_code(int, ErtsCodeIndex);
+int module_code_size(ErtsCodeIndex);
int module_table_sz(void);
+ERTS_GLB_INLINE void erts_rwlock_old_code(ErtsCodeIndex);
+ERTS_GLB_INLINE void erts_rwunlock_old_code(ErtsCodeIndex);
+ERTS_GLB_INLINE void erts_rlock_old_code(ErtsCodeIndex);
+ERTS_GLB_INLINE void erts_runlock_old_code(ErtsCodeIndex);
+#ifdef ERTS_ENABLE_LOCK_CHECK
+int erts_is_old_code_rlocked(ErtsCodeIndex);
+#endif
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+extern erts_smp_rwmtx_t the_old_code_rwlocks[ERTS_NUM_CODE_IX];
+
+ERTS_GLB_INLINE void erts_rwlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_rwlock(&the_old_code_rwlocks[code_ix]);
+}
+ERTS_GLB_INLINE void erts_rwunlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_rwunlock(&the_old_code_rwlocks[code_ix]);
+}
+ERTS_GLB_INLINE void erts_rlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_rlock(&the_old_code_rwlocks[code_ix]);
+}
+ERTS_GLB_INLINE void erts_runlock_old_code(ErtsCodeIndex code_ix)
+{
+ erts_smp_rwmtx_runlock(&the_old_code_rwlocks[code_ix]);
+}
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ERTS_GLB_INLINE int erts_is_old_code_rlocked(ErtsCodeIndex code_ix)
+{
+ return erts_smp_lc_rwmtx_is_rlocked(&the_old_code_rwlocks[code_ix]);
+}
#endif
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+
+#endif /* !__MODULE_H__ */
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 9b168889dd..1e5ae46bfa 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -62,11 +62,8 @@ label L
i_func_info I a a I
int_code_end
-i_trace_breakpoint
-i_mtrace_breakpoint
+i_generic_breakpoint
i_debug_breakpoint
-i_count_breakpoint
-i_time_breakpoint
i_return_time_trace
i_return_to_trace
i_yield
@@ -522,7 +519,6 @@ apply_bif
call_nif
call_error_handler
error_action_code
-call_traced_function
return_trace
#
@@ -829,16 +825,20 @@ call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext
#
-# The apply/2 and apply/3 BIFs are instructions.
+# apply/2 is an instruction, not a BIF.
#
call_ext u==2 u$func:erlang:apply/2 => i_apply_fun
call_ext_last u==2 u$func:erlang:apply/2 D => i_apply_fun_last D
call_ext_only u==2 u$func:erlang:apply/2 => i_apply_fun_only
-call_ext u==3 u$func:erlang:apply/3 => i_apply
-call_ext_last u==3 u$func:erlang:apply/3 D => i_apply_last D
-call_ext_only u==3 u$func:erlang:apply/3 => i_apply_only
+#
+# The apply/3 BIF is an instruction.
+#
+
+call_ext u==3 u$bif:erlang:apply/3 => i_apply
+call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D
+call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only
#
# The exit/1 and throw/1 BIFs never execute the instruction following them;
@@ -1021,7 +1021,7 @@ bif0 u$bif:erlang:node/0 Dst=d => node Dst
bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => i_get Src Dst
-bif2 Jump=j u$bif:erlang:element/2 S1=s S2=s Dst=d => gen_element(Jump, S1, S2, Dst)
+bif2 Jump=j u$bif:erlang:element/2 S1=s S2=rxy Dst=d => gen_element(Jump, S1, S2, Dst)
bif1 Fail Bif Literal=q Dst => move Literal x | bif1 Fail Bif x Dst
bif1 p Bif S1 Dst => bif1_body Bif S1 Dst
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index f1cfa8df39..db0e78b1a7 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -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
@@ -67,7 +67,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define HTTP_HDR_HASH_SIZE 53
#define HTTP_METH_HASH_SIZE 13
-#define HTTP_MAX_NAME_LEN 20
+#define HTTP_MAX_NAME_LEN 50
static char tspecial[128];
@@ -460,11 +460,9 @@ int packet_get_length(enum PacketParseType htype,
hp = (struct tpkt_head*) ptr;
if (hp->vrsn == TPKT_VRSN) {
plen = get_int16(hp->packet_length) - hlen;
- if (plen < 0)
- goto error;
- }
- else
+ } else {
goto error;
+ }
goto remain;
}
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index 26d64887d0..c626cb2780 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2010. 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
@@ -93,19 +93,14 @@ reg_safe_write_lock(Process *c_p, ErtsProcLocks *c_p_locks)
reg_write_lock();
}
+#endif
+
static ERTS_INLINE int
is_proc_alive(Process *p)
{
- int res;
- erts_pix_lock_t *pixlck = ERTS_PID2PIXLOCK(p->id);
- erts_pix_lock(pixlck);
- res = !p->is_exiting;
- erts_pix_unlock(pixlck);
- return res;
+ return !ERTS_PROC_IS_EXITING(p);
}
-#endif
-
void register_info(int to, void *to_arg)
{
int lock = !ERTS_IS_CRASH_DUMPING;
@@ -180,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;
}
@@ -209,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;
}
@@ -217,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;
}
@@ -229,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);
@@ -296,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;
@@ -389,8 +385,7 @@ erts_whereis_name(Process *c_p,
}
#else
if (rp->p
- && ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X)
- || rp->p->status != P_EXITING))
+ && ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X) || is_proc_alive(rp->p)))
*proc = rp->p;
else
*proc = NULL;
@@ -409,19 +404,19 @@ erts_whereis_name(Process *c_p,
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;
}
}
@@ -435,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();
@@ -497,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;
@@ -511,20 +506,20 @@ int erts_unregister_name(Process *c_p,
if (port != rp->pt) {
#ifdef ERTS_SMP
if (port) {
- ERTS_SMP_LC_ASSERT(port != c_prt);
- erts_smp_port_unlock(port);
+ ASSERT(port != c_prt);
+ 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
@@ -534,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);
@@ -551,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);
}
@@ -570,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 41389c8734..05bff430e3 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -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
@@ -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
@@ -39,10 +34,10 @@
#define ENABLE_CHILD_WAITER_THREAD 1
#endif
+#define ERTS_I64_LITERAL(X) X##LL
+
#if defined (__WIN32__)
# include "erl_win_sys.h"
-#elif defined (VXWORKS)
-# include "erl_vxworks_sys.h"
#else
# include "erl_unix_sys.h"
#ifndef UNIX
@@ -91,14 +86,22 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# endif
#endif
-#ifdef __GNUC__
-# if __GNUC__ < 3 && (__GNUC__ != 2 || __GNUC_MINOR__ < 96)
-# define ERTS_LIKELY(BOOL) (BOOL)
-# define ERTS_UNLIKELY(BOOL) (BOOL)
-# else
-# define ERTS_LIKELY(BOOL) __builtin_expect((BOOL), !0)
-# define ERTS_UNLIKELY(BOOL) __builtin_expect((BOOL), 0)
-# endif
+#if !defined(__GNUC__)
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) 0
+#elif !defined(__GNUC_MINOR__)
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
+ ((__GNUC__ << 24) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
+#elif !defined(__GNUC_PATCHLEVEL__)
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
+ (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12)) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
+#else
+# define ERTS_AT_LEAST_GCC_VSN__(MAJ, MIN, PL) \
+ (((__GNUC__ << 24) | (__GNUC_MINOR__ << 12) | __GNUC_PATCHLEVEL__) >= (((MAJ) << 24) | ((MIN) << 12) | (PL)))
+#endif
+
+#if ERTS_AT_LEAST_GCC_VSN__(2, 96, 0)
+# define ERTS_LIKELY(BOOL) __builtin_expect((BOOL), !0)
+# define ERTS_UNLIKELY(BOOL) __builtin_expect((BOOL), 0)
#else
# define ERTS_LIKELY(BOOL) (BOOL)
# define ERTS_UNLIKELY(BOOL) (BOOL)
@@ -113,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
@@ -172,10 +185,16 @@ 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
+#undef __deprecated
+#if ERTS_AT_LEAST_GCC_VSN__(3, 0, 0)
+# define __deprecated __attribute__((deprecated))
+#else
+# define __deprecated
+#endif
+#if ERTS_AT_LEAST_GCC_VSN__(3, 0, 4)
+# define erts_align_attribute(SZ) __attribute__ ((aligned (SZ)))
+#else
+# define erts_align_attribute(SZ)
#endif
/* In VC++, noreturn is a declspec that has to be before the types,
@@ -185,12 +204,6 @@ int real_printf(const char *fmt, ...);
#if __GNUC__
# define __decl_noreturn
# define __noreturn __attribute__((noreturn))
-# undef __deprecated
-# if __GNUC__ >= 3
-# define __deprecated __attribute__((deprecated))
-# else
-# define __deprecated
-# endif
#else
# if defined(__WIN32__) && defined(_MSC_VER)
# define __noreturn
@@ -199,7 +212,6 @@ int real_printf(const char *fmt, ...);
# define __noreturn
# define __decl_noreturn
# endif
-# define __deprecated
#endif
/*
@@ -229,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
@@ -239,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
@@ -365,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)))
@@ -471,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 */
@@ -513,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 */
@@ -587,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;
@@ -626,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);
@@ -635,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);
@@ -697,10 +729,13 @@ 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, char *value);
@@ -858,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)
@@ -971,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
@@ -1023,30 +1014,51 @@ 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)
+
+/************************************************************************
+ * If a filename in for example list_dir is not in the right encoding, it
+ * will be skipped in the resulting list, but depending on a startup setting
+ * we will inform the user in different ways. These macros define the
+ * different reactions to wrongly coded filenames. In the error case an
+ * exception will be thrown by prim_file.
+ ************************************************************************/
+#define ERL_FILENAME_WARNING_WARNING (0)
+#define ERL_FILENAME_WARNING_IGNORE (1)
+#define ERL_FILENAME_WARNING_ERROR (2)
+
+/***********************************************************************
+ * The user can request a range of character that he/she consider
+ * printable. Currently this can be either latin1 or unicode, but
+ * in the future a set of ranges, or languages, could be specified.
+ ***********************************************************************/
+#define ERL_PRINTABLE_CHARACTERS_LATIN1 (0)
+#define ERL_PRINTABLE_CHARACTERS_UNICODE (1)
int erts_get_native_filename_encoding(void);
/* The set function is only to be used by erl_init! */
-void erts_set_user_requested_filename_encoding(int encoding);
+void erts_set_user_requested_filename_encoding(int encoding, int warning);
int erts_get_user_requested_filename_encoding(void);
+int erts_get_filename_warning_type(void);
+/* This function is called from erl_init. The setting is read by BIF's
+ in io/io_lib. Setting is not atomic. */
+void erts_set_printable_characters(int range);
+/* Get the setting (ERL_PRINTABLE_CHARACTERS_{LATIN1|UNICODE} */
+int erts_get_printable_characters(void);
void erts_init_sys_common_misc(void);
#endif
-
diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c
index 932d157cd8..2fd8e0cf00 100644
--- a/erts/emulator/beam/time.c
+++ b/erts/emulator/beam/time.c
@@ -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
@@ -105,7 +105,14 @@ static ErlTimer *tiw_min_ptr;
/* END tiw_lock protected variables */
/* Actual interval time chosen by sys_init_time() */
-static int itime; /* Constant after init */
+
+#if SYS_CLOCK_RESOLUTION == 1
+# define TIW_ITIME 1
+# define TIW_ITIME_IS_CONSTANT
+#else
+static int tiw_itime; /* Constant after init */
+# define TIW_ITIME tiw_itime
+#endif
erts_smp_atomic32_t do_time; /* set at clock interrupt */
static ERTS_INLINE erts_short_time_t do_time_read(void)
@@ -123,7 +130,7 @@ static ERTS_INLINE void do_time_init(void)
erts_smp_atomic32_init_nob(&do_time, 0);
}
-/* get the time (in units of itime) to the next timeout,
+/* get the time (in units of TIW_ITIME) to the next timeout,
or -1 if there are no timeouts */
static erts_short_time_t next_time_internal(void) /* PRE: tiw_lock taken by caller */
@@ -305,11 +312,18 @@ erts_timer_wheel_memory_size(void)
void
erts_init_time(void)
{
- int i;
+ int i, itime;
/* system dependent init; must be done before do_time_init()
if timer thread is enabled */
itime = erts_init_time_sup();
+#ifdef TIW_ITIME_IS_CONSTANT
+ if (itime != TIW_ITIME) {
+ erl_exit(ERTS_ABORT_EXIT, "timer resolution mismatch %d != %d", itime, TIW_ITIME);
+ }
+#else
+ tiw_itime = itime;
+#endif
erts_smp_mtx_init(&tiw_lock, "timer_wheel");
@@ -340,7 +354,7 @@ insert_timer(ErlTimer* p, Uint t)
*
* (x + y - 1)/y is precisely the "number of bins" formula.
*/
- ticks = (t + itime - 1) / itime;
+ ticks = (t + (TIW_ITIME - 1)) / TIW_ITIME;
/*
* Ticks must be a Uint64, or the addition may overflow here,
@@ -455,7 +469,7 @@ erts_time_left(ErlTimer *p)
erts_smp_mtx_unlock(&tiw_lock);
- return (Uint) left * itime;
+ return (Uint) left * TIW_ITIME;
}
#ifdef DEBUG
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index bd708ceee6..a8f15fdc38 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -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
@@ -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
@@ -268,16 +269,42 @@ list_length(Eterm list)
return i;
}
-Uint erts_fit_in_bits(Uint n)
+static const struct {
+ Sint64 mask;
+ int bits;
+} fib_data[] = {{ERTS_I64_LITERAL(0x2), 1},
+ {ERTS_I64_LITERAL(0xc), 2},
+ {ERTS_I64_LITERAL(0xf0), 4},
+ {ERTS_I64_LITERAL(0xff00), 8},
+ {ERTS_I64_LITERAL(0xffff0000), 16},
+ {ERTS_I64_LITERAL(0xffffffff00000000), 32}};
+
+static ERTS_INLINE int
+fit_in_bits(Sint64 value, int start)
{
- Uint i;
+ int bits = 0;
+ int i;
- i = 0;
- while (n > 0) {
- i++;
- n >>= 1;
- }
- return i;
+ for (i = start; i >= 0; i--) {
+ if (value & fib_data[i].mask) {
+ value >>= fib_data[i].bits;
+ bits |= fib_data[i].bits;
+ }
+ }
+
+ bits++;
+
+ return bits;
+}
+
+int erts_fit_in_bits_int64(Sint64 value)
+{
+ return fit_in_bits(value, 5);
+}
+
+int erts_fit_in_bits_int32(Sint32 value)
+{
+ return fit_in_bits((Sint64) (Uint32) value, 4);
}
int
@@ -344,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;
}
@@ -1640,12 +1667,20 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
}
#ifndef ERTS_SMP
- if (
#ifdef USE_THREADS
- !erts_get_scheduler_data() || /* Must be scheduler thread */
+ p = NULL;
+ if (erts_get_scheduler_data()) /* Must be scheduler thread */
#endif
- (p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0)) == NULL
- || p->status == P_RUNNING) {
+ {
+ p = erts_whereis_process(NULL, 0, am_error_logger, 0, 0);
+ if (p) {
+ erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
+ if (state & ERTS_PSFLG_RUNNING)
+ p = NULL;
+ }
+ }
+
+ if (!p) {
/* buf *always* points to a null terminated string */
erts_fprintf(stderr, "(no error logger present) %T: \"%s\"\n",
tag, buf);
@@ -2982,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;
@@ -3080,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;
@@ -3145,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:
@@ -3240,7 +3276,7 @@ ptimer_timeout(ErtsSmpPTimer *ptimer)
ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS,
ERTS_P2P_FLG_ALLOW_OTHER_X);
if (p) {
- if (!p->is_exiting
+ if (!ERTS_PROC_IS_EXITING(p)
&& !(ptimer->timer.flags & ERTS_PTMR_FLG_CANCELLED)) {
ASSERT(*ptimer->timer.timer_ref == ptimer);
*ptimer->timer.timer_ref = NULL;
@@ -3436,6 +3472,254 @@ void erts_silence_warn_unused_result(long unused)
}
+/*
+ * Interval counts
+ */
+void
+erts_interval_init(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ erts_atomic_init_nob(&icp->counter.atomic, 0);
+#else
+ erts_dw_aint_t dw;
+#ifdef ETHR_SU_DW_NAINT_T__
+ dw.dw_sint = 0;
+#else
+ dw.sint[ERTS_DW_AINT_HIGH_WORD] = 0;
+ dw.sint[ERTS_DW_AINT_LOW_WORD] = 0;
+#endif
+ erts_dw_atomic_init_nob(&icp->counter.atomic, &dw);
+
+#endif
+#ifdef DEBUG
+ icp->smp_api = 0;
+#endif
+}
+
+void
+erts_smp_interval_init(erts_interval_t *icp)
+{
+#ifdef ERTS_SMP
+ erts_interval_init(icp);
+#else
+ icp->counter.not_atomic = 0;
+#endif
+#ifdef DEBUG
+ icp->smp_api = 1;
+#endif
+}
+
+static ERTS_INLINE Uint64
+step_interval_nob(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_inc_read_nob(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &exp);
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_nob(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ }
+#endif
+}
+
+static ERTS_INLINE Uint64
+step_interval_relb(erts_interval_t *icp)
+{
+#ifdef ARCH_64
+ return (Uint64) erts_atomic_inc_read_relb(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &exp);
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_relb(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ }
+#endif
+}
+
+
+static ERTS_INLINE Uint64
+ensure_later_interval_nob(erts_interval_t *icp, Uint64 ic)
+{
+ Uint64 curr_ic;
+#ifdef ARCH_64
+ curr_ic = (Uint64) erts_atomic_read_nob(&icp->counter.atomic);
+ if (curr_ic > ic)
+ return curr_ic;
+ return (Uint64) erts_atomic_inc_read_nob(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_nob(&icp->counter.atomic, &exp);
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_nob(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+ }
+#endif
+}
+
+
+static ERTS_INLINE Uint64
+ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
+{
+ Uint64 curr_ic;
+#ifdef ARCH_64
+ curr_ic = (Uint64) erts_atomic_read_acqb(&icp->counter.atomic);
+ if (curr_ic > ic)
+ return curr_ic;
+ return (Uint64) erts_atomic_inc_read_acqb(&icp->counter.atomic);
+#else
+ erts_dw_aint_t exp;
+
+ erts_dw_atomic_read_acqb(&icp->counter.atomic, &exp);
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+
+ while (1) {
+ erts_dw_aint_t new = exp;
+
+#ifdef ETHR_SU_DW_NAINT_T__
+ new.dw_sint++;
+#else
+ new.sint[ERTS_DW_AINT_LOW_WORD]++;
+ if (new.sint[ERTS_DW_AINT_LOW_WORD] == 0)
+ new.sint[ERTS_DW_AINT_HIGH_WORD]++;
+#endif
+
+ if (erts_dw_atomic_cmpxchg_acqb(&icp->counter.atomic, &new, &exp))
+ return erts_interval_dw_aint_to_val__(&new);
+
+ curr_ic = erts_interval_dw_aint_to_val__(&exp);
+ if (curr_ic > ic)
+ return curr_ic;
+ }
+#endif
+}
+
+Uint64
+erts_step_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return step_interval_nob(icp);
+}
+
+Uint64
+erts_step_interval_relb(erts_interval_t *icp)
+{
+ ASSERT(!icp->smp_api);
+ return step_interval_relb(icp);
+}
+
+Uint64
+erts_smp_step_interval_nob(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return step_interval_nob(icp);
+#else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+Uint64
+erts_smp_step_interval_relb(erts_interval_t *icp)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return step_interval_relb(icp);
+#else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+Uint64
+erts_ensure_later_interval_nob(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(!icp->smp_api);
+ return ensure_later_interval_nob(icp, ic);
+}
+
+Uint64
+erts_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(!icp->smp_api);
+ return ensure_later_interval_acqb(icp, ic);
+}
+
+Uint64
+erts_smp_ensure_later_interval_nob(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return ensure_later_interval_nob(icp, ic);
+#else
+ if (icp->counter.not_atomic > ic)
+ return icp->counter.not_atomic;
+ else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+Uint64
+erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
+{
+ ASSERT(icp->smp_api);
+#ifdef ERTS_SMP
+ return ensure_later_interval_acqb(icp, ic);
+#else
+ if (icp->counter.not_atomic > ic)
+ return icp->counter.not_atomic;
+ else
+ return ++icp->counter.not_atomic;
+#endif
+}
+
+
#ifdef DEBUG
/*
* Handy functions when using a debugger - don't use in the code!
@@ -3468,7 +3752,7 @@ Process *p;
void ppi(Eterm pid)
{
- pp(erts_pid2proc_unlocked(pid));
+ pp(erts_proc_lookup(pid));
}
void td(Eterm x)
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 912f5d3d8b..69acfc9dfd 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -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
@@ -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)
@@ -428,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
@@ -439,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. */
@@ -503,6 +507,10 @@ struct t_data
Uint64 written;
} sendfile;
#endif /* HAVE_SENDFILE */
+ struct {
+ Sint64 offset;
+ Sint64 length;
+ } fallocate;
} c;
char b[1];
};
@@ -781,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));
@@ -803,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);
}
@@ -1144,7 +1149,7 @@ static void invoke_read_line(void *data)
{
struct t_data *d = (struct t_data *) data;
int status;
- size_t read_size;
+ size_t read_size = 0;
int local_loop = (d->again == 0);
DTRACE_INVOKE_SETUP(FILE_READ_LINE);
@@ -1155,7 +1160,14 @@ static void invoke_read_line(void *data)
/* Need more place */
ErlDrvSizeT need = (d->c.read_line.read_size >= DEFAULT_LINEBUF_SIZE) ?
d->c.read_line.read_size + DEFAULT_LINEBUF_SIZE : DEFAULT_LINEBUF_SIZE;
- ErlDrvBinary *newbin = driver_alloc_binary(need);
+ ErlDrvBinary *newbin;
+#if !ALWAYS_READ_LINE_AHEAD
+ /* Use read_ahead size if need does not exceed it */
+ if (need < (d->c.read_line.binp)->orig_size &&
+ d->c.read_line.read_ahead)
+ need = (d->c.read_line.binp)->orig_size;
+#endif
+ newbin = driver_alloc_binary(need);
if (newbin == NULL) {
d->result_ok = 0;
d->errInfo.posix_errno = ENOMEM;
@@ -1862,6 +1874,9 @@ static void invoke_open(void *data)
}
d->result_ok = status;
+ if (!status) {
+ d->fd = FILE_FD_INVALID;
+ }
DTRACE_INVOKE_RETURN(FILE_OPEN);
}
@@ -1953,6 +1968,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;
@@ -2216,6 +2242,49 @@ 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 {
+ EF_FREE(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
@@ -2348,6 +2417,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;
@@ -2373,8 +2443,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);
@@ -2436,7 +2508,6 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
}
free_readdir(data);
break;
- /* See file_stop */
case FILE_CLOSE:
if (d->reply) {
TRACE_C('K');
@@ -2496,6 +2567,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();
}
@@ -2506,6 +2586,7 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
driver_set_timer(desc->port, desc->write_delay);
}
cq_execute(desc);
+
}
@@ -2745,6 +2826,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;
}
@@ -2958,6 +3040,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..e085c262b0 100644
--- a/erts/emulator/drivers/common/gzio.c
+++ b/erts/emulator/drivers/common/gzio.c
@@ -13,6 +13,7 @@
# include "config.h"
#endif
#include <stdio.h>
+#include <string.h> /* ssize_t on Mac OS X */
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -21,11 +22,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
@@ -319,7 +315,7 @@ local int get_byte(s)
if (s->z_eof) return EOF;
if (s->stream.avail_in == 0) {
#ifdef UNIX
- size_t res;
+ ssize_t res;
errno = 0;
res = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE);
if (res == 0) {
@@ -492,7 +488,7 @@ erts_gzread(gzFile file, voidp buf, unsigned len)
}
if (s->stream.avail_in == 0 && !s->z_eof) {
#ifdef UNIX
- size_t res;
+ ssize_t res;
errno = 0;
res = ERTS_GZREAD(s->file, s->inbuf, Z_BUFSIZE);
if (res == 0) {
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 8f4fff0f40..fb128fff7d 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. 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
@@ -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))
@@ -535,6 +514,12 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#endif /* __WIN32__ */
+#ifdef HAVE_SOCKLEN_T
+# define SOCKLEN_T socklen_t
+#else
+# define SOCKLEN_T int
+#endif
+
#include "packet_parser.h"
#define get_int24(s) ((((unsigned char*) (s))[0] << 16) | \
@@ -675,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 */
@@ -685,13 +671,15 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_LOPT_EXITONCLOSE 26 /* exit port on active close or not ! */
#define INET_LOPT_TCP_HIWTRMRK 27 /* set local high watermark */
#define INET_LOPT_TCP_LOWTRMRK 28 /* set local low watermark */
-#define INET_LOPT_BIT8 29 /* set 8 bit detection */
+ /* 29 unused */
#define INET_LOPT_TCP_SEND_TIMEOUT 30 /* set send timeout */
#define INET_LOPT_TCP_DELAY_SEND 31 /* Delay sends until next poll */
#define INET_LOPT_PACKET_SIZE 32 /* Max packet size */
#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
@@ -720,12 +708,6 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_IFOPT_FLAGS 6
#define INET_IFOPT_HWADDR 7
-/* INET_LOPT_BIT8 options */
-#define INET_BIT8_CLEAR 0
-#define INET_BIT8_SET 1
-#define INET_BIT8_ON 2
-#define INET_BIT8_OFF 3
-
/* INET_REQ_GETSTAT enumeration */
#define INET_STAT_RECV_CNT 1
#define INET_STAT_RECV_MAX 2
@@ -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);
@@ -921,7 +905,6 @@ typedef struct {
int mode; /* BINARY | LIST
(affect how to interpret hsz) */
int exitf; /* exit port on close or not */
- int bit8f; /* check if data has bit number 7 set */
int deliver; /* Delivery mode, TERM or PORT */
ErlDrvTermData caller; /* recipient of sync reply */
@@ -940,8 +923,6 @@ typedef struct {
int sfamily; /* address family */
enum PacketParseType htype; /* header type (TCP only?) */
unsigned int psize; /* max packet size (TCP only?) */
- int bit8; /* set if bit8f==true and data some data
- seen had the 7th bit set */
inet_address remote; /* remote address for connected sockets */
inet_address peer_addr; /* fake peer address */
inet_address name_addr; /* fake local address */
@@ -952,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 */
@@ -1191,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 */
@@ -1895,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];
@@ -1910,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 +
@@ -1936,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 +
@@ -1961,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);
}
@@ -1973,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)
@@ -1984,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)
@@ -1995,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 */
@@ -2007,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;
}
@@ -2036,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
@@ -2055,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
@@ -2078,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:
@@ -2187,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);
}
}
@@ -2284,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);
}
}
@@ -2338,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);
}
}
@@ -2369,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} */
@@ -2378,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);
}
}
@@ -2406,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} */
@@ -2417,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);
}
}
@@ -2470,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);
@@ -2524,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] */
@@ -2538,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;
}
}
@@ -3131,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);
}
/*
@@ -3154,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] */
@@ -3166,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;
}
}
@@ -3201,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);
}
/*
@@ -3220,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;
}
@@ -3241,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);
}
/*
@@ -3332,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);
}
/*
@@ -3359,21 +3346,10 @@ 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);
}
-/* scan buffer for bit 7 */
-static void scanbit8(inet_descriptor* desc, const char* buf, int len)
-{
- int c;
-
- if (!desc->bit8f || desc->bit8) return;
- c = 0;
- while(len--) c |= *buf++;
- desc->bit8 = ((c & 0x80) != 0);
-}
-
/*
** active=TRUE:
** (NOTE! distribution MUST use active=TRUE, deliver=PORT)
@@ -3391,8 +3367,6 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len)
packet_get_body(desc->inet.htype, &body, &bodylen);
- scanbit8(INETP(desc), body, bodylen);
-
if (desc->inet.deliver == INET_DELIVER_PORT) {
code = inet_port_data(INETP(desc), body, bodylen);
}
@@ -3424,8 +3398,6 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len
packet_get_body(desc->inet.htype, &body, &bodylen);
offs = body - bin->orig_bytes; /* body offset now */
- scanbit8(INETP(desc), body, bodylen);
-
if (desc->inet.deliver == INET_DELIVER_PORT)
code = inet_port_binary_data(INETP(desc), bin, offs, bodylen);
else if ((code=packet_parse(desc->inet.htype, buf, len, &desc->http_state,
@@ -3451,8 +3423,6 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz,
{
int code;
- scanbit8(desc, bin->orig_bytes+offs, len);
-
if (desc->active == INET_PASSIVE)
/* "inet" is actually for both UDP and SCTP, as well as TCP! */
return inet_async_binary_data(desc, hsz, bin, offs, len, extra);
@@ -3527,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);
@@ -5313,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
@@ -5382,13 +5309,8 @@ static int setopt_prio_tos_trick
int res;
int res_prio;
int res_tos;
-#ifdef HAVE_SOCKLEN_T
- socklen_t
-#else
- int
-#endif
- tmp_arg_sz_prio = sizeof(tmp_ival_prio),
- tmp_arg_sz_tos = sizeof(tmp_ival_tos);
+ SOCKLEN_T tmp_arg_sz_prio = sizeof(tmp_ival_prio);
+ SOCKLEN_T tmp_arg_sz_tos = sizeof(tmp_ival_tos);
res_prio = sock_getopt(fd, SOL_SOCKET, SO_PRIORITY,
(char *) &tmp_ival_prio, &tmp_arg_sz_prio);
@@ -5532,29 +5454,6 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
desc->exitf = ival;
continue;
- case INET_LOPT_BIT8:
- DEBUGF(("inet_set_opts(%ld): s=%d, BIT8=%d\r\n",
- (long)desc->port, desc->s, ival));
- switch(ival) {
- case INET_BIT8_ON:
- desc->bit8f = 1;
- desc->bit8 = 0;
- break;
- case INET_BIT8_OFF:
- desc->bit8f = 0;
- desc->bit8 = 0;
- break;
- case INET_BIT8_CLEAR:
- desc->bit8f = 1;
- desc->bit8 = 0;
- break;
- case INET_BIT8_SET:
- desc->bit8f = 1;
- desc->bit8 = 1;
- break;
- }
- continue;
-
case INET_LOPT_TCP_HIWTRMRK:
if (desc->stype == SOCK_STREAM) {
tcp_descriptor* tdesc = (tcp_descriptor*) desc;
@@ -5575,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;
@@ -5636,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;
@@ -5743,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;
@@ -5774,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)
@@ -6075,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;
@@ -6437,15 +6376,6 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
put_int32(desc->exitf, ptr);
continue;
- case INET_LOPT_BIT8:
- *ptr++ = opt;
- if (desc->bit8f) {
- put_int32(desc->bit8, ptr);
- } else {
- put_int32(INET_BIT8_OFF, ptr);
- }
- continue;
-
case INET_LOPT_TCP_HIWTRMRK:
if (desc->stype == SOCK_STREAM) {
*ptr++ = opt;
@@ -6466,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;
@@ -6572,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;
@@ -6876,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: */
@@ -6948,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;
@@ -7348,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;
@@ -7398,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 */
@@ -7430,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,
@@ -7504,8 +7499,6 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
desc->mode = INET_MODE_LIST; /* list mode */
desc->exitf = 1; /* exit port when close on active
socket */
- desc->bit8f = 0;
- desc->bit8 = 0;
desc->deliver = INET_DELIVER_TERM; /* standard term format */
desc->active = INET_PASSIVE; /* start passive */
desc->oph = NULL;
@@ -7514,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;
@@ -7813,8 +7814,8 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
desc->state = INET_STATE_BOUND;
if ((port = inet_address_port(&local)) == 0) {
- len = sizeof(local);
- sock_name(desc->s, (struct sockaddr*) &local, (unsigned int*)&len);
+ SOCKLEN_T adrlen = sizeof(local);
+ sock_name(desc->s, &local.sa, &adrlen);
port = inet_address_port(&local);
}
port = sock_ntohs(port);
@@ -7833,7 +7834,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (!IS_CONNECTED(desc))
return ctl_error(ENOTCONN, rbuf, rsize);
- if (!desc->stype == SOCK_STREAM)
+ if (desc->stype != SOCK_STREAM)
return ctl_error(EINVAL, rbuf, rsize);
if (*buf == 1 && !desc->is_ignored) {
@@ -7849,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];
@@ -7901,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);
}
@@ -7912,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;
@@ -7932,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;
@@ -8083,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));
@@ -8092,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;
@@ -8115,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;
@@ -8133,7 +8154,6 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
/* Some flags must be inherited at this point */
copy_desc->inet.mode = desc->inet.mode;
copy_desc->inet.exitf = desc->inet.exitf;
- copy_desc->inet.bit8f = desc->inet.bit8f;
copy_desc->inet.deliver = desc->inet.deliver;
copy_desc->inet.htype = desc->inet.htype;
copy_desc->inet.psize = desc->inet.psize;
@@ -8153,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;
@@ -8185,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);
}
@@ -8609,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);
}
@@ -8789,7 +8816,7 @@ static int tcp_recv_error(tcp_descriptor* desc, int err)
if (desc->inet.exitf)
driver_exit(desc->inet.port, err);
else
- desc_close(INETP(desc));
+ desc_close_read(INETP(desc));
}
return -1;
}
@@ -9350,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;
}
@@ -9360,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;
}
@@ -9371,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);
}
}
@@ -9731,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()));
@@ -9741,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))) {
@@ -9838,7 +9882,6 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err)
/* Some flags must be inherited at this point */
copy_desc->inet.mode = desc->inet.mode;
copy_desc->inet.exitf = desc->inet.exitf;
- copy_desc->inet.bit8f = desc->inet.bit8f;
copy_desc->inet.deliver = desc->inet.deliver;
copy_desc->inet.htype = desc->inet.htype;
copy_desc->inet.psize = desc->inet.psize;
@@ -10240,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;
@@ -11013,7 +11057,7 @@ subs_list *subs;
static void send_to_subscribers
(
- ErlDrvPort port,
+ ErlDrvTermData port,
subs_list *subs,
int free_subs,
ErlDrvTermData msg[],
@@ -11030,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..8912d148a5 100644
--- a/erts/emulator/drivers/unix/ttsl_drv.c
+++ b/erts/emulator/drivers/unix/ttsl_drv.c
@@ -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
@@ -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 b250bac4dc..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 %lu bytes of memory\n",
- (unsigned long)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 %lu bytes of memory\n",
- (unsigned long)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..e908c956ae 100644
--- a/erts/emulator/drivers/win32/registry_drv.c
+++ b/erts/emulator/drivers/win32/registry_drv.c
@@ -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
@@ -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_efile.c b/erts/emulator/drivers/win32/win_efile.c
index dc7add01f7..1059fa5c3a 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -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);
/*
@@ -909,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;
@@ -1021,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);
@@ -1157,8 +1159,11 @@ char* buf; /* Buffer to read into. */
size_t count; /* Number of bytes to read. */
size_t* pBytesRead; /* Where to return number of bytes read. */
{
- if (!ReadFile((HANDLE) fd, buf, count, (DWORD *) pBytesRead, NULL))
+ DWORD nbytes = 0;
+ if (!ReadFile((HANDLE) fd, buf, count, &nbytes, NULL))
return set_error(errInfo);
+
+ *pBytesRead = nbytes;
return 1;
}
@@ -1384,7 +1389,7 @@ 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 / sizeof(WCHAR),0);
@@ -1558,3 +1563,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_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 23ced284bf..fa99c817f0 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -609,8 +609,8 @@ static Uint *hipe_find_emu_address(Eterm mod, Eterm name, unsigned int arity)
Uint *code_base;
int i, n;
- modp = erts_get_module(mod);
- if (modp == NULL || (code_base = modp->code) == NULL)
+ modp = erts_get_module(mod, erts_active_code_ix());
+ if (modp == NULL || (code_base = modp->curr.code) == NULL)
return NULL;
n = code_base[MI_NUM_FUNCTIONS];
for (i = 0; i < n; ++i) {
@@ -648,7 +648,7 @@ static void *hipe_get_emu_address(Eterm m, Eterm f, unsigned int arity, int is_r
/* if not found, stub it via the export entry */
/* no lock needed around erts_export_get_or_make_stub() */
Export *export_entry = erts_export_get_or_make_stub(m, f, arity);
- address = export_entry->address;
+ address = export_entry->addressv[erts_active_code_ix()];
}
return address;
}
@@ -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_find_export_entry(m, f, BIF_ARG_2))
- goto badfun;
} else
goto badfun;
address = hipe_get_na_nofail(m, f, BIF_ARG_2, 1);
@@ -1799,7 +1791,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
if (BIF_ARG_1 == am_all) {
hipe_purge_all_refs();
- BIF_RET(NIL);
+ BIF_RET(am_ok);
}
if (!term_to_mfa(BIF_ARG_1, &mfa))
@@ -1836,7 +1828,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
caller_mfa->refers_to = NULL;
}
hipe_mfa_info_table_unlock();
- BIF_RET(NIL);
+ BIF_RET(am_ok);
}
diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c
index 7eab1ec2ad..e09988e2c5 100644
--- a/erts/emulator/hipe/hipe_bif2.c
+++ b/erts/emulator/hipe/hipe_bif2.c
@@ -173,3 +173,10 @@ BIF_RETTYPE hipe_debug_bif_wrapper(BIF_ALIST_1)
#endif /* ERTS_ENABLE_LOCK_CHECK && ERTS_SMP */
+
+BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2)
+{
+ erts_printf("hipe_debug_native_called: %T(%T)\r\n", BIF_ARG_1, BIF_ARG_2);
+ BIF_RET(am_ok);
+}
+
diff --git a/erts/emulator/hipe/hipe_bif2.tab b/erts/emulator/hipe/hipe_bif2.tab
index c71b02fbeb..45a395bf57 100644
--- a/erts/emulator/hipe/hipe_bif2.tab
+++ b/erts/emulator/hipe/hipe_bif2.tab
@@ -29,3 +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
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 2a6b2c671b..764b8d180c 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -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).
@@ -165,6 +166,7 @@ gc_bif_interface_2(nbif_put_2, put_2)
gc_bif_interface_1(nbif_hipe_bifs_show_nstack_1, hipe_show_nstack_1)
gc_bif_interface_1(nbif_hipe_bifs_show_pcb_1, hipe_bifs_show_pcb_1)
gc_bif_interface_0(nbif_hipe_bifs_nstack_used_size_0, hipe_bifs_nstack_used_size_0)
+gc_bif_interface_2(nbif_hipe_bifs_debug_native_called, hipe_bifs_debug_native_called_2)
/*
* Arithmetic operators called indirectly by the HiPE compiler.
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 7ca11f8c6c..bf25ba82af 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -188,14 +188,11 @@ void hipe_print_pcb(Process *p)
U("old_htop ", old_htop);
U("old_head ", old_heap);
U("min_heap_..", min_heap_size);
- U("status ", status);
- U("rstatus ", rstatus);
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);
@@ -204,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_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index cbbf1db2e5..0e287908b1 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -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_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 6a3ce5608f..adc8793469 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -360,7 +360,8 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
}
- if (p->status == P_WAITING) {
+
+ if (!(erts_smp_atomic32_read_acqb(&p->state) & ERTS_PSFLG_ACTIVE)) {
for (i = 0; i < p->arity; ++i)
p->arg_reg[i] = reg[i];
goto do_schedule;
@@ -451,10 +452,6 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
case HIPE_MODE_SWITCH_RES_SUSPEND: {
p->i = hipe_beam_pc_resume;
p->arity = 0;
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
- if (p->status != P_SUSPENDED)
- erts_add_to_runq(p);
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
goto do_schedule;
}
case HIPE_MODE_SWITCH_RES_WAIT:
@@ -470,7 +467,8 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
#endif
p->i = hipe_beam_pc_resume;
p->arity = 0;
- p->status = P_WAITING;
+ erts_smp_atomic32_read_band_relb(&p->state,
+ ~ERTS_PSFLG_ACTIVE);
erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
do_schedule:
{
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index 3be821f8f7..ae2c650bd2 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -503,9 +503,7 @@ static int validate_unicode(Eterm arg)
{
if (is_not_small(arg) ||
arg > make_small(0x10FFFFUL) ||
- (make_small(0xD800UL) <= arg && arg <= make_small(0xDFFFUL)) ||
- arg == make_small(0xFFFEUL) ||
- arg == make_small(0xFFFFUL))
+ (make_small(0xD800UL) <= arg && arg <= make_small(0xDFFFUL)))
return 0;
return 1;
}
diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h
index 9e3a156fbc..3f460a5a5c 100644
--- a/erts/emulator/hipe/hipe_native_bif.h
+++ b/erts/emulator/hipe/hipe_native_bif.h
@@ -110,6 +110,9 @@ int hipe_bs_put_big_integer(Eterm, Uint, byte*, unsigned, unsigned);
AEXTERN(Eterm,nbif_check_get_msg,(Process*));
Eterm hipe_check_get_msg(Process*);
+AEXTERN(BIF_RETTYPE,nbif_hipe_bifs_debug_native_called,(Process*,Eterm,Eterm));
+BIF_RETTYPE hipe_bifs_debug_native_called_2(BIF_ALIST_2);
+
/*
* SMP-specific stuff
*/
diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h
index 38509c105b..52b4681cfe 100644
--- a/erts/emulator/hipe/hipe_primops.h
+++ b/erts/emulator/hipe/hipe_primops.h
@@ -80,6 +80,7 @@ PRIMOP_LIST(am_fclearerror_error, &nbif_fclearerror_error)
#ifdef NO_FPE_SIGNALS
PRIMOP_LIST(am_emulate_fpe, &nbif_emulate_fpe)
#endif
+PRIMOP_LIST(am_debug_native_called, &nbif_hipe_bifs_debug_native_called)
#if defined(__sparc__)
#include "hipe_sparc_primops.h"
diff --git a/erts/emulator/hipe/hipe_stack.c b/erts/emulator/hipe/hipe_stack.c
index da462a64e1..53c316ba52 100644
--- a/erts/emulator/hipe/hipe_stack.c
+++ b/erts/emulator/hipe/hipe_stack.c
@@ -130,7 +130,7 @@ struct sdesc *hipe_decode_sdesc(Eterm arg)
struct sdesc *sdesc;
if (is_not_tuple(arg) ||
- (tuple_val(arg))[0] != make_arityval(5) ||
+ (tuple_val(arg))[0] != make_arityval(6) ||
term_to_Uint((tuple_val(arg))[1], &ra) == 0 ||
term_to_Uint((tuple_val(arg))[2], &exnra) == 0 ||
is_not_small((tuple_val(arg))[3]) ||
@@ -183,5 +183,13 @@ struct sdesc *hipe_decode_sdesc(Eterm arg)
off = unsigned_val(live[i]);
sdesc->livebits[off / 32] |= (1 << (off & 31));
}
+#ifdef DEBUG
+ {
+ Eterm mfa_tpl = tuple_val(arg)[6];
+ sdesc->dbg_M = tuple_val(mfa_tpl)[1];
+ sdesc->dbg_F = tuple_val(mfa_tpl)[2];
+ sdesc->dbg_A = tuple_val(mfa_tpl)[3];
+ }
+#endif
return sdesc;
}
diff --git a/erts/emulator/hipe/hipe_stack.h b/erts/emulator/hipe/hipe_stack.h
index c4f2aacd8c..66f9f04c73 100644
--- a/erts/emulator/hipe/hipe_stack.h
+++ b/erts/emulator/hipe/hipe_stack.h
@@ -35,6 +35,10 @@ struct sdesc {
struct sdesc *next; /* hash collision chain */
} bucket;
unsigned int summary; /* frame size, exn handler presence flag, arity */
+#ifdef DEBUG
+ Eterm dbg_M, dbg_F;
+ unsigned dbg_A;
+#endif
unsigned int livebits[1]; /* size depends on arch & data in summary field */
};
diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c
index 24d232c968..327c74e9aa 100644
--- a/erts/emulator/hipe/hipe_x86.c
+++ b/erts/emulator/hipe/hipe_x86.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -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_gc.h b/erts/emulator/hipe/hipe_x86_gc.h
index e4607ad27d..ccb0d2ffb8 100644
--- a/erts/emulator/hipe/hipe_x86_gc.h
+++ b/erts/emulator/hipe/hipe_x86_gc.h
@@ -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
@@ -69,6 +69,11 @@ nstack_walk_init_sdesc(const Process *p, struct nstack_walk_state *state)
nstkarity = 0;
state->sdesc0[0].summary = (0 << 9) | (0 << 8) | nstkarity;
state->sdesc0[0].livebits[0] = 0;
+# ifdef DEBUG
+ state->sdesc0[0].dbg_M = 0;
+ 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 */
__asm__ __volatile__("" : : "m"(*state) : "memory");
return &state->sdesc0[0];
diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h
index b0db93267c..63ad250d60 100644
--- a/erts/emulator/hipe/hipe_x86_glue.h
+++ b/erts/emulator/hipe/hipe_x86_glue.h
@@ -62,6 +62,9 @@ static __inline__ void hipe_arch_glue_init(void)
.sdesc = {
.bucket = { .hvalue = (unsigned long)nbif_return },
.summary = (1<<8),
+ #ifdef DEBUG
+ .dbg_F = am_return,
+ #endif
},
};
hipe_init_sdesc_table(&nbif_return_sdesc.sdesc);
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 c1336c60d9..853dd90c34 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -39,7 +39,6 @@
#include "dtrace-wrapper.h"
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
-# define ERTS_DRV_EV_STATE_EXTRA_SIZE 128
#else
# include "safe_hash.h"
# define DRV_EV_STATE_HTAB_SIZE 1024
@@ -201,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)
@@ -334,7 +322,9 @@ static void
grow_drv_ev_state(int min_ix)
{
int i;
- int new_len = min_ix + 1 + ERTS_DRV_EV_STATE_EXTRA_SIZE;
+ int new_len;
+
+ new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1);
if (new_len > max_fds)
new_len = max_fds;
@@ -377,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));
}
}
@@ -491,7 +481,8 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
int on)
{
void (*stop_select_fn)(ErlDrvEvent, void*) = NULL;
- Eterm id = drvport2id(ix);
+ Port *prt = erts_drvport2port(ix);
+ Eterm id = erts_drvport2id(ix);
ErtsSysFdType fd = (ErtsSysFdType) e;
ErtsPollEvents ctl_events = (ErtsPollEvents) 0;
ErtsPollEvents new_events, old_events;
@@ -501,9 +492,11 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(name, 64);
#endif
-
- ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)
- && erts_lc_is_port_locked(erts_drvport2port(ix)));
+
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return -1;
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if ((unsigned)fd >= (unsigned)erts_smp_atomic_read_nob(&drv_ev_state_len)) {
@@ -529,9 +522,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 = prt->drv_ptr->stop_select;
#ifdef USE_VM_PROBES
- strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1);
+ strncpy(name, prt->drv_ptr->name, sizeof(name)-1);
name[sizeof(name)-1] = '\0';
#endif
ret = 0;
@@ -664,14 +657,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 = prt->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, prt->drv_ptr->name, sizeof(name)-1);
name[sizeof(name)-1] = '\0';
#endif
}
@@ -718,13 +711,16 @@ 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;
+ Port *prt = erts_drvport2port(ix);
- ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)
- && erts_lc_is_port_locked(erts_drvport2port(ix)));
+ if (prt == ERTS_INVALID_ERL_DRV_PORT)
+ return -1;
+
+ ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if ((unsigned)fd >= (unsigned)erts_smp_atomic_read_nob(&drv_ev_state_len)) {
@@ -970,8 +966,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 != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL);
+ erts_dsprintf(dsbufp, "driver %T ", pp != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL);
}
static void
@@ -1030,8 +1026,9 @@ 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;
- if (drv_ptr != state->driver.drv_ptr) {
+ Port *prt = erts_drvport2port(ix);
+ erts_driver_t* drv_ptr = prt != ERTS_INVALID_ERL_DRV_PORT ? prt->drv_ptr : NULL;
+ if (drv_ptr && drv_ptr != state->driver.drv_ptr) {
/* Some other driver wants the stop_select callback */
if (state->driver.drv_ptr->handle) {
erts_ddll_dereference_driver(state->driver.drv_ptr->handle);
@@ -1061,8 +1058,9 @@ 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);
+ if (pp != ERTS_INVALID_ERL_DRV_PORT)
+ print_driver_name(dsbufp, pp->common.id);
+ erts_dsprintf(dsbufp, "driver %T ", pp != ERTS_INVALID_ERL_DRV_PORT ? pp->common.id : NIL);
}
static void
@@ -1099,8 +1097,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);
}
}
@@ -1111,8 +1108,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..bd8ba82a5f 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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
@@ -39,6 +39,7 @@
#include "erl_alloc.h"
#include "big.h"
#include "erl_thr_progress.h"
+#include "erl_util_queue.h"
#if HAVE_ERTS_MSEG
@@ -57,26 +58,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 +139,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 +151,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 +170,26 @@ typedef struct {
CallCounter check_cache;
} ErtsMsegCalls;
+typedef struct cache_t_ cache_t;
+
+struct cache_t_ {
+ Uint size;
+ void *seg;
+ cache_t *next;
+ cache_t *prev;
+};
+
+
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_node;
+ cache_t cache_powered_node[CACHE_AREAS];
+ cache_t cache_free;
+
+ Sint cache_size;
Uint cache_hits;
struct {
@@ -320,8 +334,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 +344,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 +391,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 +414,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 +439,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 +479,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 +518,307 @@ 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;
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (cd)
- mk->free_cache_descs = cd->next;
- return cd;
-}
+/* Cache interface */
-static ERTS_INLINE void
-free_cd(MemKind* mk, cache_desc_t *cd)
-{
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- cd->next = mk->free_cache_descs;
- mk->free_cache_descs = cd;
+
+static ERTS_INLINE void mseg_cache_clear_node(cache_t *c) {
+ c->seg = NULL;
+ c->size = 0;
+ c->next = c;
+ c->prev = c;
}
+static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Uint flags) {
-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;
+
+ ASSERT(!MSEG_FLG_IS_2POW(flags) || (MSEG_FLG_IS_2POW(flags) && MAP_IS_ALIGNED(seg) && IS_2POW(size)));
+
+ /* The idea is that sbc caching is prefered over mbc caching.
+ * Blocks are normally allocated in mb carriers and thus cached there.
+ * Large blocks has no such cache and it is up to mseg to cache them to speed things up.
+ */
+
+ if (!erts_circleq_is_empty(&(mk->cache_free))) {
+
+ /* We have free slots, use one to cache the segment */
+
+ c = erts_circleq_head(&(mk->cache_free));
+ erts_circleq_remove(c);
+
+ c->seg = seg;
+ c->size = size;
+
+ if (MSEG_FLG_IS_2POW(flags)) {
+ int ix = SIZE_TO_CACHE_AREA_IDX(size);
+
+ ASSERT(ix < CACHE_AREAS);
+ ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size);
+
+ erts_circleq_push_head(&(mk->cache_powered_node[ix]), c);
+
+ } else
+ erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+
+ mk->cache_size++;
+ ASSERT(mk->cache_size <= mk->ma->max_cache_size);
+
+ return 1;
+ } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) {
+
+ /* No free slots.
+ * Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */
+
+ c = erts_circleq_tail(&(mk->cache_unpowered_node));
+ erts_circleq_remove(c);
+
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_clear_node(c);
+
+ c->seg = seg;
+ c->size = size;
+
+ erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+
+ return 1;
+ } else if (!MSEG_FLG_IS_2POW(flags)) {
+
+ /* No free slots and no unpowered (sbc) slots.
+ * Evict smallest slot from powered cache so we can cache an unpowered (sbc) segment.
+ * Note: Though this is the wanted policy I don't think it is used significantly.
+ * This branch could probably be removed without serious impact.
+ */
+
+ int i;
+
+ for( i = 0; i < CACHE_AREAS; i++) {
+ if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ continue;
+
+ c = erts_circleq_tail(&(mk->cache_powered_node[i]));
+ erts_circleq_remove(c);
+
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_clear_node(c);
+
+ c->seg = seg;
+ c->size = size;
+
+ erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+
+ 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 flags) {
+
+ 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;
- }
- mk->cache_size++;
+ if (MSEG_FLG_IS_2POW(flags)) {
+
+ int i, ix = SIZE_TO_CACHE_AREA_IDX(size);
+ void *seg;
+ cache_t *c;
+ Uint csize;
+
+ ASSERT(IS_2POW(size));
+
+ for( i = ix; i < CACHE_AREAS; i++) {
+
+ if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ continue;
+
+ c = erts_circleq_head(&(mk->cache_powered_node[i]));
+ erts_circleq_remove(c);
+
+ ASSERT(IS_2POW(c->size));
+ ASSERT(MAP_IS_ALIGNED(c->seg));
+
+ csize = c->size;
+ seg = c->seg;
+
+ mk->cache_size--;
+ mk->cache_hits++;
+
+ /* link to free cache list */
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), c);
+
+ ASSERT(!(mk->cache_size < 0));
+
+ if (csize != size) {
+ mseg_destroy(mk->ma, mk, (char *)seg + size, csize - size);
+ }
+
+ return seg;
+ }
+ }
+ else if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) {
+ void *seg;
+ cache_t *c;
+ cache_t *best = NULL;
+ Uint bdiff = 0;
+ Uint csize;
+ Uint bad_max_abs = mk->ma->abs_max_cache_bad_fit;
+ Uint bad_max_rel = mk->ma->rel_max_cache_bad_fit;
+
+ erts_circleq_foreach(c, &(mk->cache_unpowered_node)) {
+ csize = c->size;
+ if (csize >= size) {
+ if (((csize - size)*100 < bad_max_rel*size) && (csize - size) < bad_max_abs ) {
+
+ seg = c->seg;
+
+ erts_circleq_remove(c);
+
+ mk->cache_size--;
+ mk->cache_hits++;
+
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), c);
+
+ *size_p = csize;
+
+ return seg;
+
+ } else if (!best || (csize - size) < bdiff) {
+ best = c;
+ bdiff = csize - size;
+ }
+ }
+ }
+
+ /* No cached segment met our criteria, use the best one found and trim it */
+
+ if (best) {
+
+ seg = best->seg;
+ csize = best->size;
+
+ ASSERT(best->seg);
+ ASSERT(best->size > 0);
+
+ mk->cache_hits++;
+
+ /* Use current cache placement for remaining segment space */
+
+ best->seg = seg + size;
+ best->size = csize - size;
+
+ ASSERT((size % GET_PAGE_SIZE) == 0);
+ ASSERT((best->size % GET_PAGE_SIZE) == 0);
+
+ *size_p = size;
+
+ return seg;
+
+ }
+ }
+ 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 = erts_circleq_tail(head);
+ erts_circleq_remove(c);
+
+ if (erts_mtrace_enabled)
+ erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
+
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), 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;
+
+ while (!erts_circleq_is_empty(head)) {
+
+ c = erts_circleq_tail(head);
+ erts_circleq_remove(c);
+
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_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), 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--;
+
+ }
+
+ 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 (!erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_powered_node[i]));
+ }
+
+ if (!erts_circleq_is_empty(&(mk->cache_unpowered_node)))
+ return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered_node));
+
+ 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 +826,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 (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ continue;
- ASSERT(!mk->cache);
- ASSERT(!mk->cache_end);
- ASSERT(!mk->cache_size);
+ mseg_drop_memkind_cache_size(mk, &(mk->cache_powered_node[i]));
+ ASSERT(erts_circleq_is_empty(&(mk->cache_powered_node[i])));
+ }
+ /* drop varied caches */
+ if (!erts_circleq_is_empty(&(mk->cache_unpowered_node)))
+ mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered_node));
+
+ ASSERT(erts_circleq_is_empty(&(mk->cache_unpowered_node)));
+ ASSERT(mk->cache_size == 0);
+}
- mk->segments.current.watermark = mk->segments.current.no;
+static void mseg_clear_cache(ErtsMsegAllctr_t *ma) {
+ MemKind* mk;
+
+ ERTS_MSEG_LOCK(ma);
+ ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- INC_CC(mk->ma, clear_cache);
+
+ for (mk = ma->mk_list; mk; mk = mk->next) {
+ mseg_clear_memkind_cache(mk);
+ }
+
+ INC_CC(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 +897,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, flags)) != 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;
}
@@ -777,85 +938,59 @@ mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, Uint *size_p,
static void
mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, Uint size,
- const ErtsMsegOpt_t *opt)
+ Uint flags, 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, flags)) {
+ 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);
- erts_mtrace_crr_alloc(seg, SEGTYPE, SEGTYPE, size);
- }
-
- /* ASSERT(segments.current.watermark >= segments.current.no + cache_size); */
- if (check_limits)
- check_cache_limits(mk);
+ if (erts_mtrace_enabled)
+ erts_mtrace_crr_free(atype, SEGTYPE, seg);
- schedule_cache_check(ma);
+ mseg_destroy(ma, mk, seg, size);
- }
+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);
+ mseg_dealloc(ma, atype, seg, old_size, flags, opt);
DEC_CC(ma, dealloc);
return NULL;
}
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 +1001,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 +1030,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);
+ mseg_dealloc(ma, atype, seg, old_size, flags, 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,19 +1046,24 @@ 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));
- mseg_dealloc(ma, atype, seg, old_size, opt);
+ sys_memcpy(((char *) new_seg), ((char *) seg), MIN(new_size, old_size));
+ mseg_dealloc(ma, atype, seg, old_size, flags, opt);
}
#endif
}
@@ -958,6 +1071,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 +1104,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 +1161,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 +1181,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 +1230,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 +1291,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 +1331,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,81 +1521,61 @@ 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
erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg,
- Uint size, const ErtsMsegOpt_t *opt)
+ Uint size, Uint flags, const ErtsMsegOpt_t *opt)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt);
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- mseg_dealloc(ma, atype, seg, size, opt);
+ mseg_dealloc(ma, atype, seg, size, flags, opt);
ERTS_MSEG_UNLOCK(ma);
}
void
-erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size)
+erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size, Uint flags)
{
- erts_mseg_dealloc_opt(atype, seg, size, &erts_mseg_default_opt);
+ erts_mseg_dealloc_opt(atype, seg, size, flags, &erts_mseg_default_opt);
}
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 +1596,33 @@ 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;
+ /* Clear all cache headers */
+ mseg_cache_clear_node(&(mk->cache_free));
+ mseg_cache_clear_node(&(mk->cache_unpowered_node));
- 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];
+ for (i = 0; i < CACHE_AREAS; i++) {
+ mseg_cache_clear_node(&(mk->cache_powered_node[i]));
}
- else
- mk->free_cache_descs = NULL;
+
+ /* Populate cache free list */
+
+ ASSERT(ma->max_cache_size <= MAX_CACHE_SIZE);
+
+ for (i = 0; i < ma->max_cache_size; i++) {
+ mseg_cache_clear_node(&(mk->cache[i]));
+ erts_circleq_push_head(&(mk->cache_free), &(mk->cache[i]));
+ }
+
+ mk->cache_size = 0;
+ mk->cache_hits = 0;
mk->segments.current.watermark = 0;
mk->segments.current.no = 0;
@@ -1533,9 +1638,6 @@ static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name)
ma->mk_list = mk;
}
-
-
-
void
erts_mseg_init(ErtsMsegInit_t *init)
{
@@ -1570,15 +1672,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,15 +1760,16 @@ 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);
+ erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2, (Uint) 0);
return (unsigned long) 0;
case 0x403:
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 +1805,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 +1857,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 +1884,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 +1895,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 +1948,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 +1956,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 +1976,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 +2007,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 +2034,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 +2063,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 +2073,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 +2094,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 +2115,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 +2128,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 +2164,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 +2178,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 +2192,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..3cab9e18da 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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,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 {
@@ -51,7 +74,7 @@ typedef struct {
{ \
4*1024*1024, /* amcbf: Absolute max cache bad fit */ \
20, /* rmcbf: Relative max cache bad fit */ \
- 5, /* mcs: Max cache size */ \
+ 10, /* mcs: Max cache size */ \
1000 /* cci: Cache check interval */ \
}
@@ -68,13 +91,12 @@ 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_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_opt(ErtsAlcType_t, void *, Uint, 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, Uint);
+void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, Uint, const ErtsMsegOpt_t *);
+void *erts_mseg_realloc(ErtsAlcType_t, void *, Uint, Uint *, Uint);
+void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *, 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 50a888323a..a523d67158 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -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"
@@ -105,8 +100,8 @@
#define ERTS_POLL_COALESCE_KP_RES (ERTS_POLL_USE_KQUEUE || ERTS_POLL_USE_EPOLL)
-#define FDS_STATUS_EXTRA_FREE_SIZE 128
-#define POLL_FDS_EXTRA_FREE_SIZE 128
+#define ERTS_EV_TABLE_MIN_LENGTH 1024
+#define ERTS_EV_TABLE_EXP_THRESHOLD (2048*1024)
#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
# define ERTS_POLL_ASYNC_INTERRUPT_SUPPORT 1
@@ -563,6 +558,28 @@ free_update_requests_block(ErtsPollSet ps,
* --- Growing poll set structures -------------------------------------------
*/
+int
+ERTS_POLL_EXPORT(erts_poll_get_table_len) (int new_len)
+{
+ if (new_len < ERTS_EV_TABLE_MIN_LENGTH) {
+ new_len = ERTS_EV_TABLE_MIN_LENGTH;
+ } else if (new_len < ERTS_EV_TABLE_EXP_THRESHOLD) {
+ /* find next power of 2 */
+ --new_len;
+ new_len |= new_len >> 1;
+ new_len |= new_len >> 2;
+ new_len |= new_len >> 4;
+ new_len |= new_len >> 8;
+ new_len |= new_len >> 16;
+ ++new_len;
+ } else {
+ /* grow incrementally */
+ new_len += ERTS_EV_TABLE_EXP_THRESHOLD;
+ }
+ return new_len;
+}
+
+
#if ERTS_POLL_USE_KERNEL_POLL
static void
grow_res_events(ErtsPollSet ps, int new_len)
@@ -575,7 +592,7 @@ grow_res_events(ErtsPollSet ps, int new_len)
#elif ERTS_POLL_USE_KQUEUE
struct kevent
#endif
- )*new_len;
+ ) * ERTS_POLL_EXPORT(erts_poll_get_table_len)(new_len);
/* We do not need to save previously stored data */
if (ps->res_events)
erts_free(ERTS_ALC_T_POLL_RES_EVS, ps->res_events);
@@ -589,7 +606,7 @@ static void
grow_poll_fds(ErtsPollSet ps, int min_ix)
{
int i;
- int new_len = min_ix + 1 + POLL_FDS_EXTRA_FREE_SIZE;
+ int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_ix + 1);
if (new_len > max_fds)
new_len = max_fds;
ps->poll_fds = (ps->poll_fds_len
@@ -611,7 +628,7 @@ static void
grow_fds_status(ErtsPollSet ps, int min_fd)
{
int i;
- int new_len = min_fd + 1 + FDS_STATUS_EXTRA_FREE_SIZE;
+ int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(min_fd + 1);
ASSERT(min_fd < max_fds);
if (new_len > max_fds)
new_len = max_fds;
@@ -2200,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)
{
@@ -2212,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_poll.h b/erts/emulator/sys/common/erl_poll.h
index 8dde619105..09ed9f41af 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -246,4 +246,6 @@ void ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet,
ErtsPollEvents [],
int);
+int ERTS_POLL_EXPORT(erts_poll_get_table_len)(int);
+
#endif /* #ifndef ERL_POLL_H__ */
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 461e763f03..31ad3b82d5 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -47,14 +47,21 @@
/* Written once and only once */
static int filename_encoding = ERL_FILENAME_UNKNOWN;
+static int filename_warning = ERL_FILENAME_WARNING_WARNING;
#if defined(__WIN32__) || defined(__DARWIN__)
-static int user_filename_encoding = ERL_FILENAME_UTF8; /* Default unicode on windows */
+/* Default unicode on windows and MacOS X */
+static int user_filename_encoding = ERL_FILENAME_UTF8;
#else
static int user_filename_encoding = ERL_FILENAME_LATIN1;
#endif
-void erts_set_user_requested_filename_encoding(int encoding)
+/* This controls the heuristic in printing characters in shell and w/
+ io:format("~tp", ...) etc. */
+static int printable_character_set = ERL_PRINTABLE_CHARACTERS_LATIN1;
+
+void erts_set_user_requested_filename_encoding(int encoding, int warning)
{
user_filename_encoding = encoding;
+ filename_warning = warning;
}
int erts_get_user_requested_filename_encoding(void)
@@ -62,6 +69,20 @@ int erts_get_user_requested_filename_encoding(void)
return user_filename_encoding;
}
+int erts_get_filename_warning_type(void)
+{
+ return filename_warning;
+}
+
+void erts_set_printable_characters(int range) {
+ /* Not an atomic */
+ printable_character_set = range;
+}
+
+int erts_get_printable_characters(void) {
+ return printable_character_set;
+}
+
void erts_init_sys_common_misc(void)
{
#if defined(__WIN32__)
@@ -105,3 +126,181 @@ int erts_get_native_filename_encoding(void)
{
return filename_encoding;
}
+
+/* For internal use by sys_double_to_chars_fast() */
+static char* find_first_trailing_zero(char* p)
+{
+ for (; *(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);
+}
+
+/* Convert float to string using fixed point notation.
+ * decimals must be >= 0
+ * if compact != 0, the trailing 0's will be truncated
+ */
+int
+sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
+ int compact)
+{
+ /* Note that some C compilers don't support "static const" propagation
+ * so we use a defines */
+ #define SYS_DOUBLE_RND_CONST 0.55555555555555555
+ #define FRAC_SIZE 52
+ #define EXP_SIZE 11
+ #define EXP_MASK ((1ll << EXP_SIZE) - 1)
+ #define MAX_DECIMALS (sizeof(cs_sys_double_pow10) \
+ / sizeof(cs_sys_double_pow10[0]))
+ #define FRAC_MASK ((1ll << FRAC_SIZE) - 1)
+ #define FRAC_MASK2 ((1ll << (FRAC_SIZE + 1)) - 1)
+ #define MAX_FLOAT (1ll << (FRAC_SIZE+1))
+
+ static const double cs_sys_double_pow10[] = {
+ SYS_DOUBLE_RND_CONST / 1ll,
+ SYS_DOUBLE_RND_CONST / 10ll,
+ SYS_DOUBLE_RND_CONST / 100ll,
+ SYS_DOUBLE_RND_CONST / 1000ll,
+ SYS_DOUBLE_RND_CONST / 10000ll,
+ SYS_DOUBLE_RND_CONST / 100000ll,
+ SYS_DOUBLE_RND_CONST / 1000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000000000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000000000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000000000000ll
+ };
+
+ long long mantissa, int_part = 0, frac_part = 0;
+ short exp;
+ int max;
+ int neg;
+ double fr;
+ union { long long L; double F; } x;
+ char *p = buffer;
+
+ if (decimals < 0)
+ return -1;
+
+ /* Round the number to given decimal places. The number of 5's in the
+ * SYS_DOUBLE_RND_CONST constant is chosen such that adding any more 5's doesn't
+ * change the double precision of the number, i.e.:
+ * 1> term_to_binary(0.55555555555555555, [{minor_version, 1}]).
+ * <<131,70,63,225,199,28,113,199,28,114>>
+ * 2> term_to_binary(0.5555555555555555555, [{minor_version, 1}]).
+ * <<131,70,63,225,199,28,113,199,28,114>>
+ */
+ if (f >= 0) {
+ neg = 0;
+ fr = decimals < MAX_DECIMALS ? (f + cs_sys_double_pow10[decimals]) : f;
+ x.F = fr;
+ } else {
+ neg = 1;
+ fr = decimals < MAX_DECIMALS ? (f - cs_sys_double_pow10[decimals]) : f;
+ x.F = -fr;
+ }
+
+ exp = (x.L >> FRAC_SIZE) & EXP_MASK;
+ mantissa = x.L & FRAC_MASK;
+
+ if (exp == EXP_MASK) {
+ if (mantissa == 0) {
+ if (neg)
+ *p++ = '-';
+ *p++ = 'i';
+ *p++ = 'n';
+ *p++ = 'f';
+ } else {
+ *p++ = 'n';
+ *p++ = 'a';
+ *p++ = 'n';
+ }
+ *p = '\0';
+ return p - buffer;
+ }
+
+ exp -= EXP_MASK >> 1;
+ mantissa |= (1ll << FRAC_SIZE);
+
+ /* Don't bother with optimizing too large numbers or too large precision */
+ if (x.F > MAX_FLOAT || decimals >= MAX_DECIMALS) {
+ int len = erts_snprintf(buffer, buffer_size, "%.*f", decimals, f);
+ char* p = buffer + len;
+ if (len >= buffer_size)
+ return -1;
+ /* Delete trailing zeroes */
+ if (compact)
+ p = find_first_trailing_zero(p);
+ *p = '\0';
+ return p - buffer;
+ } else 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) {
+ if (neg)
+ *p++ = '-';
+ *p++ = '0';
+ } else {
+ int ret, i, n;
+ while (int_part != 0) {
+ long long j = int_part / 10;
+ *p++ = (char)(int_part - ((j << 3) + (j << 1)) + '0');
+ int_part = j;
+ }
+ if (neg)
+ *p++ = '-';
+ /* Reverse string */
+ ret = p - buffer;
+ for (i = 0, n = ret/2; i < n; i++) {
+ int j = ret - i - 1;
+ char c = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = c;
+ }
+ }
+
+ if (decimals > 0) {
+ int i;
+ *p++ = '.';
+
+ max = buffer_size - (p - buffer) - 1 /* leave room for trailing '\0' */;
+
+ if (decimals > max)
+ return -1; /* the number is not large enough to fit in the buffer */
+
+ max = decimals;
+
+ for (i = 0; i < max; i++) {
+ /* frac_part *= 10; */
+ frac_part = (frac_part << 3) + (frac_part << 1);
+
+ *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
+ frac_part &= FRAC_MASK2;
+ }
+
+ /* Delete trailing zeroes */
+ if (compact)
+ p = find_first_trailing_zero(p);
+ }
+
+ *p = '\0';
+ return p - buffer;
+}
diff --git a/erts/emulator/sys/common/erl_util_queue.h b/erts/emulator/sys/common/erl_util_queue.h
new file mode 100644
index 0000000000..47925e2264
--- /dev/null
+++ b/erts/emulator/sys/common/erl_util_queue.h
@@ -0,0 +1,77 @@
+/*
+ * %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%
+ */
+
+#ifndef ERL_UTIL_QUEUE_H_
+#define ERL_UTIL_QUEUE_H_
+
+#define erts_circleq_head(Q) ((Q)->next)
+#define erts_circleq_tail(Q) ((Q)->prev)
+#define erts_circleq_next(Q) ((Q)->next)
+#define erts_circleq_prev(Q) ((Q)->prev)
+#define erts_circleq_is_empty(Q) ((Q)->next == (void *)(Q))
+
+#define erts_circleq_remove(N) \
+ do { \
+ (N)->next->prev = (N)->prev; \
+ (N)->prev->next = (N)->next; \
+ (N)->next = (N); \
+ (N)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_pop_head(Q, N) \
+ do { \
+ (N) = (Q)->next; \
+ (N)->next->prev = (N)->prev; \
+ (N)->prev->next = (N)->next; \
+ (N)->next = (N); \
+ (N)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_pop_tail(Q, N) \
+ do { \
+ (N) = (Q)->prev; \
+ (N)->next->prev = (N)->prev; \
+ (N)->prev->next = (N)->next; \
+ (N)->next = (N); \
+ (N)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_push_head(Q, N) \
+ do { \
+ (N)->next = (Q)->next; \
+ (N)->prev = (void *)(Q); \
+ (Q)->next->prev = (N); \
+ (Q)->next = (N); \
+ } while(0)
+
+#define erts_circleq_push_tail(Q, N) \
+ do { \
+ (N)->prev = (Q)->prev; \
+ (N)->next = (void *)(Q); \
+ (Q)->prev->next = (N); \
+ (Q)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_foreach(V, Q) \
+ for ((V) = (Q)->next; (V) != (const void *)(Q); (V) = (V)->next)
+
+#define erts_circleq_foreach_reverse(V, Q) \
+ for ((V) = (Q)->prev; (V) != (const void *)(Q); (V) = (V)->prev)
+
+#endif
diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
index 336d9586c4..12c47d0088 100644
--- a/erts/emulator/sys/unix/erl_unix_sys_ddll.c
+++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -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 964751cf86..a7ea4b2490 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -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
@@ -123,7 +123,8 @@ struct ErtsSysReportExit_ {
/* This data is shared by these drivers - initialized by spawn_init() */
static struct driver_data {
- int port_num, ofd, packet_bytes;
+ ErlDrvPort port_num;
+ int ofd, packet_bytes;
ErtsSysReportExit *report_exit;
int pid;
int alive;
@@ -577,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,
@@ -731,7 +732,8 @@ prepare_crash_dump(int secs)
list = CONS(hp, make_small(8), list); hp += 2;
/* send to heart port, CMD = 8, i.e. prepare crash dump =o */
- erts_write_to_port(ERTS_INVALID_PID, heart_port, list);
+ 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
@@ -1182,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,
@@ -1190,6 +1192,7 @@ static int set_driver_data(int port_num,
int exit_status,
int pid)
{
+ Port *prt;
ErtsSysReportExit *report_exit;
if (!exit_status)
@@ -1198,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;
@@ -1208,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);
+ if (prt != ERTS_INVALID_ERL_DRV_PORT)
+ prt->os_pid = pid;
if (read_write & DO_READ) {
driver_data[ifd].packet_bytes = packet_bytes;
@@ -1281,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;
@@ -1577,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) */
@@ -1590,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 : ".";
@@ -1657,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);
@@ -1969,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);
@@ -2017,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);
@@ -2030,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;
@@ -2046,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;
@@ -2096,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;
@@ -2147,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;
@@ -2197,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;
@@ -2320,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;
@@ -2400,10 +2408,10 @@ 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
@@ -2629,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(ERTS_Port2ErlDrvPort(pp),
rep->ifd,
(ERL_DRV_READ|ERL_DRV_USE),
1);
@@ -2649,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(ERTS_Port2ErlDrvPort(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);
}
diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c
index 8ec7b31ce0..f2f4de869d 100644
--- a/erts/emulator/sys/unix/sys_float.c
+++ b/erts/emulator/sys/unix/sys_float.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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 3bdff5d7b6..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);
-}
-
-int
-erts_sys_prepare_crash_dump(void)
-{
- return 0;
-}
-
-/* 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..4c8d83ab16 100644
--- a/erts/emulator/sys/win32/erl_win32_sys_ddll.c
+++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -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..a7c53c904d 100644
--- a/erts/emulator/sys/win32/erl_win_dyn_driver.h
+++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -37,6 +37,7 @@ WDD_TYPEDEF(int, driver_failure_posix,(ErlDrvPort, int));
WDD_TYPEDEF(int, driver_failure,(ErlDrvPort, int));
WDD_TYPEDEF(int, driver_exit, (ErlDrvPort, int));
WDD_TYPEDEF(int, driver_failure_eof, (ErlDrvPort));
+WDD_TYPEDEF(void, erl_drv_busy_msgq_limits, (ErlDrvPort, ErlDrvSizeT *, ErlDrvSizeT *));
WDD_TYPEDEF(int, driver_select, (ErlDrvPort, ErlDrvEvent, int, int));
WDD_TYPEDEF(int, driver_event, (ErlDrvPort, ErlDrvEvent,ErlDrvEventData));
WDD_TYPEDEF(int, driver_output, (ErlDrvPort, char *, ErlDrvSizeT));
@@ -47,6 +48,7 @@ WDD_TYPEDEF(ErlDrvSizeT, driver_vec_to_buf, (ErlIOVec *, char *, ErlDrvSizeT));
WDD_TYPEDEF(int, driver_set_timer, (ErlDrvPort, unsigned long));
WDD_TYPEDEF(int, driver_cancel_timer, (ErlDrvPort));
WDD_TYPEDEF(int, driver_read_timer, (ErlDrvPort, unsigned long *));
+WDD_TYPEDEF(int, erl_drv_consume_timeslice, (ErlDrvPort, int));
WDD_TYPEDEF(char *, erl_errno_id, (int));
WDD_TYPEDEF(void, set_busy_port, (ErlDrvPort, int));
WDD_TYPEDEF(void, set_port_control_flags, (ErlDrvPort, int));
@@ -74,7 +76,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));
@@ -150,6 +154,7 @@ typedef struct {
WDD_FTYPE(driver_failure) *driver_failure;
WDD_FTYPE(driver_exit) *driver_exit;
WDD_FTYPE(driver_failure_eof) *driver_failure_eof;
+ WDD_FTYPE(erl_drv_busy_msgq_limits) *erl_drv_busy_msgq_limits;
WDD_FTYPE(driver_select) *driver_select;
WDD_FTYPE(driver_event) *driver_event;
WDD_FTYPE(driver_output) *driver_output;
@@ -160,6 +165,7 @@ typedef struct {
WDD_FTYPE(driver_set_timer) *driver_set_timer;
WDD_FTYPE(driver_cancel_timer) *driver_cancel_timer;
WDD_FTYPE(driver_read_timer) *driver_read_timer;
+ WDD_FTYPE(erl_drv_consume_timeslice) *erl_drv_consume_timeslice;
WDD_FTYPE(erl_errno_id) *erl_errno_id;
WDD_FTYPE(set_busy_port)* set_busy_port;
WDD_FTYPE(set_port_control_flags) *set_port_control_flags;
@@ -187,7 +193,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;
@@ -257,6 +265,7 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_failure (WinDynDriverCallbacks.driver_failure)
#define driver_exit (WinDynDriverCallbacks.driver_exit)
#define driver_failure_eof (WinDynDriverCallbacks.driver_failure_eof)
+#define erl_drv_busy_msgq_limits (WinDynDriverCallbacks.erl_drv_busy_msgq_limits)
#define driver_select (WinDynDriverCallbacks.driver_select)
#define driver_event (WinDynDriverCallbacks.driver_event)
#define driver_output (WinDynDriverCallbacks.driver_output)
@@ -267,6 +276,7 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_set_timer (WinDynDriverCallbacks.driver_set_timer)
#define driver_cancel_timer (WinDynDriverCallbacks.driver_cancel_timer)
#define driver_read_timer (WinDynDriverCallbacks.driver_read_timer)
+#define erl_drv_consume_timeslice (WinDynDriverCallbacks.erl_drv_consume_timeslice)
#define erl_errno_id (WinDynDriverCallbacks.erl_errno_id)
#define set_busy_port (WinDynDriverCallbacks.set_busy_port)
#define set_port_control_flags (WinDynDriverCallbacks.set_port_control_flags)
@@ -294,7 +304,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)
@@ -388,6 +400,7 @@ do { \
((W).driver_failure) = driver_failure; \
((W).driver_exit) = driver_exit; \
((W).driver_failure_eof) = driver_failure_eof; \
+((W).erl_drv_busy_msgq_limits) = erl_drv_busy_msgq_limits;\
((W).driver_select) = driver_select; \
((W).driver_event) = driver_event; \
((W).driver_output) = driver_output; \
@@ -398,6 +411,7 @@ do { \
((W).driver_set_timer) = driver_set_timer; \
((W).driver_cancel_timer) = driver_cancel_timer; \
((W).driver_read_timer) = driver_read_timer; \
+((W).erl_drv_consume_timeslice) = erl_drv_consume_timeslice;\
((W).erl_errno_id) = erl_errno_id; \
((W).set_busy_port) = set_busy_port; \
((W).set_port_control_flags) = set_port_control_flags; \
@@ -425,7 +439,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 b6f11209ba..5ce1a61303 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -90,6 +90,11 @@
#define strncasecmp _strnicmp
+#ifndef __GNUC__
+# undef ERTS_I64_LITERAL
+# define ERTS_I64_LITERAL(X) X##i64
+#endif
+
/*
* Practial Windows specific macros.
*/
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 578536ed08..e0422de026 100755
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -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
@@ -57,11 +57,13 @@ extern void _dosmaperr(DWORD);
#define __argv e_argv
#endif
+typedef struct driver_data DriverData;
+
static void init_console();
static int get_and_remove_option(int* argc, char** argv, const char* option);
static char *get_and_remove_option2(int *argc, char **argv,
const char *option);
-static int init_async_io(struct async_io* aio, int use_threads);
+static int init_async_io(DriverData *dp, struct async_io* aio, int use_threads);
static void release_async_io(struct async_io* aio, ErlDrvPort);
static void async_read_file(struct async_io* aio, LPVOID buf, DWORD numToRead);
static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite);
@@ -87,9 +89,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,10 +96,9 @@ 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);
+static void close_active_handle(DriverData *, HANDLE handle);
static DWORD WINAPI threaded_handle_closer(LPVOID param);
#endif
static DWORD WINAPI threaded_reader(LPVOID param);
@@ -115,9 +113,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) \
@@ -269,7 +264,8 @@ int erts_sys_prepare_crash_dump(int secs)
list = CONS(hp, make_small(8), list); hp += 2;
/* send to heart port, CMD = 8, i.e. prepare crash dump =o */
- erts_write_to_port(NIL, heart_port, list);
+ erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
+ heart_port->common.id, list, NULL);
return 1;
}
@@ -446,6 +442,8 @@ typedef struct async_io {
DWORD bytesTransferred; /* Bytes read or write in the last operation.
* Valid only when DF_OVR_READY is set.
*/
+ DriverData *dp; /* Pointer to driver data struct which
+ this struct is part of */
} AsyncIo;
@@ -464,7 +462,7 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
* none of the file handles.
*/
-typedef struct driver_data {
+struct driver_data {
int totalNeeded; /* Total number of bytes needed to fill
* up the packet header or packet. */
int bytesInBuffer; /* Number of bytes read so far in
@@ -474,7 +472,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.
*/
@@ -482,9 +480,8 @@ typedef struct driver_data {
AsyncIo in; /* Control block for overlapped reading. */
AsyncIo out; /* Control block for overlapped writing. */
int report_exit; /* Do report exit status for the port */
-} DriverData;
-
-static DriverData* driver_data; /* Pointer to array of driver data. */
+ erts_atomic32_t refc; /* References to this struct */
+};
/* Driver interfaces */
static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
@@ -589,6 +586,26 @@ struct erl_drv_entry vanilla_driver_entry = {
stop_select
};
+static ERTS_INLINE void
+refer_driver_data(DriverData *dp)
+{
+#ifdef DEBUG
+ erts_aint32_t refc = erts_atomic32_inc_read_nob(&dp->refc);
+ ASSERT(refc > 1);
+#else
+ erts_atomic32_inc_nob(&dp->refc);
+#endif
+}
+
+static ERTS_INLINE void
+unrefer_driver_data(DriverData *dp)
+{
+ erts_aint32_t refc = erts_atomic32_dec_read_mb(&dp->refc);
+ ASSERT(refc >= 0);
+ if (refc == 0)
+ driver_free(dp);
+}
+
/*
* Initialises a DriverData structure.
*
@@ -597,67 +614,54 @@ 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.
- */
+ erts_atomic32_init_nob(&dp->refc, 1);
+ 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, &dp->in, use_threads) == -1)
+ goto async_io_error1;
+ if (init_async_io(dp, &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) {
@@ -684,7 +688,7 @@ release_driver_data(DriverData* dp)
dp->in.fd = INVALID_HANDLE_VALUE;
DEBUGF(("Waiting for the in event thingie"));
if (WaitForSingleObject(dp->in.ov.hEvent,timeout) == WAIT_TIMEOUT) {
- close_active_handle(dp->port_num, dp->in.ov.hEvent);
+ close_active_handle(dp, dp->in.ov.hEvent);
dp->in.ov.hEvent = NULL;
timeout = 0;
}
@@ -695,7 +699,7 @@ release_driver_data(DriverData* dp)
dp->out.fd = INVALID_HANDLE_VALUE;
DEBUGF(("Waiting for the out event thingie"));
if (WaitForSingleObject(dp->out.ov.hEvent,timeout) == WAIT_TIMEOUT) {
- close_active_handle(dp->port_num, dp->out.ov.hEvent);
+ close_active_handle(dp, dp->out.ov.hEvent);
dp->out.ov.hEvent = NULL;
}
DEBUGF(("...done\n"));
@@ -741,20 +745,20 @@ release_driver_data(DriverData* dp)
* the exit thread.
*/
- dp->port_num = PORT_FREE;
- erts_smp_mtx_unlock(&sys_driver_data_lock);
+ unrefer_driver_data(dp);
}
#ifdef ERTS_SMP
struct handles_to_be_closed {
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ DriverData *drv_data[MAXIMUM_WAIT_OBJECTS];
unsigned cnt;
};
static struct handles_to_be_closed* htbc_curr = NULL;
CRITICAL_SECTION htbc_lock;
-static void close_active_handle(ErlDrvPort port_num, HANDLE handle)
+static void close_active_handle(DriverData *dp, HANDLE handle)
{
struct handles_to_be_closed* htbc;
int i;
@@ -767,12 +771,18 @@ static void close_active_handle(ErlDrvPort port_num, HANDLE handle)
htbc = (struct handles_to_be_closed*) erts_alloc(ERTS_ALC_T_DRV_TAB,
sizeof(*htbc));
htbc->handles[0] = CreateAutoEvent(FALSE);
+ htbc->drv_data[0] = NULL;
htbc->cnt = 1;
thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_handle_closer, htbc, 0, &tid);
CloseHandle(thread);
}
- htbc->handles[htbc->cnt++] = handle;
- driver_select(port_num, (ErlDrvEvent)handle, ERL_DRV_USE_NO_CALLBACK, 0);
+ i = htbc->cnt++;
+ htbc->handles[i] = handle;
+ htbc->drv_data[i] = dp;
+ if (dp)
+ refer_driver_data(dp); /* Need to keep driver data until we have
+ closed the event; outstanding operation
+ might write into it.. */
SetEvent(htbc->handles[0]);
htbc_curr = htbc;
LeaveCriticalSection(&htbc_lock);
@@ -804,8 +814,13 @@ threaded_handle_closer(LPVOID param)
default:
ix = res - WAIT_OBJECT_0;
if (ix > 0 && ix < htbc->cnt) {
+ int move_ix;
CloseHandle(htbc->handles[ix]);
- htbc->handles[ix] = htbc->handles[--htbc->cnt];
+ if (htbc->drv_data[ix])
+ unrefer_driver_data(htbc->drv_data[ix]);
+ move_ix = --htbc->cnt;
+ htbc->handles[ix] = htbc->handles[move_ix];
+ htbc->drv_data[ix] = htbc->drv_data[move_ix];
}
}
if (htbc != htbc_curr) {
@@ -821,6 +836,7 @@ threaded_handle_closer(LPVOID param)
}
LeaveCriticalSection(&htbc_lock);
CloseHandle(htbc->handles[0]);
+ ASSERT(!htbc->drv_data[0]);
erts_free(ERTS_ALC_T_DRV_TAB, htbc);
DEBUGF(("threaded_handle_closer %p terminating\r\n", htbc));
return 0;
@@ -837,7 +853,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;
@@ -856,13 +871,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;
@@ -881,7 +895,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;
}
/*
@@ -889,8 +903,9 @@ reuse_driver_data(DriverData *dp, HANDLE ifd, HANDLE ofd, int read_write, ErlDrv
*/
static int
-init_async_io(AsyncIo* aio, int use_threads)
+init_async_io(DriverData *dp, AsyncIo* aio, int use_threads)
{
+ aio->dp = dp;
aio->flags = 0;
aio->thread = (HANDLE) -1;
aio->fd = INVALID_HANDLE_VALUE;
@@ -909,6 +924,8 @@ init_async_io(AsyncIo* aio, int use_threads)
if (aio->ov.hEvent == NULL)
return -1;
if (use_threads) {
+ OV_BUFFER_PTR(aio) = NULL;
+ OV_NUM_TO_READ(aio) = 0;
aio->ioAllowed = CreateAutoEvent(FALSE);
if (aio->ioAllowed == NULL)
return -1;
@@ -939,12 +956,8 @@ release_async_io(AsyncIo* aio, ErlDrvPort port_num)
CloseHandle(aio->fd);
aio->fd = INVALID_HANDLE_VALUE;
- if (aio->ov.hEvent != NULL) {
- (void) driver_select(port_num,
- (ErlDrvEvent)aio->ov.hEvent,
- ERL_DRV_USE, 0);
- /* was CloseHandle(aio->ov.hEvent); */
- }
+ if (aio->ov.hEvent != NULL)
+ CloseHandle(aio->ov.hEvent);
aio->ov.hEvent = NULL;
@@ -1154,12 +1167,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;
}
@@ -1290,9 +1297,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(port_num);
+ /* We assume that this cannot generate a negative number */
+ ASSERT(prt != ERTS_INVALID_ERL_DRV_PORT);
+ prt->os_pid = (SWord) pid;
+ }
}
if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
@@ -1315,12 +1325,15 @@ create_file_thread(AsyncIo* aio, int mode)
{
DWORD tid; /* Id for thread. */
+ refer_driver_data(aio->dp);
aio->thread = (HANDLE)
_beginthreadex(NULL, 0,
(mode & DO_WRITE) ? threaded_writer : threaded_reader,
aio, 0, &tid);
-
- return aio->thread != (HANDLE) -1;
+ if (aio->thread != (HANDLE) -1)
+ return 1;
+ unrefer_driver_data(aio->dp);
+ return 0;
}
/*
@@ -2106,6 +2119,7 @@ threaded_reader(LPVOID param)
if (aio->flags & DF_EXIT_THREAD)
break;
}
+ unrefer_driver_data(aio->dp);
return 0;
}
@@ -2185,6 +2199,7 @@ threaded_writer(LPVOID param)
}
CloseHandle(aio->fd);
aio->fd = INVALID_HANDLE_VALUE;
+ unrefer_driver_data(aio->dp);
return 0;
}
@@ -2281,12 +2296,10 @@ fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
**/
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;
}
@@ -2306,10 +2319,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,
@@ -2328,6 +2340,7 @@ static void fd_stop(ErlDrvData d)
(void) driver_select(dp->port_num,
(ErlDrvEvent)dp->out.ov.hEvent,
ERL_DRV_WRITE, 0);
+ ASSERT(dp->out.flushEvent);
SetEvent(dp->out.flushEvent);
WaitForSingleObject(dp->out.flushReplyEvent, INFINITE);
}
@@ -2372,26 +2385,20 @@ 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,
(ErlDrvEvent)dp->in.ov.hEvent,
- ERL_DRV_READ, 0);
+ ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK, 0);
}
if (dp->out.ov.hEvent != NULL) {
(void) driver_select(dp->port_num,
(ErlDrvEvent)dp->out.ov.hEvent,
- ERL_DRV_WRITE, 0);
+ ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK, 0);
}
if (dp->out.thread == (HANDLE) -1 && dp->in.thread == (HANDLE) -1) {
@@ -2403,7 +2410,8 @@ static void common_stop(int index)
*/
HANDLE thread;
DWORD tid;
- dp->port_num = PORT_EXITING;
+
+ /* threaded_exiter implicitly takes over refc from us... */
thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_exiter, dp, 0, &tid);
CloseHandle(thread);
}
@@ -2528,22 +2536,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)
@@ -2554,7 +2557,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; */
}
@@ -2568,7 +2571,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; */
}
@@ -2598,7 +2601,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. */
@@ -2633,10 +2636,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) {
@@ -2804,7 +2806,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
@@ -2812,7 +2814,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
@@ -2853,10 +2855,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
@@ -2867,7 +2869,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
@@ -3196,7 +3198,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
@@ -3321,9 +3324,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_float.c b/erts/emulator/sys/win32/sys_float.c
index 6558ad2d99..fb1ffc3089 100644
--- a/erts/emulator/sys/win32/sys_float.c
+++ b/erts/emulator/sys/win32/sys_float.c
@@ -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
@@ -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_time.c b/erts/emulator/sys/win32/sys_time.c
index b5123dc45d..2f2dfc8197 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -26,11 +26,7 @@
#include "sys.h"
#include "assert.h"
-#ifdef __GNUC__
-#define LL_LITERAL(X) X##LL
-#else
-#define LL_LITERAL(X) X##i64
-#endif
+#define LL_LITERAL(X) ERTS_I64_LITERAL(X)
/******************* Routines for time measurement *********************/
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index a7c8bd2cd2..9594ab48b1 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -47,6 +47,7 @@ MODULES= \
busy_port_SUITE \
call_trace_SUITE \
code_SUITE \
+ code_parallel_load_SUITE \
crypto_SUITE \
ddll_SUITE \
decode_packet_SUITE \
@@ -137,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 b214f87e4a..34979cacf1 100644
--- a/erts/emulator/test/alloc_SUITE_data/bucket_mask.c
+++ b/erts/emulator/test/alloc_SUITE_data/bucket_mask.c
@@ -22,7 +22,7 @@
#include "allocator_test.h"
#include <stdio.h>
-#ifdef __WIN32__ && SIZEOF_VOID_P == 8
+#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))
@@ -48,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;
@@ -73,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)
@@ -98,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);
@@ -135,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..60339ea422 100644
--- a/erts/emulator/test/beam_SUITE.erl
+++ b/erts/emulator/test/beam_SUITE.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
@@ -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 99ed8f1748..02c6de8cb1 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -25,7 +25,9 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
display/1, display_huge/0,
- types/1,
+ erl_bif_types/1,guard_bifs_in_erl_bif_types/1,
+ shadow_comments/1,
+ specs/1,improper_bif_stubs/1,auto_imports/1,
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
binary_to_atom/1,binary_to_existing_atom/1,
atom_to_binary/1,min_max/1, erlang_halt/1]).
@@ -33,7 +35,9 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [types, t_list_to_existing_atom, os_env, otp_7526,
+ [erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments,
+ specs, improper_bif_stubs, auto_imports,
+ t_list_to_existing_atom, os_env, otp_7526,
display,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
min_max, erlang_halt].
@@ -86,33 +90,20 @@ deeep(N,Acc) ->
deeep(N) ->
deeep(N,[hello]).
+erl_bif_types(Config) when is_list(Config) ->
+ ensure_erl_bif_types_compiled(),
-types(Config) when is_list(Config) ->
- c:l(erl_bif_types),
- case erlang:function_exported(erl_bif_types, module_info, 0) of
- false ->
- %% Fail cleanly.
- ?line ?t:fail("erl_bif_types not compiled");
- true ->
- types_1()
- end.
-
-types_1() ->
- ?line List0 = erlang:system_info(snifs),
+ List0 = erlang:system_info(snifs),
%% Ignore missing type information for hipe BIFs.
- ?line List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+ List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
- case [MFA || MFA <- List, not known_types(MFA)] of
- [] ->
- types_2(List);
- BadTypes ->
- io:put_chars("No type information:\n"),
- io:format("~p\n", [lists:sort(BadTypes)]),
- ?line ?t:fail({length(BadTypes),bifs_without_types})
- end.
+ KnownTypes = [MFA || MFA <- List, known_types(MFA)],
+ io:format("There are ~p BIFs with type information in erl_bif_types.",
+ [length(KnownTypes)]),
+ erl_bif_types_2(KnownTypes).
-types_2(List) ->
+erl_bif_types_2(List) ->
BadArity = [MFA || {M,F,A}=MFA <- List,
begin
Types = erl_bif_types:arg_types(M, F, A),
@@ -120,14 +111,14 @@ types_2(List) ->
end],
case BadArity of
[] ->
- types_3(List);
+ erl_bif_types_3(List);
[_|_] ->
io:put_chars("Bifs with bad arity\n"),
io:format("~p\n", [BadArity]),
?line ?t:fail({length(BadArity),bad_arity})
end.
-types_3(List) ->
+erl_bif_types_3(List) ->
BadSmokeTest = [MFA || {M,F,A}=MFA <- List,
begin
try erl_bif_types:type(M, F, A) of
@@ -151,9 +142,220 @@ types_3(List) ->
?line ?t:fail({length(BadSmokeTest),bad_smoke_test})
end.
+guard_bifs_in_erl_bif_types(_Config) ->
+ ensure_erl_bif_types_compiled(),
+
+ List0 = erlang:system_info(snifs),
+ List = [{F,A} || {erlang,F,A} <- List0,
+ erl_internal:guard_bif(F, A)],
+ Not = [FA || {F,A}=FA <- List,
+ not erl_bif_types:is_known(erlang, F, A)],
+ case Not of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["Dialyzer requires that all guard BIFs "
+ "have type information in erl_bif_types.\n\n"
+ "The following guard BIFs have no type information "
+ "in erl_bif_types:\n\n",
+ [io_lib:format(" ~p/~p\n", [F,A]) || {F,A} <- Not]]),
+ ?t:fail()
+ end.
+
+shadow_comments(_Config) ->
+ ensure_erl_bif_types_compiled(),
+
+ List0 = erlang:system_info(snifs),
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+ List = [MFA || MFA <- List1, not is_operator(MFA)],
+ HasTypes = [MFA || {M,F,A}=MFA <- List,
+ erl_bif_types:is_known(M, F, A)],
+ Path = get_code_path(),
+ BifRel = sofs:relation(HasTypes, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Specs0 = [extract_specs(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Specs = lists:append(Specs0),
+ SpecFuns0 = [F || {F,_} <- Specs],
+ SpecFuns = sofs:relation(SpecFuns0, [{m,f,a}]),
+ HasTypesAndSpecs = sofs:intersection(BifRel, SpecFuns),
+ Commented0 = lists:append([extract_comments(Mod, Path) ||
+ Mod <- BifModules]),
+ Commented = sofs:relation(Commented0, [{m,f,a}]),
+ {NoComments0,_,NoBifSpecs0} =
+ sofs:symmetric_partition(HasTypesAndSpecs, Commented),
+ NoComments = sofs:to_external(NoComments0),
+ NoBifSpecs = sofs:to_external(NoBifSpecs0),
+
+ case NoComments of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["If a BIF stub has both a spec and has type information in "
+ "erl_bif_types, there *must*\n"
+ "be a comment in the source file to make that immediately "
+ "obvious.\n\nThe following comments are missing:\n\n",
+ [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n",
+ [M,F,A]) || {M,F,A} <- NoComments]]),
+ ?t:fail()
+ end,
+
+ case NoBifSpecs of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["The following functions have \"shadowed\" comments "
+ "claiming that there is type information in erl_bif_types,\n"
+ "but actually there is no such type information.\n\n"
+ "Therefore, the following comments should be removed:\n\n",
+ [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n",
+ [M,F,A]) || {M,F,A} <- NoBifSpecs]]),
+ ?t:fail()
+ end.
+
+extract_comments(Mod, Path) ->
+ Beam = which(Mod, Path),
+ SrcDir = filename:join(filename:dirname(filename:dirname(Beam)), "src"),
+ Src = filename:join(SrcDir, atom_to_list(Mod) ++ ".erl"),
+ {ok,Bin} = file:read_file(Src),
+ Lines0 = binary:split(Bin, <<"\n">>, [global]),
+ Lines1 = [T || <<"%% Shadowed by erl_bif_types: ",T/binary>> <- Lines0],
+ {ok,ReMFA} = re:compile("([^:]*):([^/]*)/(\\d*)"),
+ Lines = [L || L <- Lines1, re:run(L, ReMFA, [{capture,[]}]) =:= match],
+ [begin
+ {match,[M,F,A]} = re:run(L, ReMFA, [{capture,all_but_first,list}]),
+ {list_to_atom(M),list_to_atom(F),list_to_integer(A)}
+ end || L <- Lines].
+
+ensure_erl_bif_types_compiled() ->
+ c:l(erl_bif_types),
+ case erlang:function_exported(erl_bif_types, module_info, 0) of
+ false ->
+ %% Fail cleanly.
+ ?t:fail("erl_bif_types not compiled");
+ true ->
+ ok
+ end.
+
known_types({M,F,A}) ->
erl_bif_types:is_known(M, F, A).
+specs(_) ->
+ List0 = erlang:system_info(snifs),
+
+ %% Ignore missing type information for hipe BIFs.
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+
+ %% Ignore all operators.
+ List = [MFA || MFA <- List1, not is_operator(MFA)],
+
+ %% Extract specs from the abstract code for all BIFs.
+ Path = get_code_path(),
+ BifRel = sofs:relation(List, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Specs0 = [extract_specs(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Specs = lists:append(Specs0),
+ BifSet = sofs:set(List, [function]),
+ SpecRel0 = sofs:relation(Specs, [{function,spec}]),
+ SpecRel = sofs:restriction(SpecRel0, BifSet),
+
+ %% Find BIFs without specs.
+ NoSpecs0 = sofs:difference(BifSet, sofs:domain(SpecRel)),
+ NoSpecs = sofs:to_external(NoSpecs0),
+ case NoSpecs of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars("The following BIFs don't have specs:\n"),
+ [print_mfa(MFA) || MFA <- NoSpecs],
+ ?t:fail()
+ end.
+
+is_operator({erlang,F,A}) ->
+ erl_internal:arith_op(F, A) orelse
+ erl_internal:bool_op(F, A) orelse
+ erl_internal:comp_op(F, A) orelse
+ erl_internal:list_op(F, A) orelse
+ erl_internal:send_op(F, A);
+is_operator(_) -> false.
+
+extract_specs(M, Abstr) ->
+ [{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr].
+
+make_mfa(M, {F,A}) -> {M,F,A};
+make_mfa(M, {M,_,_}=MFA) -> MFA.
+
+improper_bif_stubs(_) ->
+ Bifs0 = erlang:system_info(snifs),
+ Bifs = [MFA || {M,_,_}=MFA <- Bifs0, M =/= hipe_bifs],
+ Path = get_code_path(),
+ BifRel = sofs:relation(Bifs, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Funcs0 = [extract_functions(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Funcs = lists:append(Funcs0),
+ BifSet = sofs:set(Bifs, [function]),
+ FuncRel0 = sofs:relation(Funcs, [{function,code}]),
+ FuncRel = sofs:restriction(FuncRel0, BifSet),
+ [check_stub(MFA, Body) || {MFA,Body} <- sofs:to_external(FuncRel)],
+ ok.
+
+auto_imports(_Config) ->
+ Path = get_code_path(),
+ {erlang,Abstr} = extract_abstract(erlang, Path),
+ SpecFuns = [Name || {attribute,_,spec,{Name,_}} <- Abstr],
+ auto_imports(SpecFuns, 0).
+
+auto_imports([{F,A}|T], Errors) ->
+ case erl_internal:bif(F, A) of
+ false ->
+ io:format("~p/~p: not auto-imported, but spec claims it "
+ "is auto-imported", [F,A]),
+ auto_imports(T, Errors+1);
+ true ->
+ auto_imports(T, Errors)
+ end;
+auto_imports([{erlang,F,A}|T], Errors) ->
+ case erl_internal:bif(F, A) of
+ false ->
+ auto_imports(T, Errors);
+ true ->
+ io:format("~p/~p: auto-imported, but "
+ "spec claims it is *not* auto-imported", [F,A]),
+ auto_imports(T, Errors+1)
+ end;
+auto_imports([], 0) ->
+ ok;
+auto_imports([], Errors) ->
+ ?t:fail({Errors,inconsistencies}).
+
+extract_functions(M, Abstr) ->
+ [{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr].
+
+check_stub({erlang,apply,3}, _) ->
+ ok;
+check_stub({_,F,A}, B) ->
+ try
+ [{clause,_,Args,[],Body}] = B,
+ A = length(Args),
+ [{call,_,{remote,_,{atom,_,erlang},{atom,_,nif_error}},[_]}] = Body
+ catch
+ _:_ ->
+ io:put_chars("Invalid body for the following BIF stub:\n"),
+ Func = {function,0,F,A,B},
+ io:put_chars(erl_pp:function(Func)),
+ io:nl(),
+ io:put_chars("The body should be: erlang:nif_error(undef)"),
+ ?t:fail()
+ end.
+
t_list_to_existing_atom(Config) when is_list(Config) ->
?line all = list_to_existing_atom("all"),
?line ?MODULE = list_to_existing_atom(?MODULE_STRING),
@@ -279,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)],
@@ -292,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)),
@@ -483,6 +685,35 @@ erlang_halt(Config) when is_list(Config) ->
id(I) -> I.
+%% Get code path, including the path for the erts application.
+get_code_path() ->
+ case code:lib_dir(erts) of
+ {error,bad_name} ->
+ Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
+ [Erts|code:get_path()];
+ _ ->
+ code:get_path()
+ end.
+
+which(Mod, Path) ->
+ which_1(atom_to_list(Mod) ++ ".beam", Path).
+
+which_1(Base, [D|Ds]) ->
+ Path = filename:join(D, Base),
+ case filelib:is_regular(Path) of
+ true -> Path;
+ false -> which_1(Base, Ds)
+ end.
+print_mfa({M,F,A}) ->
+ io:format("~p:~p/~p", [M,F,A]).
+
+extract_abstract(Mod, Path) ->
+ Beam = which(Mod, Path),
+ {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} =
+ beam_lib:chunks(Beam, [abstract_code]),
+ {Mod,Abstr}.
+
+
hostname() ->
hostname(atom_to_list(node())).
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 58e0cb4096..babdb3363f 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -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_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index 9c88803fea..123952d01d 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -547,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),
diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl
index 3a29fd4d68..2c63296b83 100644
--- a/erts/emulator/test/busy_port_SUITE.erl
+++ b/erts/emulator/test/busy_port_SUITE.erl
@@ -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
@@ -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..f7937bc8d3 100644
--- a/erts/emulator/test/busy_port_SUITE_data/Makefile.src
+++ b/erts/emulator/test/busy_port_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. 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
@@ -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..7807b47e3d 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
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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
@@ -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..2008b33a72
--- /dev/null
+++ b/erts/emulator/test/busy_port_SUITE_data/scheduling_drv.c
@@ -0,0 +1,190 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2009-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%
+ */
+
+#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/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 9d80b01748..eaecd32f95 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -25,6 +25,7 @@
init_per_testcase/2,end_per_testcase/2,
hipe/1,process_specs/1,basic/1,flags/1,errors/1,pam/1,change_pam/1,
return_trace/1,exception_trace/1,on_load/1,deep_exception/1,
+ upgrade/1,
exception_nocatch/1,bit_syntax/1]).
%% Helper functions.
@@ -46,6 +47,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
Common = [errors, on_load],
NotHipe = [process_specs, basic, flags, pam, change_pam,
+ upgrade,
return_trace, exception_trace, deep_exception,
exception_nocatch, bit_syntax],
Hipe = [hipe],
@@ -76,7 +78,13 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
+ ?t:timetrap_cancel(Dog),
+
+ %% Reloading the module will clear all trace patterns, and
+ %% in a debug-compiled emulator run assertions of the counters
+ %% for the number of traced exported functions in this module.
+
+ c:l(?MODULE).
hipe(Config) when is_list(Config) ->
?line 0 = erlang:trace_pattern({?MODULE,worker_foo,1}, true),
@@ -185,8 +193,13 @@ basic() ->
%% Trace some functions...
?line trace_func({lists,'_','_'}, []),
+
+ %% Make sure that tracing the same functions more than once
+ %% does not cause any problems.
+ ?line 3 = trace_func({?MODULE,foo,'_'}, true),
?line 3 = trace_func({?MODULE,foo,'_'}, true),
?line 1 = trace_func({?MODULE,bar,0}, true),
+ ?line 1 = trace_func({?MODULE,bar,0}, true),
?line {traced,global} = trace_info({?MODULE,bar,0}, traced),
?line 1 = trace_func({erlang,list_to_integer,1}, true),
?line {traced,global} = trace_info({erlang,list_to_integer,1}, traced),
@@ -267,6 +280,118 @@ foo() -> foo0.
foo(X) -> X+1.
foo(X, Y) -> X+Y.
+
+%% Note that the semantics that this test case verifies
+%% are not explicitly specified in the docs (what I could find in R15B).
+%% This test case was written to verify that we do not change
+%% any behaviour with the introduction of "block-free" upgrade in R16.
+%% In short: Do not refer to this test case as an authority of how it must work.
+upgrade(doc) ->
+ "Test tracing on module being upgraded";
+upgrade(Config) when is_list(Config) ->
+ V1 = compile_version(my_upgrade_test, 1, Config),
+ V2 = compile_version(my_upgrade_test, 2, Config),
+ start_tracer(),
+ upgrade_do(V1, V2, false),
+ upgrade_do(V1, V2, true).
+
+upgrade_do(V1, V2, TraceLocalVersion) ->
+ {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V1),
+
+
+ %% Test that trace is cleared after load_module
+
+ trace_func({my_upgrade_test,'_','_'}, [], [global]),
+ case TraceLocalVersion of
+ true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
+ _ -> ok
+ end,
+ 1 = my_upgrade_test:version(),
+ 1 = my_upgrade_test:do_local(),
+ 1 = my_upgrade_test:do_real_local(),
+ put('F1_exp', my_upgrade_test:make_fun_exp()),
+ put('F1_loc', my_upgrade_test:make_fun_local()),
+ 1 = (get('F1_exp'))(),
+ 1 = (get('F1_loc'))(),
+
+ Self = self(),
+ expect({trace,Self,call,{my_upgrade_test,version,[]}}),
+ expect({trace,Self,call,{my_upgrade_test,do_local,[]}}),
+ expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}),
+ case TraceLocalVersion of
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}});
+ _ -> ok
+ end,
+ expect({trace,Self,call,{my_upgrade_test,make_fun_exp,[]}}),
+ expect({trace,Self,call,{my_upgrade_test,make_fun_local,[]}}),
+ expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F1_exp
+ case TraceLocalVersion of
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F1_loc
+ _ -> ok
+ end,
+
+ {module,my_upgrade_test} = erlang:load_module(my_upgrade_test, V2),
+ 2 = my_upgrade_test:version(),
+ put('F2_exp', my_upgrade_test:make_fun_exp()),
+ put('F2_loc', my_upgrade_test:make_fun_local()),
+ 2 = (get('F1_exp'))(),
+ 1 = (get('F1_loc'))(),
+ 2 = (get('F2_exp'))(),
+ 2 = (get('F2_loc'))(),
+ expect(),
+
+ put('F1_exp', undefined),
+ put('F1_loc', undefined),
+ erlang:garbage_collect(),
+ erlang:purge_module(my_upgrade_test),
+
+ % Test that trace is cleared after delete_module
+
+ trace_func({my_upgrade_test,'_','_'}, [], [global]),
+ case TraceLocalVersion of
+ true -> trace_func({my_upgrade_test,local_version,0}, [], [local]);
+ _ -> ok
+ end,
+ 2 = my_upgrade_test:version(),
+ 2 = my_upgrade_test:do_local(),
+ 2 = my_upgrade_test:do_real_local(),
+ 2 = (get('F2_exp'))(),
+ 2 = (get('F2_loc'))(),
+
+ expect({trace,Self,call,{my_upgrade_test,version,[]}}),
+ expect({trace,Self,call,{my_upgrade_test,do_local,[]}}),
+ expect({trace,Self,call,{my_upgrade_test,do_real_local,[]}}),
+ case TraceLocalVersion of
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}});
+ _ -> ok
+ end,
+ expect({trace,Self,call,{my_upgrade_test,version,[]}}), % F2_exp
+ case TraceLocalVersion of
+ true -> expect({trace,Self,call,{my_upgrade_test,local_version,[]}}); % F2_loc
+ _ -> ok
+ end,
+
+ true = erlang:delete_module(my_upgrade_test),
+ {'EXIT',{undef,_}} = (catch my_upgrade_test:version()),
+ {'EXIT',{undef,_}} = (catch ((get('F2_exp'))())),
+ 2 = (get('F2_loc'))(),
+ expect(),
+
+ put('F2_exp', undefined),
+ put('F2_loc', undefined),
+ erlang:garbage_collect(),
+ erlang:purge_module(my_upgrade_test),
+ ok.
+
+compile_version(Module, Version, Config) ->
+ Data = ?config(data_dir, Config),
+ File = filename:join(Data, atom_to_list(Module)),
+ {ok,Module,Bin} = compile:file(File, [{d,'VERSION',Version},
+ binary,report]),
+ Bin.
+
+
+
%% Test flags (arity, timestamp) for call_trace/3.
%% Also, test the '{tracer,Pid}' option.
flags(_Config) ->
@@ -1151,11 +1276,13 @@ trace_info(What, Key) ->
Res.
trace_func(MFA, MatchSpec) ->
- get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA, MatchSpec]}},
+ trace_func(MFA, MatchSpec, []).
+trace_func(MFA, MatchSpec, Flags) ->
+ get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA, MatchSpec, Flags]}},
Res = receive
{apply_result,Result} -> Result
end,
- ok = io:format("trace_pattern(~p, ~p) -> ~p", [MFA,MatchSpec,Res]),
+ ok = io:format("trace_pattern(~p, ~p, ~p) -> ~p", [MFA,MatchSpec,Flags,Res]),
Res.
trace_pid(Pid, On, Flags) ->
diff --git a/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl b/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl
new file mode 100644
index 0000000000..11b8a95209
--- /dev/null
+++ b/erts/emulator/test/call_trace_SUITE_data/my_upgrade_test.erl
@@ -0,0 +1,26 @@
+-module(my_upgrade_test).
+
+-export([version/0]).
+-export([do_local/0]).
+-export([do_real_local/0]).
+-export([make_fun_exp/0]).
+-export([make_fun_local/0]).
+
+
+version() ->
+ ?VERSION.
+
+do_local() ->
+ version().
+
+do_real_local() ->
+ local_version().
+
+local_version() ->
+ ?VERSION.
+
+make_fun_exp() ->
+ fun() -> ?MODULE:version() end.
+
+make_fun_local() ->
+ fun() -> local_version() end.
diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl
new file mode 100644
index 0000000000..aa9e4c96c6
--- /dev/null
+++ b/erts/emulator/test/code_parallel_load_SUITE.erl
@@ -0,0 +1,198 @@
+%%
+%% %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: Björn-Egil Dahlberg
+
+-module(code_parallel_load_SUITE).
+-export([
+ all/0,
+ suite/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ end_per_testcase/2
+ ]).
+
+-export([
+ multiple_load_check_purge_repeat/1,
+ many_load_distributed_only_once/1
+ ]).
+
+-define(model, code_parallel_load_SUITE_model).
+-define(interval, 50).
+-define(number_of_processes, 160).
+-define(passes, 4).
+
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ multiple_load_check_purge_repeat,
+ many_load_distributed_only_once
+ ].
+
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ Dog=?t:timetrap(?t:minutes(3)),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Func, Config) ->
+ SConf = ?config(save_config, Config),
+ Pids = proplists:get_value(purge_pids, SConf),
+
+ case check_old_code(?model) of
+ true -> check_and_purge_processes_code(Pids, ?model);
+ _ -> ok
+ end,
+ case erlang:delete_module(?model) of
+ true -> check_and_purge_processes_code(Pids, ?model);
+ _ -> ok
+ end,
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+
+multiple_load_check_purge_repeat(_Conf) ->
+ Ts = [v1,v2,v3,v4,v5,v6],
+
+ %% generate code that receives a token, code switches to new code
+ %% then matches this token against a literal code token
+ %% should be identical
+ %% (smoke test for parallel code loading
+ Codes = [{T, generate(?model, [], [
+ format("check(T) -> receive {_Pid, change, T1} -> "
+ " ~w:check(T1)\n"
+ " after 0 -> T = f(), check(T) end.\n", [?model]),
+ format("f() -> ~w.~n", [T])
+ ])} || T <- Ts],
+
+ Pids = setup_code_changer(Codes),
+ {save_config, [{purge_pids,Pids}]}.
+
+setup_code_changer([{Token,Code}|Cs] = Codes) ->
+ {module, ?model} = erlang:load_module(?model,Code),
+ Pids = setup_checkers(Token,?number_of_processes),
+ code_changer(Cs, Codes, ?interval,Pids,?passes),
+ Pids.
+
+code_changer(_, _, _, Pids, 0) ->
+ [unlink(Pid) || Pid <- Pids],
+ [exit(Pid, die) || Pid <- Pids],
+ io:format("done~n"),
+ ok;
+code_changer([], Codes, T, Pids, Ps) ->
+ code_changer(Codes, Codes, T, Pids, Ps - 1);
+code_changer([{Token,Code}|Cs], Codes, T, Pids, Ps) ->
+ receive after T ->
+ io:format("load code with token ~4w : pass ~4w~n", [Token, Ps]),
+ {module, ?model} = erlang:load_module(?model, Code),
+ % this is second time we call load_module for this module
+ % so it should have old code
+ [Pid ! {self(), change, Token} || Pid <- Pids],
+ % should we wait a moment or just blantantly try to check and purge repeatadly?
+ receive after 1 -> ok end,
+ ok = check_and_purge_processes_code(Pids, ?model),
+ code_changer(Cs, Codes, T, Pids, Ps)
+ end.
+
+
+
+many_load_distributed_only_once(_Conf) ->
+ Ts = [<<"first version">>, <<"second version">>],
+
+ [{Token1,Code1},{Token2, Code2}] = [{T, generate(?model, [], [
+ "check({<<\"second version\">> = V, Pid}) -> V = f(), Pid ! {self(), completed, V}, ok;\n" ++
+ format("check(T) -> receive {Pid, change, T1, B} -> "
+ " Res = erlang:load_module(~w, B), Pid ! {self(), change, Res},\n"
+ " ~w:check({T1, Pid})\n"
+ " after 0 -> T = f(), check(T) end.\n", [?model, ?model]),
+ format("f() -> ~w.~n", [T])
+ ])} || T <- Ts],
+
+
+ {module, ?model} = erlang:load_module(?model, Code1),
+ Pids = setup_checkers(Token1,?number_of_processes),
+
+ receive after 1000 -> ok end, % give 'em some time to spin up
+ [Pid ! {self(), change, Token2, Code2} || Pid <- Pids],
+ Loads = [receive {Pid, change, Res} -> Res end || Pid <- Pids],
+ [receive {Pid, completed, Token2} -> ok end || Pid <- Pids],
+
+ ok = ensure_only_one_load(Loads, 0),
+ {save_config, [{purge_pids,Pids}]}.
+
+ensure_only_one_load([], 1) -> ok;
+ensure_only_one_load([], _) -> too_many_loads;
+ensure_only_one_load([{module, ?model}|Loads], N) ->
+ ensure_only_one_load(Loads, N + 1);
+ensure_only_one_load([{error, not_purged}|Loads], N) ->
+ ensure_only_one_load(Loads, N).
+% no other return values are allowed from load_module
+
+
+%% aux
+
+setup_checkers(_,0) -> [];
+setup_checkers(T,N) -> [spawn_link(fun() -> ?model:check(T) end) | setup_checkers(T, N-1)].
+
+check_and_purge_processes_code(Pids, M) ->
+ check_and_purge_processes_code(Pids, M, []).
+check_and_purge_processes_code([], M, []) ->
+ erlang:purge_module(M),
+ ok;
+check_and_purge_processes_code([], M, Pending) ->
+ io:format("Processes ~w are still executing old code - retrying.~n", [Pending]),
+ check_and_purge_processes_code(Pending, M, []);
+check_and_purge_processes_code([Pid|Pids], M, Pending) ->
+ case erlang:check_process_code(Pid, M) of
+ false ->
+ check_and_purge_processes_code(Pids, M, Pending);
+ true ->
+ check_and_purge_processes_code(Pids, M, [Pid|Pending])
+ end.
+
+
+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)].
+
+format(F,Ts) -> lists:flatten(io_lib:format(F, Ts)).
diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl
index 6e15c228cd..ffb68ed4ee 100644
--- a/erts/emulator/test/ddll_SUITE.erl
+++ b/erts/emulator/test/ddll_SUITE.erl
@@ -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
@@ -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/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl
index 4acbe8c6e0..2baf91cf29 100644
--- a/erts/emulator/test/decode_packet_SUITE.erl
+++ b/erts/emulator/test/decode_packet_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
@@ -403,6 +403,9 @@ http_request(Msg) ->
{"Other-Field: with some text\r\n",
{http_header,0, "Other-Field" ,undefined, "with some text"},
{http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}},
+ {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n",
+ {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"},
+ {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}},
{"Multi-Line: Once upon a time in a land far far away,\r\n"
" there lived a princess imprisoned in the highest tower\r\n"
" of the most haunted castle.\r\n",
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index a833e357cf..aa6cf2b881 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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..dfba7d098f 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -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
@@ -77,7 +77,9 @@
thread_mseg_alloc_cache_clean/1,
otp_9302/1,
thr_free_drv/1,
- async_blast/1]).
+ async_blast/1,
+ thr_msg_blast/1,
+ consume_timeslice/1]).
-export([bin_prefix/2]).
@@ -147,7 +149,9 @@ all() ->
thread_mseg_alloc_cache_clean,
otp_9302,
thr_free_drv,
- async_blast].
+ async_blast,
+ thr_msg_blast,
+ consume_timeslice].
groups() ->
[{timer, [],
@@ -1136,7 +1140,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,12 +2016,388 @@ 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.
+
+consume_timeslice(Config) when is_list(Config) ->
+ %%
+ %% Verify that erl_drv_consume_timeslice() works.
+ %%
+ %% The first four cases expect that the command signal is
+ %% delivered immediately, i.e., isn't scheduled. Since there
+ %% are no conflicts these signals should normally be delivered
+ %% immediately. However some builds and configurations may
+ %% schedule these ops anyway, in these cases we do not verify
+ %% scheduling counts.
+ %%
+ %% When signal is delivered immediately we must take into account
+ %% that process and port are "virtualy" scheduled out and in
+ %% in the trace generated.
+ %%
+ %% Port ! {_, {command, _}, and port_command() differs. The send
+ %% instruction needs to check if the caller is out of reductions
+ %% at the end of the instruction, since no erlang function call
+ %% is involved. Otherwise, a sequence of send instructions would
+ %% not be scheduled out even when out of reductions. port_commond()
+ %% doesn't do that since it will always (since R16A) be called via
+ %% the erlang wrappers in the erlang module.
+ %%
+ %% The last two cases tests scheduled operations. We create
+ %% a conflict by executing at the same time on different
+ %% schedulers. When only one scheduler we enable parallelism on
+ %% the port instead.
+ %%
+ Path = ?config(data_dir, Config),
+ erl_ddll:start(),
+ ok = load_driver(Path, consume_timeslice_drv),
+ Port = open_port({spawn, consume_timeslice_drv}, [{parallelism, false}]),
+
+ Parent = self(),
+ Go = make_ref(),
+
+ "enabled" = port_control(Port, $E, ""),
+ Proc1 = spawn_link(fun () ->
+ receive Go -> ok end,
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}}
+ end),
+ receive after 100 -> ok end,
+ count_pp_sched_start(),
+ Proc1 ! Go,
+ wait_command_msgs(Port, 10),
+ [{Port, Sprt1}, {Proc1, Sproc1}] = count_pp_sched_stop([Port, Proc1]),
+ case Sprt1 of
+ 10 ->
+ true = in_range(5, Sproc1-10, 7);
+ _ ->
+ case erlang:system_info(lock_checking) of
+ true -> ?t:format("Ignore bad sched count due to lock checking", []);
+ false -> ?t:fail({unexpected_sched_counts, Sprt1, Sproc1})
+ end
+ end,
+
+ "disabled" = port_control(Port, $D, ""),
+ Proc2 = spawn_link(fun () ->
+ receive Go -> ok end,
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}},
+ Port ! {Parent, {command, ""}}
+ end),
+ receive after 100 -> ok end,
+ count_pp_sched_start(),
+ Proc2 ! Go,
+ wait_command_msgs(Port, 10),
+ [{Port, Sprt2}, {Proc2, Sproc2}] = count_pp_sched_stop([Port, Proc2]),
+ case Sprt2 of
+ 10 ->
+ true = in_range(1, Sproc2-10, 2);
+ _ ->
+ case erlang:system_info(lock_checking) of
+ true -> ?t:format("Ignore bad sched count due to lock checking", []);
+ false -> ?t:fail({unexpected_sched_counts, Sprt2, Sproc2})
+ end
+ end,
+
+ "enabled" = port_control(Port, $E, ""),
+ Proc3 = spawn_link(fun () ->
+ receive Go -> ok end,
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, "")
+ end),
+ count_pp_sched_start(),
+ Proc3 ! Go,
+ wait_command_msgs(Port, 10),
+ [{Port, Sprt3}, {Proc3, Sproc3}] = count_pp_sched_stop([Port, Proc3]),
+ case Sprt3 of
+ 10 ->
+ true = in_range(5, Sproc3-10, 7);
+ _ ->
+ case erlang:system_info(lock_checking) of
+ true -> ?t:format("Ignore bad sched count due to lock checking", []);
+ false -> ?t:fail({unexpected_sched_counts, Sprt3, Sproc3})
+ end
+ end,
+ "disabled" = port_control(Port, $D, ""),
+ Proc4 = spawn_link(fun () ->
+ receive Go -> ok end,
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, ""),
+ port_command(Port, "")
+ end),
+ count_pp_sched_start(),
+ Proc4 ! Go,
+ wait_command_msgs(Port, 10),
+ [{Port, Sprt4}, {Proc4, Sproc4}] = count_pp_sched_stop([Port, Proc4]),
+ case Sprt4 of
+ 10 ->
+ true = in_range(1, Sproc4-10, 2);
+ _ ->
+ case erlang:system_info(lock_checking) of
+ true -> ?t:format("Ignore bad sched count due to lock checking", []);
+ false -> ?t:fail({unexpected_sched_counts, Sprt4, Sproc4})
+ end
+ end,
+
+ SOnl = erlang:system_info(schedulers_online),
+ %% If only one scheduler use port with parallelism set to true,
+ %% in order to trigger scheduling of command signals
+ Port2 = case SOnl of
+ 1 ->
+ Port ! {self(), close},
+ receive {Port, closed} -> ok end,
+ open_port({spawn, consume_timeslice_drv},
+ [{parallelism, true}]);
+ _ ->
+ process_flag(scheduler, 1),
+ 1 = erlang:system_info(scheduler_id),
+ Port
+ end,
+ count_pp_sched_start(),
+ "enabled" = port_control(Port2, $E, ""),
+ W5 = case SOnl of
+ 1 ->
+ false;
+ _ ->
+ W1= spawn_opt(fun () ->
+ 2 = erlang:system_info(scheduler_id),
+ "sleeped" = port_control(Port2, $S, "")
+ end, [link,{scheduler,2}]),
+ receive after 100 -> ok end,
+ W1
+ end,
+ Proc5 = spawn_opt(fun () ->
+ receive Go -> ok end,
+ 1 = erlang:system_info(scheduler_id),
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}}
+ end, [link,{scheduler,1}]),
+ receive after 100 -> ok end,
+ Proc5 ! Go,
+ wait_procs_exit([W5, Proc5]),
+ wait_command_msgs(Port2, 10),
+ [{Port2, Sprt5}, {Proc5, Sproc5}] = count_pp_sched_stop([Port2, Proc5]),
+ true = in_range(2, Sproc5, 3),
+ true = in_range(7, Sprt5, 20),
+
+ count_pp_sched_start(),
+ "disabled" = port_control(Port2, $D, ""),
+ W6 = case SOnl of
+ 1 ->
+ false;
+ _ ->
+ W2= spawn_opt(fun () ->
+ 2 = erlang:system_info(scheduler_id),
+ "sleeped" = port_control(Port2, $S, "")
+ end, [link,{scheduler,2}]),
+ receive after 100 -> ok end,
+ W2
+ end,
+ Proc6 = spawn_opt(fun () ->
+ receive Go -> ok end,
+ 1 = erlang:system_info(scheduler_id),
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}},
+ Port2 ! {Parent, {command, ""}}
+ end, [link,{scheduler,1}]),
+ receive after 100 -> ok end,
+ Proc6 ! Go,
+ wait_procs_exit([W6, Proc6]),
+ wait_command_msgs(Port2, 10),
+ [{Port2, Sprt6}, {Proc6, Sproc6}] = count_pp_sched_stop([Port2, Proc6]),
+ true = in_range(2, Sproc6, 3),
+ true = in_range(3, Sprt6, 6),
+
+ process_flag(scheduler, 0),
+
+ Port2 ! {self(), close},
+ receive {Port2, closed} -> ok end,
+ ok.
+
+wait_command_msgs(_, 0) ->
+ ok;
+wait_command_msgs(Port, N) ->
+ receive
+ {Port, command} ->
+ wait_command_msgs(Port, N-1)
+ end.
+
+in_range(Low, Val, High) when is_integer(Low),
+ is_integer(Val),
+ is_integer(High),
+ Low =< Val,
+ Val =< High ->
+ true;
+in_range(Low, Val, High) when is_integer(Low),
+ is_integer(Val),
+ is_integer(High) ->
+ false.
+
+count_pp_sched_start() ->
+ erlang:trace(all, true, [running_procs, running_ports, {tracer, self()}]),
+ ok.
+
+count_pp_sched_stop(Ps) ->
+ Td = erlang:trace_delivered(all),
+ erlang:trace(all, false, [running_procs, running_ports, {tracer, self()}]),
+ PNs = lists:map(fun (P) -> {P, 0} end, Ps),
+ receive {trace_delivered, all, Td} -> ok end,
+ Res = count_proc_sched(Ps, PNs),
+ ?t:format("Scheduling counts: ~p~n", [Res]),
+ erlang:display({scheduling_counts, Res}),
+ Res.
+
+do_inc_pn(_P, []) ->
+ throw(undefined);
+do_inc_pn(P, [{P,N}|PNs]) ->
+ [{P,N+1}|PNs];
+do_inc_pn(P, [PN|PNs]) ->
+ [PN|do_inc_pn(P, PNs)].
+
+inc_pn(P, PNs) ->
+ try
+ do_inc_pn(P, PNs)
+ catch
+ throw:undefined -> PNs
+ end.
+
+count_proc_sched(Ps, PNs) ->
+ receive
+ TT when element(1, TT) == trace, element(3, TT) == in ->
+% erlang:display(TT),
+ count_proc_sched(Ps, inc_pn(element(2, TT), PNs));
+ TT when element(1, TT) == trace, element(3, TT) == out ->
+ count_proc_sched(Ps, PNs)
+ after 0 ->
+ PNs
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Utilities
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%flush_msgs() ->
+% receive
+% M ->
+% erlang:display(M),
+% flush_msgs()
+% after 0 ->
+% ok
+% end.
+
+wait_procs_exit([]) ->
+ ok;
+wait_procs_exit([P|Ps]) when is_pid(P) ->
+ Mon = erlang:monitor(process, P),
+ receive
+ {'DOWN', Mon, process, P, _} ->
+ wait_procs_exit(Ps)
+ end;
+wait_procs_exit([_|Ps]) ->
+ wait_procs_exit(Ps).
+
get_port_msg(Port, Timeout) ->
receive
{Port, What} ->
diff --git a/erts/emulator/test/driver_SUITE_data/Makefile.src b/erts/emulator/test/driver_SUITE_data/Makefile.src
index 9cc107cc66..1fedd72200 100644
--- a/erts/emulator/test/driver_SUITE_data/Makefile.src
+++ b/erts/emulator/test/driver_SUITE_data/Makefile.src
@@ -14,7 +14,9 @@ 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@ \
+ consume_timeslice_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..72be107168 100644
--- a/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/async_blast_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-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,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/consume_timeslice_drv.c b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c
new file mode 100644
index 0000000000..578a210ab2
--- /dev/null
+++ b/erts/emulator/test/driver_SUITE_data/consume_timeslice_drv.c
@@ -0,0 +1,172 @@
+/*
+ * %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/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, 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"
+#ifdef __WIN32__
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+static void stop(ErlDrvData drv_data);
+static ErlDrvData start(ErlDrvPort port,
+ char *command);
+static void output(ErlDrvData drv_data,
+ char *buf, ErlDrvSizeT len);
+static ErlDrvSSizeT control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen);
+
+static ErlDrvEntry consume_timeslice_drv_entry = {
+ NULL /* init */,
+ start,
+ stop,
+ output,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "consume_timeslice_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 tport;
+ ErlDrvTermData cmd_msg[6];
+ int consume_timeslice;
+} consume_timeslice_data_t;
+
+
+DRIVER_INIT(consume_timeslice_drv)
+{
+ return &consume_timeslice_drv_entry;
+}
+
+static void stop(ErlDrvData drv_data)
+{
+ driver_free((void *) drv_data);
+}
+
+static ErlDrvData start(ErlDrvPort port,
+ char *command)
+{
+ consume_timeslice_data_t *ctsd;
+
+ ctsd = driver_alloc(sizeof(consume_timeslice_data_t));
+ if (!ctsd)
+ return ERL_DRV_ERROR_GENERAL;
+
+ ctsd->port = port;
+ ctsd->tport = driver_mk_port(port);
+ ctsd->consume_timeslice = 0;
+
+ ctsd->cmd_msg[0] = ERL_DRV_PORT;
+ ctsd->cmd_msg[1] = ctsd->tport;
+ ctsd->cmd_msg[2] = ERL_DRV_ATOM;
+ ctsd->cmd_msg[3] = driver_mk_atom("command");
+ ctsd->cmd_msg[4] = ERL_DRV_TUPLE;
+ ctsd->cmd_msg[5] = (ErlDrvTermData) 2;
+
+ return (ErlDrvData) ctsd;
+}
+
+static void output(ErlDrvData drv_data,
+ char *buf, ErlDrvSizeT len)
+{
+ consume_timeslice_data_t *ctsd = (consume_timeslice_data_t *) drv_data;
+ int res;
+
+ if (ctsd->consume_timeslice) {
+ int res = erl_drv_consume_timeslice(ctsd->port, 50);
+ if (res < 0) {
+ driver_failure_atom(ctsd->port, "erl_drv_consume_timeslice() failed");
+ return;
+ }
+ }
+
+ res = erl_drv_output_term(ctsd->tport,
+ ctsd->cmd_msg,
+ sizeof(ctsd->cmd_msg)/sizeof(ErlDrvTermData));
+ if (res <= 0) {
+ driver_failure_atom(ctsd->port, "erl_drv_output_term() failed");
+ return;
+ }
+}
+static ErlDrvSSizeT control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ consume_timeslice_data_t *ctsd = (consume_timeslice_data_t *) drv_data;
+ int res;
+ char *res_str;
+ ErlDrvSSizeT res_len;
+
+ switch (command) {
+ case 'E':
+ ctsd->consume_timeslice = 1;
+ res_str = "enabled";
+ break;
+ case 'D':
+ ctsd->consume_timeslice = 0;
+ res_str = "disabled";
+ break;
+ case 'S':
+#ifdef __WIN32__
+ Sleep((DWORD) 1000);
+#else
+ sleep(1);
+#endif
+ res_str = "sleeped";
+ break;
+ default:
+ res_str = "what?";
+ break;
+ }
+
+ res_len = strlen(res_str);
+ if (res_len > rlen) {
+ char *abuf = driver_alloc(sizeof(char)*res_len);
+ if (!abuf) {
+ driver_failure_atom(ctsd->port, "driver_alloc() failed");
+ return 0;
+ }
+ *rbuf = abuf;
+ }
+
+ memcpy((void *) *rbuf, (void *) res_str, res_len);
+
+ return res_len;
+}
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..9d8bbac231 100644
--- a/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/ioq_exit_drv.c
@@ -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
@@ -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..88df73f696 100644
--- a/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/otp_9302_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-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
@@ -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/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 9ac004200e..65367eab98 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -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
@@ -21,6 +21,8 @@
init_per_group/2,end_per_group/2]).
-export([iter_max_files/1]).
+-export([do_iter_max_files/2]).
+
-include_lib("test_server/include/test_server.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -51,11 +53,17 @@ end_per_group(_GroupName, Config) ->
iter_max_files(suite) -> [];
iter_max_files(Config) when is_list(Config) ->
- ?line DataDir = ?config(data_dir,Config),
- ?line TestFile = filename:join(DataDir, "existing_file"),
- ?line L = do_iter_max_files(10, TestFile),
- ?line io:format("Number of files opened in each test:~n~w\n", [L]),
- ?line all_equal(L),
+ DataDir = ?config(data_dir,Config),
+ TestFile = filename:join(DataDir, "existing_file"),
+ N = 10,
+ %% Run on a different node in order to set the max ports
+ Dir = filename:dirname(code:which(?MODULE)),
+ {ok,Node} = test_server:start_node(test_iter_max_files,slave,
+ [{args,"+Q 1524 -pa " ++ Dir}]),
+ L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]),
+ test_server:stop_node(Node),
+ io:format("Number of files opened in each test:~n~w\n", [L]),
+ all_equal(L),
Head = hd(L),
if Head >= 2 -> ok;
true -> ?line test_server:fail(too_few_files)
@@ -91,6 +99,6 @@ open_files(Name) ->
{ok, Fd} ->
[Fd| open_files(Name)];
{error, Reason} ->
- io:format("Error reason: ~p", [Reason]),
+% io:format("Error reason: ~p", [Reason]),
[]
end.
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..1de6d6fb56 100644
--- a/erts/emulator/test/estone_SUITE.erl
+++ b/erts/emulator/test/estone_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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(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/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index ef06845cf2..36ba4e0f48 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -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..36889b6c36 100644
--- a/erts/emulator/test/gc_SUITE.erl
+++ b/erts/emulator/test/gc_SUITE.erl
@@ -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
@@ -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/hash_SUITE.erl b/erts/emulator/test/hash_SUITE.erl
index 830ed91da9..43c9d64af7 100644
--- a/erts/emulator/test/hash_SUITE.erl
+++ b/erts/emulator/test/hash_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -75,7 +76,7 @@ config(priv_dir,_) ->
test_phash2/1,otp_5292/1,bit_level_binaries/1,otp_7127/1,
end_per_testcase/2,init_per_testcase/2]).
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(test_server:minutes(10)),
+ Dog=test_server:timetrap(test_server:minutes(10)),
[{watchdog, Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -169,24 +170,24 @@ otp_7127(Config) when is_list(Config) ->
%% define -DSTANDALONE when compiling.
%%
basic_test() ->
- ?line 685556714 = erlang:phash({a,b,c},16#FFFFFFFF),
- ?line 14468079 = erlang:hash({a,b,c},16#7FFFFFF),
- ?line 37442646 = erlang:phash([a,b,c,{1,2,3},c:pid(0,2,3),
+ 685556714 = erlang:phash({a,b,c},16#FFFFFFFF),
+ 14468079 = erlang:hash({a,b,c},16#7FFFFFF),
+ 37442646 = erlang:phash([a,b,c,{1,2,3},c:pid(0,2,3),
16#77777777777777],16#FFFFFFFF),
- ?line Comment = case erlang:hash([a,b,c,{1,2,3},c:pid(0,2,3),
+ Comment = case erlang:hash([a,b,c,{1,2,3},c:pid(0,2,3),
16#77777777777777],16#7FFFFFF) of
102727602 ->
- ?line big = erlang:system_info(endian),
+ big = erlang:system_info(endian),
"Big endian machine";
105818829 ->
- ?line little = erlang:system_info(endian),
+ little = erlang:system_info(endian),
"Little endian machine"
end,
ExternalReference = <<131,114,0,3,100,0,13,110,111,110,111,100,101,64,
110,111,104,111,115,116,0,0,0,0,122,0,0,0,0,0,0,0,0>>,
- ?line 1113403635 = erlang:phash(binary_to_term(ExternalReference),
+ 1113403635 = erlang:phash(binary_to_term(ExternalReference),
16#FFFFFFFF),
- ?line 123 = erlang:hash(binary_to_term(ExternalReference),
+ 123 = erlang:hash(binary_to_term(ExternalReference),
16#7FFFFFF),
ExternalFun = <<131,117,0,0,0,3,103,100,0,13,110,111,110,111,100,101,64,
110,111,104,111,115,116,0,0,0,38,0,0,0,0,0,100,0,8,101,
@@ -204,9 +205,9 @@ basic_test() ->
104,101,108,108,100,0,10,108,111,99,97,108,95,102,117,
110,99,108,0,0,0,1,103,100,0,13,110,111,110,111,100,101,
64,110,111,104,111,115,116,0,0,0,22,0,0,0,0,0,106>>,
- ?line 170987488 = erlang:phash(binary_to_term(ExternalFun),
+ 170987488 = erlang:phash(binary_to_term(ExternalFun),
16#FFFFFFFF),
- ?line 124460689 = erlang:hash(binary_to_term(ExternalFun),
+ 124460689 = erlang:hash(binary_to_term(ExternalFun),
16#7FFFFFF),
case (catch erlang:phash(1,0)) of
{'EXIT',{badarg, _}} ->
@@ -237,23 +238,23 @@ range_test() ->
spread_test(N) ->
- ?line test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
+ test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
X
end),
- ?line test_fun(N,{erlang,phash},0,fun(X) ->
+ test_fun(N,{erlang,phash},0,fun(X) ->
X
end),
- ?line test_fun(N,{erlang,phash},16#123456789ABCDEF123456789ABCDEF,fun(X) ->
+ test_fun(N,{erlang,phash},16#123456789ABCDEF123456789ABCDEF,fun(X) ->
X
end),
- ?line test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
+ test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
integer_to_list(X)
end),
- ?line test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
+ test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
integer_to_bytelist(X,[])
end),
- ?line test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
- integer_to_binary(X)
+ test_fun(N,{erlang,phash},16#50000000000,fun(X) ->
+ integer_to_binary_value(X)
end).
@@ -265,14 +266,14 @@ cmp_test(N) ->
do_cmp_hashes(0,_) ->
ok;
do_cmp_hashes(N,Steps) ->
- ?line R0 = random:uniform(1 bsl Steps - 1) + random:uniform(16#FFFFFFFF),
- ?line R = case random:uniform(2) of
+ R0 = random:uniform(1 bsl Steps - 1) + random:uniform(16#FFFFFFFF),
+ R = case random:uniform(2) of
1 ->
R0;
_ ->
-R0
end,
- ?line NSteps = case N rem 10 of
+ NSteps = case N rem 10 of
0 ->
case (Steps + 8) rem 1024 of
0 ->
@@ -283,9 +284,9 @@ do_cmp_hashes(N,Steps) ->
_ ->
Steps
end,
- ?line X = erlang:phash(R,16#FFFFFFFF),
- ?line Y = make_hash(R,16#FFFFFFFF),
- ?line case X =:= Y of
+ X = erlang:phash(R,16#FFFFFFFF),
+ Y = make_hash(R,16#FFFFFFFF),
+ case X =:= Y of
true ->
do_cmp_hashes(N - 1, NSteps);
_ ->
@@ -363,6 +364,15 @@ phash2_test() ->
%% (cannot use block_hash due to compatibility issues...)
{abc,26499},
{abd,26500},
+ {'åäö', 62518},
+ %% 81 runes as an atom, 'ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰ'
+ {erlang:binary_to_term(<<131, 118, 0, 243, (unicode:characters_to_binary(lists:seq(5792, 5872)))/binary >>), 241561024},
+ %% åäö dynamic
+ {erlang:binary_to_term(<<131, 118, 0, 6, 195, 165, 195, 164, 195, 182>>),62518},
+ %% the atom '゙゚゛゜ゝゞゟ゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズ'
+ {erlang:binary_to_term(<<131, 118, 0, 102, (unicode:characters_to_binary(lists:seq(12441, 12542)))/binary>>), 246053818},
+ %% the atom, '😃'
+ {erlang:binary_to_term(<<131, 118, 0, 4, 240, 159, 152, 131>>), 1026307},
%% small
{0,3175731469},
@@ -469,8 +479,8 @@ phash2_test() ->
SpecFun = fun(S) -> sofs:no_elements(S) > 1 end,
F = sofs:relation_to_family(sofs:converse(sofs:relation(L))),
D = sofs:to_external(sofs:family_specification(SpecFun, F)),
- ?line [] = D,
- ?line [] = [{E,H,H2} || {E,H} <- L, (H2 = erlang:phash2(E, Max)) =/= H],
+ [] = D,
+ [] = [{E,H,H2} || {E,H} <- L, (H2 = erlang:phash2(E, Max)) =/= H],
ok.
-ifdef(FALSE).
@@ -497,17 +507,17 @@ otp_5292_test() ->
end,
S2 = md5([md5(hash_int(S, E, PH)) || {Start, N, Sz} <- d(),
{S, E} <- int(Start, N, Sz)]),
- ?line Comment = case S1 of
+ Comment = case S1 of
<<4,248,208,156,200,131,7,1,173,13,239,173,112,81,16,174>> ->
- ?line big = erlang:system_info(endian),
+ big = erlang:system_info(endian),
"Big endian machine";
<<180,28,33,231,239,184,71,125,76,47,227,241,78,184,176,233>> ->
- ?line little = erlang:system_info(endian),
+ little = erlang:system_info(endian),
"Little endian machine"
end,
- ?line <<124,81,198,121,174,233,19,137,10,83,33,80,226,111,238,99>> = S2,
- ?line 2 = erlang:hash(1, (1 bsl 27) -1),
- ?line {'EXIT', _} = (catch erlang:hash(1, (1 bsl 27))),
+ <<124,81,198,121,174,233,19,137,10,83,33,80,226,111,238,99>> = S2,
+ 2 = erlang:hash(1, (1 bsl 27) -1),
+ {'EXIT', _} = (catch erlang:hash(1, (1 bsl 27))),
{comment, Comment}.
d() ->
@@ -528,21 +538,21 @@ md5(T) ->
erlang:md5(term_to_binary(T)).
bit_level_binaries() ->
- ?line [3511317,7022633,14044578,28087749,56173436,112344123,90467083|_] =
+ [3511317,7022633,14044578,28087749,56173436,112344123,90467083|_] =
bit_level_all_different(fun erlang:hash/2),
- ?line [3511317,7022633,14044578,28087749,56173436,112344123,90467083|_] =
+ [3511317,7022633,14044578,28087749,56173436,112344123,90467083|_] =
bit_level_all_different(fun erlang:phash/2),
- ?line [102233154,19716,102133857,4532024,123369135,24565730,109558721|_] =
+ [102233154,19716,102133857,4532024,123369135,24565730,109558721|_] =
bit_level_all_different(fun erlang:phash2/2),
- ?line 13233341 = test_hash_phash(<<42:7>>, 16#7FFFFFF),
- ?line 79121243 = test_hash_phash(<<99:7>>, 16#7FFFFFF),
- ?line 95517726 = test_hash_phash(<<16#378ABF73:31>>, 16#7FFFFFF),
+ 13233341 = test_hash_phash(<<42:7>>, 16#7FFFFFF),
+ 79121243 = test_hash_phash(<<99:7>>, 16#7FFFFFF),
+ 95517726 = test_hash_phash(<<16#378ABF73:31>>, 16#7FFFFFF),
- ?line 64409098 = test_phash2(<<99:7>>, 16#7FFFFFF),
- ?line 55555814 = test_phash2(<<123,19:2>>, 16#7FFFFFF),
- ?line 83868582 = test_phash2(<<123,45,6:3>>, 16#7FFFFFF),
- ?line 2123204 = test_phash2(<<123,45,7:3>>, 16#7FFFFFF),
+ 64409098 = test_phash2(<<99:7>>, 16#7FFFFFF),
+ 55555814 = test_phash2(<<123,19:2>>, 16#7FFFFFF),
+ 83868582 = test_phash2(<<123,45,6:3>>, 16#7FFFFFF),
+ 2123204 = test_phash2(<<123,45,7:3>>, 16#7FFFFFF),
ok.
@@ -579,7 +589,7 @@ test_phash2(Bitstr, Rem) ->
otp_7127_test() ->
%% Used to return 2589127136.
- ?line 38990304 = erlang:phash2(<<"Scott9">>),
+ 38990304 = erlang:phash2(<<"Scott9">>),
ok.
%%
@@ -711,7 +721,7 @@ collect_hits() ->
init_table(),
N.
-integer_to_binary(N) ->
+integer_to_binary_value(N) ->
list_to_binary(lists:reverse(integer_to_bytelist(N,[]))).
integer_to_bytelist(0,Acc) ->
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 461773114e..8dbc6b6538 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
index 024c3456a8..a492501959 100644
--- a/erts/emulator/test/mtx_SUITE.erl
+++ b/erts/emulator/test/mtx_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
@@ -58,8 +58,12 @@
init_per_suite(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Lib = filename:join([DataDir, atom_to_list(?MODULE)]),
- ok = erlang:load_nif(Lib, none),
- Config.
+ case {erlang:load_nif(Lib, none),erlang:system_info(threads)} of
+ {{error,_},false} ->
+ {skip, "No thread support"};
+ _ ->
+ Config
+ end.
end_per_suite(Config) when is_list(Config) ->
catch erts_debug:set_internal_state(available_internal_state, false),
diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
index b6c843269c..e65d99e968 100644
--- a/erts/emulator/test/mtx_SUITE_data/Makefile.src
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. 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
@@ -27,4 +27,11 @@ LIBS = @ERTS_LIBS@
all: $(NIF_LIBS)
+mtx_SUITE.c: force_rebuild
+ touch mtx_SUITE.c
+
+force_rebuild:
+ echo "Force rebuild to compensate for emulator type dependencies"
+
+
@SHLIB_RULES@
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 6bd7361612..dcf58fa474 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_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
@@ -36,7 +36,7 @@
threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1,
is_checks/1,
get_length/1, make_atom/1, make_string/1, reverse_list_test/1,
- otp_9668/1
+ otp_9668/1, consume_timeslice/1
]).
-export([many_args_100/100]).
@@ -63,7 +63,7 @@ all() ->
resource_takeover, threading, send, send2, send3,
send_threaded, neg, is_checks, get_length, make_atom,
make_string,reverse_list_test,
- otp_9668
+ otp_9668, consume_timeslice
].
groups() ->
@@ -1259,6 +1259,108 @@ otp_9668(Config) ->
?line verify_tmpmem(TmpMem),
ok.
+consume_timeslice(Config) when is_list(Config) ->
+ CONTEXT_REDS = 2000,
+ Me = self(),
+ Go = make_ref(),
+ RedDiff = make_ref(),
+ Done = make_ref(),
+ DummyMFA = {?MODULE,dummy_call,1},
+ P = spawn(fun () ->
+ receive Go -> ok end,
+ {reductions, R1} = process_info(self(), reductions),
+ 1 = consume_timeslice_nif(100, false),
+ dummy_call(111),
+ 0 = consume_timeslice_nif(90, false),
+ dummy_call(222),
+ 1 = consume_timeslice_nif(10, false),
+ dummy_call(333),
+ 0 = consume_timeslice_nif(25, false),
+ 0 = consume_timeslice_nif(25, false),
+ 0 = consume_timeslice_nif(25, false),
+ 1 = consume_timeslice_nif(25, false),
+ 0 = consume_timeslice_nif(25, false),
+
+ ok = case consume_timeslice_nif(1, true) of
+ Cnt when Cnt > 70, Cnt < 80 -> ok;
+ Other -> Other
+ end,
+ dummy_call(444),
+
+ {reductions, R2} = process_info(self(), reductions),
+ Me ! {RedDiff, R2 - R1},
+ exit(Done)
+ end),
+ erlang:yield(),
+
+ erlang:trace_pattern(DummyMFA, [], [local]),
+ ?line 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]),
+
+ P ! Go,
+
+ %% receive Go -> ok end,
+ ?line {trace, P, in, _} = next_tmsg(P),
+
+ %% consume_timeslice_nif(100),
+ %% dummy_call(111)
+ ?line {trace, P, out, _} = next_tmsg(P),
+ ?line {trace, P, in, _} = next_tmsg(P),
+ ?line {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P),
+
+ %% consume_timeslice_nif(90),
+ %% dummy_call(222)
+ ?line {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P),
+
+ %% consume_timeslice_nif(10),
+ %% dummy_call(333)
+ ?line {trace, P, out, _} = next_tmsg(P),
+ ?line {trace, P, in, _} = next_tmsg(P),
+ ?line {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P),
+
+ %% 25,25,25,25, 25
+ ?line {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
+ ?line {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P),
+
+ %% consume_timeslice(1,true)
+ %% dummy_call(444)
+ ?line {trace, P, out, DummyMFA} = next_tmsg(P),
+ ?line {trace, P, in, DummyMFA} = next_tmsg(P),
+ ?line {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P),
+
+ %% exit(Done)
+ ?line {trace, P, exit, Done} = next_tmsg(P),
+
+ ExpReds = (100 + 90 + 10 + 25*5 + 75) * CONTEXT_REDS div 100,
+ receive
+ {RedDiff, Reductions} when Reductions < (ExpReds + 10), Reductions > (ExpReds - 10) ->
+ io:format("Reductions = ~p~n", [Reductions]),
+ ok;
+ {RedDiff, Reductions} ->
+ ?t:fail({unexpected_reduction_count, Reductions})
+ end,
+
+ none = next_msg(P),
+
+ ok.
+
+next_msg(Pid) ->
+ receive
+ M -> M
+ after 100 ->
+ none
+ end.
+
+next_tmsg(Pid) ->
+ receive TMsg when is_tuple(TMsg),
+ element(1, TMsg) == trace,
+ element(2, TMsg) == Pid ->
+ TMsg
+ after 100 ->
+ none
+ end.
+
+dummy_call(_) ->
+ ok.
tmpmem() ->
case erlang:system_info({allocator,temp_alloc}) of
@@ -1370,6 +1472,7 @@ reverse_list(_) -> ?nif_stub.
echo_int(_) -> ?nif_stub.
type_sizes() -> ?nif_stub.
otp_9668_nif(_) -> ?nif_stub.
+consume_timeslice_nif(_,_) -> ?nif_stub.
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 03092fef5e..c8f286f629 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2009-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2009-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
@@ -1456,6 +1456,27 @@ static ERL_NIF_TERM otp_9668_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
return atom_ok;
}
+static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int percent;
+ char atom[10];
+ int do_repeat;
+
+ if (!enif_get_int(env, argv[0], &percent) ||
+ !enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) {
+ return enif_make_badarg(env);
+ }
+ if (strcmp(atom , "true") == 0) {
+ int cnt = 1;
+ while (enif_consume_timeslice(env, percent) == 0 && cnt < 200)
+ cnt++;
+ return enif_make_int(env, cnt);
+ }
+ else {
+ return enif_make_int(env, enif_consume_timeslice(env, percent));
+ }
+}
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -1504,7 +1525,8 @@ static ErlNifFunc nif_funcs[] =
{"reverse_list",1, reverse_list},
{"echo_int", 1, echo_int},
{"type_sizes", 0, type_sizes},
- {"otp_9668_nif", 1, otp_9668_nif}
+ {"otp_9668_nif", 1, otp_9668_nif},
+ {"consume_timeslice_nif", 2, consume_timeslice_nif}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 6b6ac28e2e..57eec87e63 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.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
@@ -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..b92a0e2059 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -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
@@ -25,29 +25,34 @@
%% abs/1
%% float/1
%% float_to_list/1
+%% float_to_list/2
%% integer_to_list/1
%% list_to_float/1
%% list_to_integer/1
%% round/1
%% trunc/1
-
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2, t_abs/1, t_float/1,
- t_float_to_list/1, t_integer_to_list/1,
- t_list_to_integer/1,
- t_list_to_float_safe/1, t_list_to_float_risky/1,
- t_round/1, t_trunc/1]).
+%% integer_to_binary/1
+%% integer_to_binary/2
+%% binary_to_integer/1
+
+-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2, t_abs/1, t_float/1,
+ t_float_to_string/1, t_integer_to_string/1,
+ t_string_to_integer/1,
+ t_string_to_float_safe/1, t_string_to_float_risky/1,
+ t_round/1, t_trunc/1
+ ]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [t_abs, t_float, t_float_to_list, t_integer_to_list,
- {group, t_list_to_float}, t_list_to_integer, t_round,
+ [t_abs, t_float, t_float_to_string, t_integer_to_string,
+ {group, t_string_to_float}, t_string_to_integer, t_round,
t_trunc].
groups() ->
- [{t_list_to_float, [],
- [t_list_to_float_safe, t_list_to_float_risky]}].
+ [{t_string_to_float, [],
+ [t_string_to_float_safe, t_string_to_float_risky]}].
init_per_suite(Config) ->
Config.
@@ -64,225 +69,382 @@ end_per_group(_GroupName, Config) ->
t_abs(Config) when is_list(Config) ->
%% Floats.
- ?line 5.5 = abs(id(5.5)),
- ?line 0.0 = abs(id(0.0)),
- ?line 100.0 = abs(id(-100.0)),
+ 5.5 = abs(id(5.5)),
+ 0.0 = abs(id(0.0)),
+ 100.0 = abs(id(-100.0)),
%% Integers.
- ?line 5 = abs(id(5)),
- ?line 0 = abs(id(0)),
- ?line 100 = abs(id(-100)),
+ 5 = abs(id(5)),
+ 0 = abs(id(0)),
+ 100 = abs(id(-100)),
%% The largest smallnum. OTP-3190.
- ?line X = id((1 bsl 27) - 1),
- ?line X = abs(X),
- ?line X = abs(X-1)+1,
- ?line X = abs(X+1)-1,
- ?line X = abs(-X),
- ?line X = abs(-X-1)-1,
- ?line X = abs(-X+1)+1,
+ X = id((1 bsl 27) - 1),
+ X = abs(X),
+ X = abs(X-1)+1,
+ X = abs(X+1)-1,
+ X = abs(-X),
+ X = abs(-X-1)-1,
+ X = abs(-X+1)+1,
%% Bignums.
BigNum = id(13984792374983749),
- ?line BigNum = abs(BigNum),
- ?line BigNum = abs(-BigNum),
+ BigNum = abs(BigNum),
+ BigNum = abs(-BigNum),
ok.
t_float(Config) when is_list(Config) ->
- ?line 0.0 = float(id(0)),
- ?line 2.5 = float(id(2.5)),
- ?line 0.0 = float(id(0.0)),
- ?line -100.55 = float(id(-100.55)),
- ?line 42.0 = float(id(42)),
- ?line -100.0 = float(id(-100)),
+ 0.0 = float(id(0)),
+ 2.5 = float(id(2.5)),
+ 0.0 = float(id(0.0)),
+ -100.55 = float(id(-100.55)),
+ 42.0 = float(id(42)),
+ -100.0 = float(id(-100)),
%% Bignums.
- ?line 4294967305.0 = float(id(4294967305)),
- ?line -4294967305.0 = float(id(-4294967305)),
+ 4294967305.0 = float(id(4294967305)),
+ -4294967305.0 = float(id(-4294967305)),
%% Extremly big bignums.
- ?line Big = id(list_to_integer(id(lists:duplicate(2000, $1)))),
- ?line {'EXIT', {badarg, _}} = (catch float(Big)),
-
- %% Invalid types and lists.
- ?line {'EXIT', {badarg, _}} = (catch list_to_integer(id(atom))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_integer(id(123))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_integer(id([$1,[$2]]))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_integer(id("1.2"))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_integer(id("a"))),
- ?line {'EXIT', {badarg, _}} = (catch list_to_integer(id(""))),
+ Big = id(list_to_integer(id(lists:duplicate(2000, $1)))),
+ {'EXIT', {badarg, _}} = (catch float(Big)),
+
ok.
-%% Tests float_to_list/1.
-
-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),
+%% Tests float_to_list/1, float_to_list/2, float_to_binary/1, float_to_binary/2
+
+t_float_to_string(Config) when is_list(Config) ->
+ test_fts("0.00000000000000000000e+00", 0.0),
+ test_fts("2.50000000000000000000e+01", 25.0),
+ test_fts("2.50000000000000000000e+00", 2.5),
+ test_fts("2.50000000000000000000e-01", 0.25),
+ test_fts("-3.50000000000000000000e+17", -350.0e15),
+ test_fts("1.00000000000000000000e+00",1.0),
+ test_fts("1.00000000000000000000e+00",1.0, []),
+ test_fts("-1.00000000000000000000e+00",-1.0, []),
+ test_fts("-1.00000000000000000000",-1.0, [{decimals, 20}]),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, -1}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, 254}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{scientific, 250}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0e+300, [{decimals, 1}])),
+ {'EXIT', {badarg, _}} = (catch float_to_binary(1.0, [{decimals, -1}])),
+ {'EXIT', {badarg, _}} = (catch float_to_binary(1.0, [{decimals, 254}])),
+ {'EXIT', {badarg, _}} = (catch float_to_binary(1.0, [{scientific, 250}])),
+ {'EXIT', {badarg, _}} = (catch float_to_binary(1.0e+300, [{decimals, 1}])),
+ test_fts("1.0e+300",1.0e+300, [{scientific, 1}]),
+ test_fts("1.0",1.0, [{decimals, 249}, compact]),
+ test_fts("1",1.0,[{decimals,0}]),
+ test_fts("2",1.9,[{decimals,0}]),
+ test_fts("123456789012345680.0",123456789012345678.0,
+ [{decimals, 236}, compact]),
+ {'EXIT', {badarg, _}} = (catch float_to_list(
+ 123456789012345678.0, [{decimals, 237}])),
+ {'EXIT', {badarg, _}} = (catch float_to_binary(
+ 123456789012345678.0, [{decimals, 237}])),
+ test_fts("1." ++ string:copies("0", 249) ++ "e+00",
+ 1.0, [{scientific, 249}, compact]),
+
+ X1 = float_to_list(1.0),
+ X2 = float_to_list(1.0, [{scientific, 20}]),
+ X1 = X2,
+
+ Y1 = float_to_binary(1.0),
+ Y2 = float_to_binary(1.0, [{scientific, 20}]),
+ Y1 = Y2,
+
+ test_fts("1.000e+00",1.0, [{scientific, 3}]),
+ test_fts("1.000",1.0, [{decimals, 3}]),
+ test_fts("1.0",1.0, [{decimals, 1}]),
+ test_fts("1.0",1.0, [{decimals, 3}, compact]),
+ test_fts("1.12",1.123, [{decimals, 2}]),
+ test_fts("1.123",1.123, [{decimals, 3}]),
+ test_fts("1.123",1.123, [{decimals, 3}, compact]),
+ test_fts("1.1230",1.123, [{decimals, 4}]),
+ test_fts("1.12300",1.123, [{decimals, 5}]),
+ test_fts("1.123",1.123, [{decimals, 5}, compact]),
+ test_fts("1.1234",1.1234,[{decimals, 6}, compact]),
+ test_fts("1.01",1.005, [{decimals, 2}]),
+ test_fts("-1.01",-1.005,[{decimals, 2}]),
+ test_fts("0.999",0.999, [{decimals, 3}]),
+ test_fts("-0.999",-0.999,[{decimals, 3}]),
+ test_fts("1.0",0.999, [{decimals, 2}, compact]),
+ test_fts("-1.0",-0.999,[{decimals, 2}, compact]),
+ test_fts("0.5",0.5, [{decimals, 1}]),
+ test_fts("-0.5",-0.5, [{decimals, 1}]),
+ "2.333333" = erlang:float_to_list(7/3, [{decimals, 6}, compact]),
+ "2.333333" = erlang:float_to_list(7/3, [{decimals, 6}]),
+ <<"2.333333">> = erlang:float_to_binary(7/3, [{decimals, 6}, compact]),
+ <<"2.333333">> = erlang:float_to_binary(7/3, [{decimals, 6}]),
+ test_fts("0.00000000000000000000e+00",0.0, [compact]),
+ test_fts("0.0",0.0, [{decimals, 10}, compact]),
+ test_fts("123000000000000000000.0",1.23e20, [{decimals, 10}, compact]),
+ test_fts("1.2300000000e+20",1.23e20, [{scientific, 10}, compact]),
+ test_fts("1.23000000000000000000e+20",1.23e20, []),
ok.
-test_ftl(Expect, Float) ->
- %% No ?line on the next line -- we want the line number from t_float_to_list.
- Expect = remove_zeros(lists:reverse(float_to_list(Float)), []).
-
-%% Removes any non-significant zeros in a floating point number.
-%% Example: 2.500000e+01 -> 2.5e+1
-
-remove_zeros([$+, $e|Rest], [$0, X|Result]) ->
- remove_zeros([$+, $e|Rest], [X|Result]);
-remove_zeros([$-, $e|Rest], [$0, X|Result]) ->
- remove_zeros([$-, $e|Rest], [X|Result]);
-remove_zeros([$0, $.|Rest], [$e|Result]) ->
- remove_zeros(Rest, [$., $0, $e|Result]);
-remove_zeros([$0|Rest], [$e|Result]) ->
- remove_zeros(Rest, [$e|Result]);
-remove_zeros([Char|Rest], Result) ->
- remove_zeros(Rest, [Char|Result]);
-remove_zeros([], Result) ->
- Result.
-
-%% Tests integer_to_list/1.
-
-t_integer_to_list(Config) when is_list(Config) ->
- ?line "0" = integer_to_list(id(0)),
- ?line "42" = integer_to_list(id(42)),
- ?line "-42" = integer_to_list(id(-42)),
- ?line "32768" = integer_to_list(id(32768)),
- ?line "268435455" = integer_to_list(id(268435455)),
- ?line "-268435455" = integer_to_list(id(-268435455)),
- ?line "123456932798748738738" = integer_to_list(id(123456932798748738738)),
- ?line Big_List = id(lists:duplicate(2000, id($1))),
- ?line Big = list_to_integer(Big_List),
- ?line Big_List = integer_to_list(Big),
- ok.
+test_fts(Expect, Float) ->
+ Expect = float_to_list(Float),
+ BinExpect = list_to_binary(Expect),
+ BinExpect = float_to_binary(Float).
-%% Tests list_to_float/1.
+test_fts(Expect, Float, Args) ->
+ Expect = float_to_list(Float,Args),
+ BinExpect = list_to_binary(Expect),
+ BinExpect = float_to_binary(Float,Args).
-t_list_to_float_safe(Config) when is_list(Config) ->
- ?line 0.0 = list_to_float(id("0.0")),
- ?line 0.0 = list_to_float(id("-0.0")),
- ?line 0.5 = list_to_float(id("0.5")),
- ?line -0.5 = list_to_float(id("-0.5")),
- ?line 100.0 = list_to_float(id("1.0e2")),
- ?line 127.5 = list_to_float(id("127.5")),
- ?line -199.5 = list_to_float(id("-199.5")),
+%% Tests list_to_float/1.
- ?line {'EXIT',{badarg,_}} = (catch list_to_float(id("0"))),
- ?line {'EXIT',{badarg,_}} = (catch list_to_float(id("0..0"))),
- ?line {'EXIT',{badarg,_}} = (catch list_to_float(id("0e12"))),
- ?line {'EXIT',{badarg,_}} = (catch list_to_float(id("--0.0"))),
+t_string_to_float_safe(Config) when is_list(Config) ->
+ test_stf(0.0,"0.0"),
+ test_stf(0.0,"-0.0"),
+ test_stf(0.5,"0.5"),
+ test_stf(-0.5,"-0.5"),
+ test_stf(100.0,"1.0e2"),
+ test_stf(127.5,"127.5"),
+ test_stf(-199.5,"-199.5"),
+
+ {'EXIT',{badarg,_}} = (catch list_to_float(id("0"))),
+ {'EXIT',{badarg,_}} = (catch list_to_float(id("0..0"))),
+ {'EXIT',{badarg,_}} = (catch list_to_float(id("0e12"))),
+ {'EXIT',{badarg,_}} = (catch list_to_float(id("--0.0"))),
+ {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"0">>))),
+ {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"0..0">>))),
+ {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"0e12">>))),
+ {'EXIT',{badarg,_}} = (catch binary_to_float(id(<<"--0.0">>))),
+
+ UBin = <<0:3,(id(<<"0.0">>))/binary,0:5>>,
+ <<_:3,UnAlignedBin:3/binary,0:5>> = id(UBin),
+ 0.0 = binary_to_float(UnAlignedBin),
+
+ ABin = <<0:8,(id(<<"1.0">>))/binary,0:8>>,
+ <<_:8,AlignedBin:3/binary,0:8>> = id(ABin),
+ 1.0 = binary_to_float(AlignedBin),
ok.
%% This might crash the emulator...
%% (Known to crash the Unix version of Erlang 4.4.1)
-t_list_to_float_risky(Config) when is_list(Config) ->
- ?line Many_Ones = lists:duplicate(25000, id($1)),
- ?line id(list_to_float("2."++Many_Ones)),
- ?line {'EXIT', {badarg, _}} = (catch list_to_float("2"++Many_Ones)),
- ok.
-
-%% Tests list_to_integer/1.
+t_string_to_float_risky(Config) when is_list(Config) ->
+ Many_Ones = lists:duplicate(25000, id($1)),
+ id(list_to_float("2."++Many_Ones)),
+ {'EXIT', {badarg, _}} = (catch list_to_float("2"++Many_Ones)),
-t_list_to_integer(Config) when is_list(Config) ->
- ?line 0 = list_to_integer(id("0")),
- ?line 0 = list_to_integer(id("00")),
- ?line 0 = list_to_integer(id("-0")),
- ?line 1 = list_to_integer(id("1")),
- ?line -1 = list_to_integer(id("-1")),
- ?line 42 = list_to_integer(id("42")),
- ?line -12 = list_to_integer(id("-12")),
- ?line 32768 = list_to_integer(id("32768")),
- ?line 268435455 = list_to_integer(id("268435455")),
- ?line -268435455 = list_to_integer(id("-268435455")),
-
- %% Bignums.
- ?line 123456932798748738738 = list_to_integer(id("123456932798748738738")),
- ?line id(list_to_integer(lists:duplicate(2000, id($1)))),
+ id(binary_to_float(list_to_binary("2."++Many_Ones))),
+ {'EXIT', {badarg, _}} = (catch binary_to_float(
+ list_to_binary("2"++Many_Ones))),
ok.
+test_stf(Expect,List) ->
+ Expect = list_to_float(List),
+ Bin = list_to_binary(List),
+ Expect = binary_to_float(Bin).
+
%% Tests round/1.
t_round(Config) when is_list(Config) ->
- ?line 0 = round(id(0.0)),
- ?line 0 = round(id(0.4)),
- ?line 1 = round(id(0.5)),
- ?line 0 = round(id(-0.4)),
- ?line -1 = round(id(-0.5)),
- ?line 255 = round(id(255.3)),
- ?line 256 = round(id(255.6)),
- ?line -1033 = round(id(-1033.3)),
- ?line -1034 = round(id(-1033.6)),
+ 0 = round(id(0.0)),
+ 0 = round(id(0.4)),
+ 1 = round(id(0.5)),
+ 0 = round(id(-0.4)),
+ -1 = round(id(-0.5)),
+ 255 = round(id(255.3)),
+ 256 = round(id(255.6)),
+ -1033 = round(id(-1033.3)),
+ -1034 = round(id(-1033.6)),
% OTP-3722:
- ?line X = id((1 bsl 27) - 1),
- ?line MX = -X,
- ?line MXm1 = -X-1,
- ?line MXp1 = -X+1,
- ?line F = id(X + 0.0),
- ?line X = round(F),
- ?line X = round(F+1)-1,
- ?line X = round(F-1)+1,
- ?line MX = round(-F),
- ?line MXm1 = round(-F-1),
- ?line MXp1 = round(-F+1),
-
- ?line X = round(F+0.1),
- ?line X = round(F+1+0.1)-1,
- ?line X = round(F-1+0.1)+1,
- ?line MX = round(-F+0.1),
- ?line MXm1 = round(-F-1+0.1),
- ?line MXp1 = round(-F+1+0.1),
-
- ?line X = round(F-0.1),
- ?line X = round(F+1-0.1)-1,
- ?line X = round(F-1-0.1)+1,
- ?line MX = round(-F-0.1),
- ?line MXm1 = round(-F-1-0.1),
- ?line MXp1 = round(-F+1-0.1),
-
- ?line 0.5 = abs(round(F+0.5)-(F+0.5)),
- ?line 0.5 = abs(round(F-0.5)-(F-0.5)),
- ?line 0.5 = abs(round(-F-0.5)-(-F-0.5)),
- ?line 0.5 = abs(round(-F+0.5)-(-F+0.5)),
+ X = id((1 bsl 27) - 1),
+ MX = -X,
+ MXm1 = -X-1,
+ MXp1 = -X+1,
+ F = id(X + 0.0),
+ X = round(F),
+ X = round(F+1)-1,
+ X = round(F-1)+1,
+ MX = round(-F),
+ MXm1 = round(-F-1),
+ MXp1 = round(-F+1),
+
+ X = round(F+0.1),
+ X = round(F+1+0.1)-1,
+ X = round(F-1+0.1)+1,
+ MX = round(-F+0.1),
+ MXm1 = round(-F-1+0.1),
+ MXp1 = round(-F+1+0.1),
+
+ X = round(F-0.1),
+ X = round(F+1-0.1)-1,
+ X = round(F-1-0.1)+1,
+ MX = round(-F-0.1),
+ MXm1 = round(-F-1-0.1),
+ MXp1 = round(-F+1-0.1),
+
+ 0.5 = abs(round(F+0.5)-(F+0.5)),
+ 0.5 = abs(round(F-0.5)-(F-0.5)),
+ 0.5 = abs(round(-F-0.5)-(-F-0.5)),
+ 0.5 = abs(round(-F+0.5)-(-F+0.5)),
%% Bignums.
- ?line 4294967296 = round(id(4294967296.1)),
- ?line 4294967297 = round(id(4294967296.9)),
- ?line -4294967296 = -round(id(4294967296.1)),
- ?line -4294967297 = -round(id(4294967296.9)),
+ 4294967296 = round(id(4294967296.1)),
+ 4294967297 = round(id(4294967296.9)),
+ -4294967296 = -round(id(4294967296.1)),
+ -4294967297 = -round(id(4294967296.9)),
ok.
t_trunc(Config) when is_list(Config) ->
- ?line 0 = trunc(id(0.0)),
- ?line 5 = trunc(id(5.3333)),
- ?line -10 = trunc(id(-10.978987)),
+ 0 = trunc(id(0.0)),
+ 5 = trunc(id(5.3333)),
+ -10 = trunc(id(-10.978987)),
% The largest smallnum, converted to float (OTP-3722):
- ?line X = id((1 bsl 27) - 1),
- ?line F = id(X + 0.0),
+ X = id((1 bsl 27) - 1),
+ F = id(X + 0.0),
io:format("X = ~p/~w/~w, F = ~p/~w/~w, trunc(F) = ~p/~w/~w~n",
[X, X, binary_to_list(term_to_binary(X)),
F, F, binary_to_list(term_to_binary(F)),
trunc(F), trunc(F), binary_to_list(term_to_binary(trunc(F)))]),
- ?line X = trunc(F),
- ?line X = trunc(F+1)-1,
- ?line X = trunc(F-1)+1,
- ?line X = -trunc(-F),
- ?line X = -trunc(-F-1)-1,
- ?line X = -trunc(-F+1)+1,
+ X = trunc(F),
+ X = trunc(F+1)-1,
+ X = trunc(F-1)+1,
+ X = -trunc(-F),
+ X = -trunc(-F-1)-1,
+ X = -trunc(-F+1)+1,
%% Bignums.
- ?line 4294967305 = trunc(id(4294967305.7)),
- ?line -4294967305 = trunc(id(-4294967305.7)),
+ 4294967305 = trunc(id(4294967305.7)),
+ -4294967305 = trunc(id(-4294967305.7)),
ok.
+
+%% Tests integer_to_binary/1.
+
+t_integer_to_string(Config) when is_list(Config) ->
+ test_its("0",0),
+ test_its("42",42),
+ test_its("-42",-42),
+ test_its("32768",32768),
+ test_its("268435455",268435455),
+ test_its("-268435455",-268435455),
+ test_its("123456932798748738738",123456932798748738738),
+
+ %% 1 bsl 33, just beyond 32 bit
+ test_its("8589934592",8589934592),
+ test_its("-8589934592",-8589934592),
+ %% 1 bsl 65, just beyond 64 bit
+ test_its("36893488147419103232",36893488147419103232),
+ test_its("-36893488147419103232",-36893488147419103232),
+
+ %% Bignums.
+ BigBin = id(list_to_binary(lists:duplicate(2000, id($1)))),
+ Big = erlang:binary_to_integer(BigBin),
+ BigBin = erlang:integer_to_binary(Big),
+
+ %% Invalid types
+ lists:foreach(fun(Value) ->
+ {'EXIT', {badarg, _}} =
+ (catch erlang:integer_to_binary(Value)),
+ {'EXIT', {badarg, _}} =
+ (catch erlang:integer_to_list(Value))
+ end,[atom,1.2,0.0,[$1,[$2]]]),
+
+ ok.
+
+test_its(List,Int) ->
+ Int = list_to_integer(List),
+ Int = binary_to_integer(list_to_binary(List)).
+
+%% Tests binary_to_integer/1.
+
+t_string_to_integer(Config) when is_list(Config) ->
+ 0 = erlang:binary_to_integer(id(<<"00">>)),
+ 0 = erlang:binary_to_integer(id(<<"-0">>)),
+ 0 = erlang:binary_to_integer(id(<<"+0">>)),
+
+ test_sti(0),
+ test_sti(1),
+ test_sti(-1),
+ test_sti(42),
+ test_sti(-12),
+ test_sti(32768),
+ test_sti(268435455),
+ test_sti(-268435455),
+
+ %% 1 bsl 28 - 1, just before 32 bit bignum
+ test_sti(1 bsl 28 - 1),
+ %% 1 bsl 28, just beyond 32 bit small
+ test_sti(1 bsl 28),
+ %% 1 bsl 33, just beyond 32 bit
+ test_sti(1 bsl 33),
+ %% 1 bsl 60 - 1, just before 64 bit bignum
+ test_sti(1 bsl 60 - 1),
+ %% 1 bsl 60, just beyond 64 bit small
+ test_sti(1 bsl 60),
+ %% 1 bsl 65, just beyond 64 bit
+ test_sti(1 bsl 65),
+ %% Bignums.
+ test_sti(123456932798748738738,16),
+ test_sti(list_to_integer(lists:duplicate(2000, $1))),
+
+ %% unalign string
+ Str = <<"10">>,
+ UnalignStr = <<0:3, (id(Str))/binary, 0:5>>,
+ <<_:3, SomeStr:2/binary, _:5>> = id(UnalignStr),
+ 10 = erlang:binary_to_integer(SomeStr),
+
+ %% Invalid types
+ lists:foreach(fun(Value) ->
+ {'EXIT', {badarg, _}} =
+ (catch binary_to_integer(Value)),
+ {'EXIT', {badarg, _}} =
+ (catch erlang:list_to_integer(Value))
+ end,[atom,1.2,0.0,[$1,[$2]]]),
+
+ % Default base error cases
+ lists:foreach(fun(Value) ->
+ {'EXIT', {badarg, _}} =
+ (catch erlang:binary_to_integer(
+ list_to_binary(Value))),
+ {'EXIT', {badarg, _}} =
+ (catch erlang:list_to_integer(Value))
+ end,["1.0"," 1"," -1",""]),
+
+ % Custom base error cases
+ lists:foreach(fun({Value,Base}) ->
+ {'EXIT', {badarg, _}} =
+ (catch binary_to_integer(
+ list_to_binary(Value),Base)),
+ {'EXIT', {badarg, _}} =
+ (catch erlang:list_to_integer(Value,Base))
+ end,[{" 1",1},{" 1",37},{"2",2},{"C",11},
+ {"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111z",16},
+ {"1z111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",16},
+ {"111z11111111",16}]),
+
+ ok.
+
+test_sti(Num) ->
+ [begin
+ io:format("Testing ~p:~p",[Num,Base]),
+ test_sti(Num,Base)
+ end|| Base <- lists:seq(2,36)].
+
+test_sti(Num,Base) ->
+ Num = list_to_integer(int2list(Num,Base),Base),
+ Num = -1*list_to_integer(int2list(Num*-1,Base),Base),
+ Num = binary_to_integer(int2bin(Num,Base),Base),
+ Num = -1*binary_to_integer(int2bin(Num*-1,Base),Base).
+
% Calling this function (which is not supposed to be inlined) prevents
% the compiler from calculating the answer, so we don't test the compiler
% instead of the newest runtime system.
id(X) -> X.
+
+%% Uses the printing library to to integer_to_binary conversions.
+int2bin(Int,Base) when Base < 37 ->
+ iolist_to_binary(int2list(Int,Base)).
+
+int2list(Int,Base) when Base < 37 ->
+ lists:flatten(io_lib:format("~."++integer_to_list(Base)++"B",[Int])).
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 9fa4df6373..13aa0f4c00 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -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..ea91546b0c 100644
--- a/erts/emulator/test/port_SUITE_data/dead_port.c
+++ b/erts/emulator/test/port_SUITE_data/dead_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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_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..863cd2d654 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -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
@@ -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..ddf8b2d919 100644
--- a/erts/emulator/test/save_calls_SUITE.erl
+++ b/erts/emulator/test/save_calls_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index 7ff7449ff5..c28224729d 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.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
@@ -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 32e2a98e3c..1bed49aad2 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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,
@@ -80,6 +81,7 @@ config(priv_dir,_) ->
exception_meta_nocatch/1, exception_meta_nocatch_apply/1,
exception_meta_nocatch_function/1,
exception_meta_nocatch_apply_function/1,
+ concurrency/1,
init_per_testcase/2, end_per_testcase/2]).
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(test_server:minutes(2)),
@@ -89,14 +91,23 @@ end_per_testcase(_Case, Config) ->
shutdown(),
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
- ok.
+
+ %% Reloading the module will clear all trace patterns, and
+ %% in a debug-compiled emulator run assertions of the counters
+ %% for the number of functions with breakpoints.
+
+ c:l(?MODULE).
+
+
+
suite() -> [{ct_hooks,[ts_install_cth]}].
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,
@@ -106,7 +117,8 @@ all() ->
exception_meta_apply_function, exception_meta_nocatch,
exception_meta_nocatch_apply,
exception_meta_nocatch_function,
- exception_meta_nocatch_apply_function]
+ exception_meta_nocatch_apply_function,
+ concurrency]
end.
groups() ->
@@ -350,7 +362,8 @@ same(A, B) ->
basic_test() ->
?line setup([call]),
- ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ NumMatches = erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
?line ?CT(?MODULE,exported_wrap,[1]),
@@ -572,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]),
@@ -703,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,[]]},
@@ -813,6 +931,42 @@ clean_location({crash,{Reason,Stk0}}) ->
{crash,{Reason,Stk}};
clean_location(Term) -> Term.
+concurrency(_Config) ->
+ N = erlang:system_info(schedulers),
+
+ %% Spawn 2*N processes that spin in a tight infinite loop,
+ %% and one process that will turn on and off local call
+ %% trace on the infinite_loop/0 function. We expect the
+ %% emulator to crash if there is a memory barrier bug or
+ %% if an aligned word-sized write is not atomic.
+
+ Ps0 = [spawn_monitor(fun() -> infinite_loop() end) ||
+ _ <- lists:seq(1, 2*N)],
+ OnAndOff = fun() -> concurrency_on_and_off() end,
+ Ps1 = [spawn_monitor(OnAndOff)|Ps0],
+ ?t:sleep(1000),
+
+ %% Now spawn off N more processes that turn on off and off
+ %% a local trace pattern.
+ Ps = [spawn_monitor(OnAndOff) || _ <- lists:seq(1, N)] ++ Ps1,
+ ?t:sleep(1000),
+
+ %% Clean up.
+ [exit(Pid, kill) || {Pid,_} <- Ps],
+ [receive
+ {'DOWN',Ref,process,Pid,killed} -> ok
+ end || {Pid,Ref} <- Ps],
+ erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]),
+ ok.
+
+concurrency_on_and_off() ->
+ 1 = erlang:trace_pattern({?MODULE,infinite_loop,0}, true, [local]),
+ 1 = erlang:trace_pattern({?MODULE,infinite_loop,0}, false, [local]),
+ concurrency_on_and_off().
+
+infinite_loop() ->
+ infinite_loop().
+
%%% Tracee target functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
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..46ece41096 100644
--- a/erts/emulator/test/tuple_SUITE.erl
+++ b/erts/emulator/test/tuple_SUITE.erl
@@ -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
@@ -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/gen_git_version b/erts/emulator/utils/gen_git_version
new file mode 100755
index 0000000000..ef06a4b8e2
--- /dev/null
+++ b/erts/emulator/utils/gen_git_version
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+OUTPUT_FILE=$1
+
+if command -v git 2>&1 >/dev/null &&
+ test -d $ERL_TOP/.git -o -f $ERL_TOP/.git
+then
+ VSN=`git describe --match "OTP_R[0-9][0-9][A-B]*" HEAD`
+ case "$VSN" in
+ OTP_R*-g*)
+ VSN=`echo $VSN | sed -e 's/.*-g\\(.*\\)/\\1/g'` ;;
+ *) VSN="na" ;;
+ esac
+else
+ VSN="na"
+fi
+
+
+# Only update the file if there has been a change to
+# the version number.
+if test -r $OUTPUT_FILE
+then
+ VC=`sed -n -e 's/^.*"\\\\"\\(.*\\)\\\\"".*/\\1/p' < $OUTPUT_FILE`
+else
+ VC=unset
+fi
+
+if test "$VSN" != "$VC"
+then
+ echo "# Automatically generated by $0 - DO NOT EDIT." > $OUTPUT_FILE
+ if test "$VSN" = "na"
+ then
+ echo "# GIT_VSN=-DERLANG_GIT_VERSION=\"\\\"$VSN\\\"\"" >> $OUTPUT_FILE
+ else
+ echo "GIT_VSN=-DERLANG_GIT_VERSION=\"\\\"$VSN\\\"\"" >> $OUTPUT_FILE
+ fi
+ exit 0
+fi
+exit 1 \ No newline at end of file
diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables
index 91efb4c023..597a201e5a 100755
--- a/erts/emulator/utils/make_tables
+++ b/erts/emulator/utils/make_tables
@@ -2,7 +2,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2010. 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
@@ -167,7 +167,6 @@ typedef struct bif_entry {
extern BifEntry bif_table[];
extern Export* bif_export[];
-extern unsigned char erts_bif_trace_flags[];
#define BIF_SIZE $bif_size
@@ -197,7 +196,6 @@ includes("export.h", "sys.h", "erl_vm.h", "erl_process.h", "bif.h",
"erl_bif_table.h", "erl_atom_table.h");
print "\nExport* bif_export[BIF_SIZE];\n";
-print "unsigned char erts_bif_trace_flags[BIF_SIZE];\n\n";
print "BifEntry bif_table[] = {\n";
for ($i = 0; $i < @bif; $i++) {
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..94bb74c876 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -2,7 +2,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
@@ -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..8817bde8d7 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -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
@@ -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..ac354dcc78 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -2,7 +2,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
@@ -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..90df7cc25a 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -2,7 +2,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
@@ -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/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index fd9969ae2b..cc24a556a3 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2012. 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
@@ -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 83fe97df8e..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
@@ -80,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)
@@ -113,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
@@ -210,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=
@@ -235,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:
@@ -301,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
@@ -332,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
@@ -399,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 \
+ $(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/ct_run.c b/erts/etc/common/ct_run.c
index 7aaab716f7..5e5b612a12 100644
--- a/erts/etc/common/ct_run.c
+++ b/erts/etc/common/ct_run.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010. 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
@@ -85,7 +85,6 @@ static char* strsave(char* string);
static void push_words(char* src);
static int run_erlang(char* name, char** argv);
static char* get_default_emulator(char* progname);
-static void print_deprecation_warning(char *progname);
#ifdef __WIN32__
static char* possibly_quote(char* arg);
#endif
@@ -132,8 +131,6 @@ main(int argc, char** argv)
int erl_args;
char** argv0 = argv;
- print_deprecation_warning(argv[0]);
-
emulator = get_default_emulator(argv[0]);
/*
@@ -447,15 +444,6 @@ static char *simple_basename(char *path)
return path;
}
-static void print_deprecation_warning(char* progpath)
-{
- char *basename = simple_basename(progpath);
- if(strcmp(basename,"run_test") == 0 ||
- strcmp(basename, "run_test.exe") == 0) {
- printf("---***---\nDeprecated: run_test is deprecated and will be removed in R16B,\n please use ct_run instead\n---***---\n");
- }
-}
-
static char*
get_default_emulator(char* progname)
{
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
index f63ba3ee64..c2d7c7c76d 100644
--- a/erts/etc/common/erlc.c
+++ b/erts/etc/common/erlc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2012. 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
@@ -186,6 +186,7 @@ main(int argc, char** argv)
*/
PUSH("+sbtu");
+ PUSH("+A0");
PUSH("-noinput");
PUSH2("-mode", "minimal");
PUSH2("-boot", "start_clean");
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 52add1c1ba..9d674a7c65 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -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
@@ -124,9 +124,11 @@ static char *pluss_val_switches[] = {
"bwt",
"cl",
"ct",
+ "tbt",
"wt",
"ws",
"ss",
+ "pp",
NULL
};
/* +h arguments with values */
@@ -182,7 +184,6 @@ void error(char* format, ...);
#if !defined(ERTS_HAVE_SMP_EMU)
static void usage_notsup(const char *switchname);
#endif
-static void usage_msg(const char *msg);
static char **build_args_from_env(char *env_var);
static char **build_args_from_string(char *env_var);
static void initial_argv_massage(int *argc, char ***argv);
@@ -799,7 +800,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':
@@ -909,6 +912,16 @@ int main(int argc, char **argv)
i++;
}
break;
+ case 'p':
+ if (argv[i][2] != 'c' || argv[i][3] != '\0')
+ goto the_default;
+ if (i+1 >= argc)
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ break;
case 'z':
if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) {
goto the_default;
@@ -989,8 +1002,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);
}
@@ -1104,7 +1116,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");
@@ -1128,13 +1141,6 @@ usage_notsup(const char *switchname)
#endif
static void
-usage_msg(const char *msg)
-{
- fprintf(stderr, "%s\n", msg);
- usage_aux();
-}
-
-static void
usage_format(char *format, ...)
{
va_list args;
diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c
index 9e80ec6656..118bc6ef90 100644
--- a/erts/etc/common/escript.c
+++ b/erts/etc/common/escript.c
@@ -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
@@ -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 ed75a8f256..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,9 +86,6 @@
#include <fcntl.h>
#include <process.h>
#endif
-#ifdef VXWORKS
-#include "sys.h"
-#endif
/*
* Implement time correction using times() call even on Linuxes
@@ -113,19 +103,7 @@
#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>
@@ -559,8 +537,7 @@ kill_old_erlang(void){
CloseHandle(erlh);
}
}
-#elif !defined(VXWORKS)
-/* Unix eh? */
+#else
static void
kill_old_erlang(void){
pid_t pid;
@@ -579,7 +556,7 @@ kill_old_erlang(void){
}
}
}
-#endif /* Not on VxWorks */
+#endif
#ifdef __WIN32__
void win_system(char *command)
@@ -1094,59 +1071,6 @@ 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);
- }
-}
-
-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);
- }
-}
-
-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);
-}
-
-time_t timestamp(time_t *res)
-{
- time_t r;
- ++lock_time;
- r = (time_t) elapsed;
- --lock_time;
- if (res != NULL)
- *res = r;
- return r;
-}
-
#elif defined(HAVE_GETHRTIME) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
#if defined(GETHRTIME_WITH_CLOCK_GETTIME)
@@ -1172,7 +1096,6 @@ typedef hrtime_t SysHrTime;
#define sys_gethrtime() gethrtime()
#endif
-
void init_timestamp(void)
{
}
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
index e923233ce9..bef97862a3 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);
@@ -2562,7 +2562,8 @@ static void debugf(char *format, ...)
}
#else
/* suppress warning with 'if' */
- if(write(2,buff,strlen(buff)));
+ if(write(2,buff,strlen(buff)))
+ ;
#endif
va_end(ap);
}
@@ -2574,7 +2575,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");
@@ -2585,7 +2586,8 @@ static void warning(char *format, ...)
}
#else
/* suppress warning with 'if' */
- if(write(2,buff,strlen(buff)));
+ if(write(2,buff,strlen(buff)))
+ ;
#endif
va_end(ap);
}
@@ -2597,7 +2599,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");
@@ -2608,7 +2610,8 @@ static void fatal(char *format, ...)
}
#else
/* suppress warning with 'if' */
- if(write(2,buff,strlen(buff)));
+ if(write(2,buff,strlen(buff)))
+ ;
#endif
va_end(ap);
#ifndef WIN32
diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src
index 2dcd070a6d..0f33258a28 100644
--- a/erts/etc/unix/Install.src
+++ b/erts/etc/unix/Install.src
@@ -2,7 +2,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
@@ -92,9 +92,6 @@ cp -p "$ERL_ROOT/erts-%I_VSN%/bin/typer" .
cp -p "$ERL_ROOT/erts-%I_VSN%/bin/ct_run" .
cp -p "$ERL_ROOT/erts-%I_VSN%/bin/escript" .
-# Remove in R16B
-ln -s ct_run run_test
-
#
# Set a soft link to epmd
# This should not be done for an embedded system!
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index e0d7404de7..cc7d77fd9a 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -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=1 "
+ 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/etp-commands b/erts/etc/unix/etp-commands
index 1c886620bb..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
@@ -1022,16 +1036,17 @@ define etp-cp-1
# Non-reentrant
#
set $etp_cp = (Eterm)($arg0)
- set $etp_cp_low = modules
- set $etp_cp_high = $etp_cp_low + num_loaded_modules
- set $etp_cp_mid = mid_module
+ set $etp_ranges = &r[(int)the_active_code_index]
+ set $etp_cp_low = $etp_ranges->modules
+ set $etp_cp_high = $etp_cp_low + $etp_ranges->n
+ set $etp_cp_mid = (Range*)$etp_ranges->mid
set $etp_cp_p = 0
#
while $etp_cp_low < $etp_cp_high
if $etp_cp < $etp_cp_mid->start
set $etp_cp_high = $etp_cp_mid
else
- if $etp_cp > $etp_cp_mid->end
+ if $etp_cp > (BeamInstr*)$etp_cp_mid->end
set $etp_cp_low = $etp_cp_mid + 1
else
set $etp_cp_p = $etp_cp_low = $etp_cp_high = $etp_cp_mid
@@ -1263,7 +1278,802 @@ 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.r.o.tab[((int) $arg0)])
+ printf "(Process *) %p\n", $proc
+end
+
+define etp-pid2proc-1
+# Args: Eterm
+#
+ etp-pid2pix-1 $arg0
+ set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$etp_pix])
+end
+
+define etp-pid2proc
+# Args: Eterm
+#
+ etp-pid2proc-1 $arg0
+ printf "(Process *) %p\n", $proc
+end
+
+define etp-proc-state-int
+# Args: int
+#
+ if ($arg0 & 0xfffff000)
+ printf "GARBAGE | "
+ end
+ if ($arg0 & 0x800)
+ printf "trapping-exit | "
+ end
+ if ($arg0 & 0x400)
+ printf "bound | "
+ end
+ if ($arg0 & 0x200)
+ printf "garbage-collecting | "
+ end
+ if ($arg0 & 0x100)
+ printf "suspended | "
+ end
+ if ($arg0 & 0x80)
+ printf "running | "
+ end
+ if ($arg0 & 0x40)
+ printf "in-run-queue | "
+ end
+ if ($arg0 & 0x20)
+ printf "active | "
+ end
+ if ($arg0 & 0x10)
+ printf "pending-exit | "
+ end
+ if ($arg0 & 0x8)
+ printf "exiting | "
+ end
+ if ($arg0 & 0x4)
+ printf "free | "
+ end
+ if ($arg0 & 0x3) == 0
+ printf "prio-max\n"
+ else
+ if ($arg0 & 0x3) == 1
+ printf "prio-high\n"
+ else
+ if ($arg0 & 0x3) == 2
+ printf "prio-normal\n"
+ else
+ printf "prio-low\n"
+ end
+ end
+ end
+end
+
+document etp-proc-state-int
+%---------------------------------------------------------------------------
+% etp-proc-state-int int
+%
+% Print state of process state value
+%---------------------------------------------------------------------------
+end
+
+
+define etp-proc-state
+# Args: Process*
+#
+ set $state_int = *(((Uint32 *) &(((Process *) $arg0)->state)))
+ etp-proc-state-int $state_int
+end
+
+document etp-proc-state
+%---------------------------------------------------------------------------
+% etp-proc-state Process*
+%
+% Print state of process
+%---------------------------------------------------------------------------
+end
+
+define etp-process-info
+# Args: Process*
+#
+ printf " Pid: "
+ etp-1 $arg0->common.id
+ printf "\n State: "
+ etp-proc-state $arg0
+ 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: "
+ etp-1 $arg0->current[0]
+ printf ":"
+ etp-1 $arg0->current[1]
+ printf "/%d\n", $arg0->current[2]
+ end
+ if ($arg0->cp)
+ printf " CP: "
+ etp-cp-1 $arg0->cp
+ printf "\n"
+ end
+ if ($arg0->i)
+ printf " I: "
+ etp-cp-1 $arg0->i
+ printf "\n"
+ end
+ printf " Heap size: %ld\n", $arg0->heap_sz
+ if ($arg0->old_heap)
+ printf " Old-heap size: %ld\n", $arg0->old_hend - $arg0->old_heap
+ end
+ printf " Mbuf size: %ld\n", $arg0->mbuf_sz
+ if (etp_smp_compiled)
+ printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len
+ else
+ printf " Msgq len: %d\n", $arg0->msg.len
+ end
+ printf " Parent: "
+ etp-1 $arg0->parent
+ printf "\n Pointer: (Process *) %p\n", $arg0
+end
+
+document etp-process-info
+%---------------------------------------------------------------------------
+% etp-process-info Process*
+%
+% Print info about process
+%---------------------------------------------------------------------------
+end
+
+define etp-processes
+ if (!erts_initialized)
+ printf "No processes, since system isn't initialized!\n"
+ else
+ set $proc_ix = 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
+ end
+ set $proc_ix++
+ end
+ printf "---\n",
+ end
+end
+
+document etp-processes
+%---------------------------------------------------------------------------
+% etp-processes
+%
+% Print misc info about all 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-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)
+ printf " connected"
+ end
+ if ($arg0 & 0x2)
+ printf " exiting"
+ end
+ if ($arg0 & 0x4)
+ printf " distribution"
+ end
+ if ($arg0 & 0x8)
+ printf " binary-io"
+ end
+ if ($arg0 & 0x10)
+ printf " soft-eof"
+ end
+ if ($arg0 & 0x20)
+ printf " closing"
+ end
+ if ($arg0 & 0x40)
+ printf " send-closed"
+ end
+ if ($arg0 & 0x80)
+ printf " linebuf-io"
+ end
+ if ($arg0 & 0x100)
+ printf " free"
+ end
+ if ($arg0 & 0x200)
+ printf " initializing"
+ end
+ if ($arg0 & 0x400)
+ printf " port-specific-lock"
+ end
+ if ($arg0 & 0x800)
+ printf " invalid"
+ end
+ if ($arg0 & 0x1000)
+ printf " halt"
+ end
+ if (etp_debug_compiled)
+ if ($arg0 & 0x7fffe000)
+ printf " GARBAGE"
+ end
+ else
+ if ($arg0 & 0xffffe000)
+ printf " GARBAGE"
+ end
+ end
+ printf "\n"
+end
+
+document etp-port-state-int
+%---------------------------------------------------------------------------
+% etp-proc-state-int int
+%
+% Print port state
+%---------------------------------------------------------------------------
+end
+
+
+define etp-port-state
+# Args: Port*
+#
+ set $state_int = *(((Uint32 *) &(((Port *) $arg0)->state)))
+ etp-port-state-int $state_int
+end
+
+document etp-port-state
+%---------------------------------------------------------------------------
+% etp-proc-state-int Port *
+%
+% Print port state
+%---------------------------------------------------------------------------
+end
+
+define etp-port-info
+# Args: Port*
+#
+ printf " Port: "
+ etp-1 $arg0->common.id
+ printf "\n Name: %s\n", $arg0->name
+ 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: "
+ set $connected = *(((Eterm *) &(((Port *) $arg0)->connected)))
+ etp-1 $connected
+ printf "\n Pointer: (Port *) %p\n", $arg0
+end
+
+document etp-port-info
+%---------------------------------------------------------------------------
+% etp-port-info Port*
+%
+% Print info about port
+%---------------------------------------------------------------------------
+end
+
+
+define etp-ports
+ if (!erts_initialized)
+ printf "No ports, since system isn't initialized!\n"
+ else
+ set $port_ix = 0
+ 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
+ printf "---\n",
+ end
+end
+
+document etp-ports
+%---------------------------------------------------------------------------
+% etp-ports
+%
+% Print misc info about all ports
+%---------------------------------------------------------------------------
+end
+
+define etp-rq-flags-int
+# Args: int
+#
+ if ($arg0 & 0x1f)
+ printf " Queue Mask:"
+ if ($arg0 & 0x1)
+ printf " max"
+ end
+ if ($arg0 & 0x2)
+ printf " high"
+ end
+ if ($arg0 & 0x4)
+ printf " normal"
+ end
+ if ($arg0 & 0x8)
+ printf " low"
+ end
+ if ($arg0 & 0x10)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & 0x3fe0)
+ printf " Emigrate Mask:"
+ if ($arg0 & 0x20)
+ printf " max"
+ end
+ if ($arg0 & 0x40)
+ printf " high"
+ end
+ if ($arg0 & 0x80)
+ printf " normal"
+ end
+ if ($arg0 & 0x100)
+ printf " low"
+ end
+ if ($arg0 & 0x200)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & 0x7fc00)
+ printf " Immigrate Mask:"
+ if ($arg0 & 0x400)
+ printf " max"
+ end
+ if ($arg0 & 0x800)
+ printf " high"
+ end
+ if ($arg0 & 0x1000)
+ printf " normal"
+ end
+ if ($arg0 & 0x2000)
+ printf " low"
+ end
+ if ($arg0 & 0x4000)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & 0xf8000)
+ printf " Evaquate Mask:"
+ if ($arg0 & 0x8000)
+ printf " max"
+ end
+ if ($arg0 & 0x10000)
+ printf " high"
+ end
+ if ($arg0 & 0x20000)
+ printf " normal"
+ end
+ if ($arg0 & 0x40000)
+ printf " low"
+ end
+ if ($arg0 & 0x80000)
+ printf " ports"
+ end
+ printf "\n"
+ end
+
+ if ($arg0 & ~0xfffff)
+ printf " Misc Flags:"
+ if ($arg0 & 0x100000)
+ printf " out-of-work"
+ end
+ if ($arg0 & 0x200000)
+ printf " halftime-out-of-work"
+ end
+ if ($arg0 & 0x400000)
+ printf " suspended"
+ end
+ if ($arg0 & 0x800000)
+ printf " check-cpu-bind"
+ end
+ if ($arg0 & 0x1000000)
+ printf " inactive"
+ end
+ if ($arg0 & 0x2000000)
+ printf " non-empty"
+ end
+ if ($arg0 & 0x4000000)
+ printf " protected"
+ end
+ if ($arg0 & ~0x7ffffff)
+ printf " GARBAGE(0x%x)", ($arg0 & ~0x3ffffff)
+ end
+ printf "\n"
+ end
+end
+
+document etp-rq-flags-int
+%---------------------------------------------------------------------------
+% etp-rq-flags-int
+%
+% Print run queue flags
+%---------------------------------------------------------------------------
+end
+
+define etp-ssi-flags
+# Args: int
+#
+ if ($arg0 & 0x1)
+ printf " sleeping"
+ end
+ if ($arg0 & 0x2)
+ printf " poll"
+ end
+ if ($arg0 & 0x4)
+ printf " tse"
+ end
+ if ($arg0 & 0x8)
+ printf " waiting"
+ end
+ if ($arg0 & 0x10)
+ printf " suspended"
+ end
+ printf "\n"
+end
+
+document etp-ssi-flags
+%---------------------------------------------------------------------------
+% etp-ssi-flags
+% Arg int
+%
+% Print aux work flags
+%---------------------------------------------------------------------------
+end
+
+define etp-aux-work-flags
+# Args: int
+#
+ if ($arg0 & 0x1)
+ printf " delayed-dealloc"
+ end
+ if ($arg0 & 0x2)
+ printf " delayed-dealloc-thr-prgr"
+ end
+ if ($arg0 & 0x4)
+ printf " fix-alloc-dealloc"
+ end
+ if ($arg0 & 0x8)
+ printf " fix-alloc-lower-lim"
+ end
+ if ($arg0 & 0x10)
+ printf " async-ready"
+ end
+ if ($arg0 & 0x20)
+ printf " async-ready-clean"
+ end
+ if ($arg0 & 0x40)
+ printf " misc-work-thr-prgr"
+ end
+ if ($arg0 & 0x80)
+ printf " misc-work"
+ end
+ if ($arg0 & 0x100)
+ printf " check-children"
+ end
+ if ($arg0 & 0x200)
+ printf " set-tmo"
+ end
+ if ($arg0 & 0x400)
+ printf " mseg-cached-check"
+ end
+ if ($arg0 & ~0x7ff)
+ printf " GARBAGE"
+ end
+ printf "\n"
+end
+
+document etp-aux-work-flags
+%---------------------------------------------------------------------------
+% etp-aux-work-flags
+% Arg int
+%
+% Print aux work flags
+%---------------------------------------------------------------------------
+end
+
+define etp-schedulers
+ if (!erts_initialized)
+ printf "No schedulers, since system isn't initialized!\n"
+ else
+ set $sched_ix = 0
+ while $sched_ix < erts_no_schedulers
+ printf "--- Scheduler %d ---\n", $sched_ix+1
+ printf " IX: %d\n", $sched_ix
+ if (erts_aligned_scheduler_data[$sched_ix].esd.cpu_id < 0)
+ printf " CPU Binding: unbound\n"
+ else
+ printf " CPU Binding: %d\n", erts_aligned_scheduler_data[$sched_ix].esd.cpu_id
+ end
+ printf " Aux work Flags:"
+ set $aux_work_flags = *((Uint32 *) &erts_aligned_scheduler_data[$sched_ix].esd.ssi->aux_work)
+ etp-aux-work-flags $aux_work_flags
+ printf " Sleep Info Flags:"
+ set $ssi_flags = *((Uint32 *) &erts_aligned_scheduler_data[$sched_ix].esd.ssi->flags)
+ etp-ssi-flags $ssi_flags
+ printf " Pointer: (ErtsSchedulerData *) %p\n", &erts_aligned_scheduler_data[$sched_ix].esd
+ printf " - Run Queue -\n"
+ if (etp_smp_compiled)
+ set $runq = erts_aligned_scheduler_data[$sched_ix].esd.run_queue
+ else
+ set $runq = &erts_aligned_run_queues[0].runq
+ end
+ printf " Length: total=%d", *((Uint32 *) &($runq->len))
+ printf ", max=%d", *((Uint32 *) &($runq->procs.prio_info[0].len))
+ printf ", high=%d", *((Uint32 *) &($runq->procs.prio_info[1].len))
+ printf ", normal=%d", *((Uint32 *) &($runq->procs.prio_info[2].len))
+ printf ", low=%d", *((Uint32 *) &($runq->procs.prio_info[3].len))
+ printf ", port=%d\n", *((Uint32 *) &($runq->ports.info.len))
+ if ($runq->misc.start)
+ printf " Misc Jobs: yes\n"
+ else
+ printf " Misc Jobs: no\n"
+ end
+ set $rq_flags = *((Uint32 *) &($runq->flags))
+ etp-rq-flags-int $rq_flags
+ printf " Pointer: (ErtsRunQueue *) %p\n", $runq
+
+ set $sched_ix++
+ end
+ printf "-------------------\n",
+ end
+end
+
+document etp-schedulers
+%---------------------------------------------------------------------------
+% etp-schedulers
+%
+% Print misc info about all schedulers
+%---------------------------------------------------------------------------
+end
+
+define etp-migration-info
+ set $minfo = (ErtsMigrationPaths *) *((UWord *) &erts_migration_paths)
+ set $rq_ix = 0
+ while $rq_ix < erts_no_run_queues
+ if ($minfo->mpath[$rq_ix])
+ printf "---\n"
+ printf "Run Queue Ix: %d\n", $rq_ix
+ etp-rq-flags-int $minfo->mpath[$rq_ix].flags
+ end
+ set $rq_ix++
+ end
+end
+
+document etp-migration-info
+%---------------------------------------------------------------------------
+% etp-migration-info
+%
+% Print migration information
+%---------------------------------------------------------------------------
+end
+
+define etp-system-info
+ printf "--------------- System Information ---------------\n"
+ printf "OTP release: %s\n", etp_otp_release
+ 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)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "HiPE support: "
+ if (etp_hipe)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ if (etp_smp_compiled)
+ printf "SMP support: yes\n"
+ else
+ printf "SMP support: no\n"
+ end
+ printf "Thread support: "
+ if (etp_thread_compiled)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "Kernel poll: "
+ if (etp_kernel_poll_support)
+ if (!erts_initialized)
+ printf "Supported\n"
+ else
+ if (erts_use_kernel_poll)
+ printf "Supported and used\n"
+ else
+ printf "Supported but not used\n"
+ end
+ end
+ else
+ printf "No support\n"
+ end
+ printf "Debug compiled: "
+ if (etp_debug_compiled)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "Lock checking: "
+ if (etp_lock_check)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+ printf "Lock counting: "
+ if (etp_lock_count)
+ printf "yes\n"
+ else
+ printf "no\n"
+ end
+
+ if (!erts_initialized)
+ printf "System not initialized\n"
+ else
+ printf "Node name: "
+ etp-1 erts_this_node->sysname
+ printf "\n"
+ printf "Number of schedulers: %d\n", erts_no_schedulers
+ printf "Number of async-threads: %d\n", erts_async_max_threads
+ end
+ printf "--------------------------------------------------\n"
+end
+
+document etp-system-info
+%---------------------------------------------------------------------------
+% etp-system-info
+%
+% Print general information about the system
+%---------------------------------------------------------------------------
+end
define etp-dictdump
# Args: ProcDict*
@@ -1407,69 +2217,6 @@ document etpf-offheapdump
%---------------------------------------------------------------------------
end
-define etp-print-procs
-# Args: Eterm
-#
-# Non-reentrant
-#
- etp-print-procs-1
-end
-
-define etp-print-procs-1
-# Args: Eterm*
-#
-# Non-reentrant
-#
- set $etp_print_procs_q = erts_max_processes / 10
- set $etp_print_procs_r = erts_max_processes % 10
- set $etp_print_procs_t = 10
- set $etp_print_procs_m = $etp_print_procs_q
- if $etp_print_procs_r > 0
- set $etp_print_procs_m++
- set $etp_print_procs_r--
- end
- set $etp_print_procs_i = 0
- set $etp_print_procs_found = 0
- while $etp_print_procs_i < erts_max_processes
- if process_tab[$etp_print_procs_i]
- printf "%d: ", $etp_print_procs_i
- etp-1 process_tab[$etp_print_procs_i]->id
- printf " "
- etp-1 ((Eterm)(process_tab[$etp_print_procs_i]->i))
- printf " heap=%d/%d(%d)", process_tab[$etp_print_procs_i]->htop - process_tab[$etp_print_procs_i]->heap, \
- process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->heap, \
- process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->stop
- printf " old=%d/%d ", process_tab[$etp_print_procs_i]->old_htop - process_tab[$etp_print_procs_i]->old_heap, \
- process_tab[$etp_print_procs_i]->old_hend - process_tab[$etp_print_procs_i]->old_heap
- printf " mbuf_sz=%d ", process_tab[$etp_print_procs_i]->mbuf_sz
- printf " min=%d ", process_tab[$etp_print_procs_i]->min_heap_size
- printf " flags=%x ", process_tab[$etp_print_procs_i]->flags
- printf " msgs=%d ", process_tab[$etp_print_procs_i]->msg.len
- printf "\n"
- end
- set $etp_print_procs_i++
- if $etp_print_procs_i > $etp_print_procs_m
- printf "%% %d%%...\n", $etp_print_procs_t
- set $etp_print_procs_t += 10
- set $etp_print_procs_m += $etp_print_procs_q
- if $etp_print_procs_r > 0
- set $etp_print_procs_m++
- set $etp_print_procs_r--
- end
- end
- end
- printf "%% 100%%.\n"
-end
-
-document etp-print-procs
-%---------------------------------------------------------------------------
-% etp-print-procs Eterm
-%
-% Print some information about ALL processes.
-%---------------------------------------------------------------------------
-end
-
-
define etp-search-heaps
# Args: Eterm
#
@@ -1498,20 +2245,21 @@ define etp-search-heaps-1
end
set $etp_search_heaps_i = 0
set $etp_search_heaps_found = 0
- while $etp_search_heaps_i < erts_max_processes
- if process_tab[$etp_search_heaps_i]
- if (process_tab[$etp_search_heaps_i]->heap <= ($arg0)) && \
- (($arg0) < process_tab[$etp_search_heaps_i]->hend)
+ 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)
printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \
- ($arg0)-process_tab[$etp_search_heaps_i]->heap
+ ($arg0)-$proc->heap
end
- if (process_tab[$etp_search_heaps_i]->old_heap <= ($arg0)) && \
- (($arg0) <= process_tab[$etp_search_heaps_i]->old_hend)
+ if ($proc->old_heap <= ($arg0)) && \
+ (($arg0) <= $proc->old_hend)
printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \
- ($arg0)-process_tab[$etp_search_heaps_i]->old_heap
+ ($arg0)-$proc->old_heap
end
set $etp_search_heaps_cnt = 0
- set $etp_search_heaps_p = process_tab[$etp_search_heaps_i]->mbuf
+ set $etp_search_heaps_p = $proc->mbuf
while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth)
set $etp_search_heaps_cnt++
if (&($etp_search_heaps_p->mem) <= ($arg0)) && \
@@ -1523,7 +2271,7 @@ define etp-search-heaps-1
set $etp_search_heaps_p = $etp_search_heaps_p->next
end
if $etp_search_heaps_p
- printf "process_tab[%d] %% Too many HeapFragments\n", \
+ printf "Process ix=%d %% Too many HeapFragments\n", \
$etp_search_heaps_i
end
end
@@ -2070,7 +2818,7 @@ document etp-init
%---------------------------------------------------------------------------
end
-
etp-init
help etp-init
etp-show
+etp-system-info
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/Install.c b/erts/etc/win32/Install.c
index d680b67dd6..dd02a9c111 100644
--- a/erts/etc/win32/Install.c
+++ b/erts/etc/win32/Install.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2011. 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
@@ -171,20 +171,6 @@ int main(int argc, char **argv)
fprintf(stderr,"Continuing installation anyway...\n");
}
}
-
- // Remove in R16B
- sprintf(fromname,"%s\\%s",bin_dir,"ct_run.exe");
- sprintf(toname,"%s\\%s",bin_dir,"run_test.exe");
- if (GetFileAttributes(fromname) == 0xFFFFFFFF) {
- fprintf(stderr,"Could not find file %s\n",
- fromname);
- exit(1);
- }
- if (!CopyFile(fromname,toname,FALSE)) {
- fprintf(stderr,"Could not copy file %s to %s\n",
- fromname,toname);
- fprintf(stderr,"Continuing installation anyway...\n");
- }
for (i = 0; scripts[i] != NULL; ++i) {
sprintf(fromname,"%s\\%s",release_dir,scripts[i]);
diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh
index 0d9ba4248d..e75edf3738 100755
--- a/erts/etc/win32/nsis/dll_version_helper.sh
+++ b/erts/etc/win32/nsis/dll_version_helper.sh
@@ -2,7 +2,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
diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h
index 064c4a5c09..62fe4120e4 100644
--- a/erts/include/internal/erl_printf_format.h
+++ b/erts/include/internal/erl_printf_format.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2005-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2005-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,6 +28,22 @@
#include <stdarg.h>
#include <stdlib.h>
+#include "erl_int_sizes_config.h"
+
+#if SIZEOF_VOID_P == SIZEOF_LONG
+typedef unsigned long ErlPfUWord;
+typedef long ErlPfSWord;
+#elif SIZEOF_VOID_P == SIZEOF_INT
+typedef unsigned int ErlPfUWord;
+typedef int ErlPfSWord;
+#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
+typedef unsigned long long ErlPfUWord;
+typedef long long ErlPfSWord;
+#else
+#error Found no appropriate type to use for 'Eterm', 'Uint' and 'Sint'
+#endif
+
+
typedef int (*fmtfn_t)(void*, char*, size_t);
extern int erts_printf_format(fmtfn_t, void*, char*, va_list);
@@ -36,11 +52,21 @@ extern int erts_printf_char(fmtfn_t, void*, char);
extern int erts_printf_string(fmtfn_t, void*, char *);
extern int erts_printf_buf(fmtfn_t, void*, char *, size_t);
extern int erts_printf_pointer(fmtfn_t, void*, void *);
-extern int erts_printf_ulong(fmtfn_t, void*, char, int, int, unsigned long);
-extern int erts_printf_slong(fmtfn_t, void*, char, int, int, signed long);
+extern int erts_printf_uword(fmtfn_t, void*, char, int, int, ErlPfUWord);
+extern int erts_printf_sword(fmtfn_t, void*, char, int, int, ErlPfSWord);
extern int erts_printf_double(fmtfn_t, void *, char, int, int, double);
-extern int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long, unsigned long*);
-
+#ifdef HALFWORD_HEAP_EMULATOR
+# if SIZEOF_INT != 4
+# error Unsupported integer size for HALFWORD_HEAP_EMULATOR
+# endif
+typedef unsigned int ErlPfEterm;
+#else
+typedef ErlPfUWord ErlPfEterm;
#endif
+extern int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*);
+
+
+#endif /* ERL_PRINTF_FORMAT_H__ */
+
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index aef31e282a..ab728c65fa 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2004-2012. 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
@@ -59,10 +59,6 @@
# undef ETHR_TRY_INLINE_FUNCS
#endif
-#if !defined(ETHR_DISABLE_NATIVE_IMPLS) && (defined(PURIFY)||defined(VALGRIND))
-# define ETHR_DISABLE_NATIVE_IMPLS
-#endif
-
/* Assume 64-byte cache line size */
#define ETHR_CACHE_LINE_SIZE 64
#define ETHR_CACHE_LINE_MASK (ETHR_CACHE_LINE_SIZE - 1)
@@ -413,7 +409,11 @@ extern ethr_runtime_t ethr_runtime__;
# endif
#endif
-#include "ethr_optimized_fallbacks.h"
+#ifdef VALGRIND /* mutex as fallback for spinlock for VALGRIND */
+# undef ETHR_HAVE_NATIVE_SPINLOCKS
+#else
+# include "ethr_optimized_fallbacks.h"
+#endif
typedef struct {
void *(*thread_create_prepare_func)(void);
diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in
index aed889eaef..0fc3ac6efc 100644
--- a/erts/lib_src/Makefile.in
+++ b/erts/lib_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. 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
@@ -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,18 +566,19 @@ 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) \
+ $(V_at)$(DEP_CC) -MM $(THR_DEFS) $(DEP_FLAGS) $(ETHREAD_LIB_SRC) \
| $(SED_r_DEPEND) >> $(DEPEND_MK)
endif
endif
@@ -579,54 +586,54 @@ 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..b32681f40e 100644
--- a/erts/lib_src/common/erl_misc_utils.c
+++ b/erts/lib_src/common/erl_misc_utils.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2006-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
@@ -124,6 +124,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__)
@@ -186,7 +192,8 @@ struct erts_cpu_info_t_ {
static __forceinline int
get_proc_affinity(erts_cpu_info_t *cpuinfo, cpu_set_t *cpuset)
{
- DWORD pamask, samask;
+ DWORD_PTR pamask;
+ DWORD_PTR samask;
if (GetProcessAffinityMask(GetCurrentProcess(), &pamask, &samask)) {
*cpuset = (cpu_set_t) pamask;
return 0;
@@ -669,6 +676,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 +767,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 fd25cce7ed..d6ae76f14c 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-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2005-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
@@ -165,7 +165,7 @@ static char heX[] = "0123456789ABCDEF";
#define SIGN(X) ((X) > 0 ? 1 : ((X) < 0 ? -1 : 0))
#define USIGN(X) ((X) == 0 ? 0 : 1)
-int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long, unsigned long*) = NULL;
+int (*erts_printf_eterm_func)(fmtfn_t, void*, ErlPfEterm, long, ErlPfEterm*) = NULL;
static int
noop_fn(void *vfp, char* buf, size_t len)
@@ -234,7 +234,7 @@ static int fmt_fld(fmtfn_t fn,void* arg,
return 0;
}
-static int fmt_long(fmtfn_t fn,void* arg,int sign,unsigned long uval,
+static int fmt_uword(fmtfn_t fn,void* arg,int sign,ErlPfUWord uval,
int width,int precision,int fmt,int* count)
{
char buf[32];
@@ -335,11 +335,11 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
int fi = 0;
char format_str[7];
char sbuf[32];
- char *bufp;
+ char *bufp = sbuf;
double dexp;
int exp;
size_t max_size = 1;
- size_t size;
+ int size;
int new_fmt = fmt;
int fpe_was_unmasked;
@@ -425,12 +425,12 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
max_size++; /* '\0' */
- if (max_size < sizeof(sbuf))
- bufp = sbuf;
- else {
+ if (max_size >= sizeof(sbuf)) {
bufp = (char *) malloc(sizeof(char)*max_size);
if (!bufp) {
res = -ENOMEM;
+ /* Make sure not to trigger free */
+ bufp = sbuf;
goto out;
}
}
@@ -448,10 +448,10 @@ static int fmt_double(fmtfn_t fn,void*arg,double val,
res = fmt_fld(fn, arg, bufp, size, 0, width, 0, new_fmt, count);
+ out:
if (bufp != sbuf)
free((void *) bufp);
- out:
if (erts_printf_unblock_fpe)
(*erts_printf_unblock_fpe)(fpe_was_unmasked);
return res;
@@ -475,7 +475,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
int res = 0;
while(*ptr) {
- unsigned long ul_val;
+ ErlPfUWord ul_val;
int fmt = 0;
int width = -1;
int precision = -1;
@@ -661,22 +661,22 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
switch(fmt & FMTL_MASK) {
case FMTL_hh: {
signed char tval = (signed char) va_arg(ap,int);
- ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
- res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
+ res = fmt_uword(fn,arg,SIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
case FMTL_h: {
signed short tval = (signed short) va_arg(ap,int);
- ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
- res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
+ res = fmt_uword(fn,arg,SIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
case FMTL_l: {
signed long tval = (signed long) va_arg(ap,long);
- ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
- res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
+ res = fmt_uword(fn,arg,SIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
@@ -693,8 +693,8 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
#endif
default: {
signed int tval = (signed int) va_arg(ap,int);
- ul_val = (unsigned long) (tval < 0 ? (-tval) : tval);
- res = fmt_long(fn,arg,SIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) (tval < 0 ? (-tval) : tval);
+ res = fmt_uword(fn,arg,SIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
@@ -707,21 +707,21 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
switch(fmt & FMTL_MASK) {
case FMTL_hh: {
unsigned char tval = (unsigned char) va_arg(ap,int);
- ul_val = (unsigned long) tval;
- res = fmt_long(fn,arg,USIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) tval;
+ res = fmt_uword(fn,arg,USIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
case FMTL_h: {
unsigned short tval = (unsigned short) va_arg(ap,int);
- ul_val = (unsigned long) tval;
- res = fmt_long(fn,arg,USIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) tval;
+ res = fmt_uword(fn,arg,USIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
case FMTL_l: {
- ul_val = (unsigned long) va_arg(ap,long);
- res = fmt_long(fn,arg,USIGN(ul_val),ul_val,
+ ul_val = (ErlPfUWord) va_arg(ap,long);
+ res = fmt_uword(fn,arg,USIGN(ul_val),ul_val,
width,precision,fmt,&count);
break;
}
@@ -736,8 +736,8 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
#endif
default: {
unsigned int tval = (unsigned int) va_arg(ap,int);
- ul_val = (unsigned long) tval;
- res = fmt_long(fn,arg,USIGN(tval),ul_val,
+ ul_val = (ErlPfUWord) tval;
+ res = fmt_uword(fn,arg,USIGN(tval),ul_val,
width,precision,fmt,&count);
break;
}
@@ -795,10 +795,10 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
case FMTC_p: {
void* addr = va_arg(ap, void*);
- res = fmt_long(fn,
+ res = fmt_uword(fn,
arg,
- USIGN((unsigned long) addr),
- (unsigned long) addr,
+ USIGN((ErlPfUWord) addr),
+ (ErlPfUWord) addr,
width < 0 ? ((int) 2*sizeof(void *)) : width,
(precision < 0
? ((int) 2*sizeof(void *))
@@ -822,8 +822,8 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
case FMTC_T: /* Eterm */
case FMTC_R: { /* Eterm, Eterm* base (base ignored if !HALFWORD_HEAP) */
long prec;
- unsigned long eterm;
- unsigned long* eterm_base;
+ ErlPfEterm eterm;
+ ErlPfEterm* eterm_base;
if (!erts_printf_eterm_func)
return -EINVAL;
@@ -833,9 +833,9 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap)
prec = LONG_MAX;
else
prec = (long) precision;
- eterm = va_arg(ap, unsigned long);
+ eterm = va_arg(ap, ErlPfEterm);
eterm_base = ((fmt & FMTC_MASK) == FMTC_R) ?
- va_arg(ap, unsigned long*) : NULL;
+ va_arg(ap, ErlPfEterm*) : NULL;
if (width > 0 && !(fmt & FMTF_adj)) {
res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec, eterm_base);
if (res < 0)
@@ -890,8 +890,8 @@ int
erts_printf_pointer(fmtfn_t fn, void *arg, void *ptr)
{
int count = 0;
- int res = fmt_long(fn, arg, USIGN((unsigned long) ptr),
- (unsigned long) ptr, 2*sizeof(void *),
+ int res = fmt_uword(fn, arg, USIGN((ErlPfUWord) ptr),
+ (ErlPfUWord) ptr, 2*sizeof(void *),
2*sizeof(void *), FMTC_x|FMTF_pad|FMTF_alt, &count);
if (res < 0)
return res;
@@ -899,8 +899,8 @@ erts_printf_pointer(fmtfn_t fn, void *arg, void *ptr)
}
int
-erts_printf_ulong(fmtfn_t fn, void *arg, char conv, int pad, int width,
- unsigned long val)
+erts_printf_uword(fmtfn_t fn, void *arg, char conv, int pad, int width,
+ ErlPfUWord val)
{
int count = 0;
int res;
@@ -917,21 +917,21 @@ erts_printf_ulong(fmtfn_t fn, void *arg, char conv, int pad, int width,
}
if (pad)
prec = width;
- res = fmt_long(fn, arg, USIGN(val), val, width, prec, fmt, &count);
+ res = fmt_uword(fn, arg, USIGN(val), val, width, prec, fmt, &count);
if (res < 0)
return res;
return count;
}
-extern int
-erts_printf_slong(fmtfn_t fn, void *arg, char conv, int pad, int width,
- signed long val)
+int
+erts_printf_sword(fmtfn_t fn, void *arg, char conv, int pad, int width,
+ ErlPfSWord val)
{
int count = 0;
int res;
int fmt = 0;
int prec = -1;
- unsigned long ul_val;
+ ErlPfUWord ul_val;
switch (conv) {
case 'd': fmt |= FMTC_d; break;
case 'i': fmt |= FMTC_d; break;
@@ -943,8 +943,8 @@ erts_printf_slong(fmtfn_t fn, void *arg, char conv, int pad, int width,
}
if (pad)
prec = width;
- ul_val = (unsigned long) (val < 0 ? -val : val);
- res = fmt_long(fn, arg, SIGN(val), ul_val, width, prec, fmt, &count);
+ ul_val = (ErlPfUWord) (val < 0 ? -val : val);
+ res = fmt_uword(fn, arg, SIGN(val), ul_val, width, prec, fmt, &count);
if (res < 0)
return res;
return count;
diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c
index e363279f2e..036914af7b 100644
--- a/erts/lib_src/common/ethr_mutex.c
+++ b/erts/lib_src/common/ethr_mutex.c
@@ -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
@@ -692,7 +692,7 @@ write_lock_wait(struct ethr_mutex_base_ *mtxb,
goto chk_spin;
if (--until_yield == 0) {
until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ETHR_YIELD();
+ (void) ETHR_YIELD();
}
}
@@ -711,7 +711,7 @@ write_lock_wait(struct ethr_mutex_base_ *mtxb,
ETHR_SPIN_BODY;
if (--until_yield == 0) {
until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ETHR_YIELD();
+ (void) ETHR_YIELD();
}
act = ethr_atomic32_read(&mtxb->flgs);
scnt--;
@@ -2161,7 +2161,7 @@ rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx, ethr_sint32_t initial)
ETHR_SPIN_BODY;
if (--until_yield == 0) {
until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ETHR_YIELD();
+ (void) ETHR_YIELD();
}
act = ethr_atomic32_read(&rwmtx->mtxb.flgs);
scnt--;
@@ -2288,7 +2288,7 @@ rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx,
ETHR_SPIN_BODY;
if (--until_yield == 0) {
until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ETHR_YIELD();
+ (void) ETHR_YIELD();
}
act = ethr_atomic32_read(&rwmtx->mtxb.flgs);
scnt--;
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 7fe4a27703..a4ddae3d5d 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 003f382ab7..684659cca1 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..74c08fa4c9
--- /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 aad3544a27..143d5b18b9 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 55b6708767..a6b2fdb985 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 ac02293d0f..9460f1da6f 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 24294b92ea..8c47d8b611 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 f8bb2d1797..e6f3995b50 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 823b1d8d95..cdbaa43d5d 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 5bcc2eb6e4..a224b6a5d4 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -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 5ee3d03fef..e8ddfc4a57 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-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
@@ -149,8 +149,18 @@ start_it("inet", Id, Pid, Hosts) ->
start_it("efile", Id, Pid, _Hosts) ->
process_flag(trap_exit, true),
- {ok, Port} = prim_file:open([binary]),
- init_ack(Pid),
+ {ok, Port} = prim_file:start(),
+ %% Check that we started in a valid directory.
+ case prim_file:get_cwd(Port) of
+ {error, _} ->
+ %% At this point in the startup, we have no error_logger at all.
+ Report = "Invalid current directory or invalid filename "
+ "mode: loader cannot read current directory\n",
+ erlang:display(Report),
+ exit({error, invalid_current_directory});
+ _ ->
+ init_ack(Pid)
+ end,
MultiGet = case erlang:system_info(thread_pool_size) of
0 -> false;
_ -> true
@@ -434,7 +444,7 @@ efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) -
efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) ->
%% One port for each file read in "parallel":
- case prim_file:open([binary]) of
+ case prim_file:start() of
{ok, Port} ->
Port0 = State#state.data,
State1 = State#state{data = Port},
@@ -1385,14 +1395,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) ->
@@ -1429,32 +1432,6 @@ win32_pathtype(Name) ->
relative
end.
-vxworks_first(Name) ->
- case Name of
- [] ->
- {not_device, [], []};
- [$/ | 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]->
- {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) ->
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 1cbce5b80b..8e4a471a82 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.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
@@ -36,7 +36,8 @@
-export([set_cookie/2, get_cookie/0]).
-export([nodes/0]).
--export([list_to_integer/2,integer_to_list/2]).
+-export([integer_to_list/2]).
+-export([integer_to_binary/2]).
-export([flush_monitor_message/2]).
-export([set_cpu_topology/1, format_cpu_topology/1]).
-export([await_proc_exit/3]).
@@ -48,9 +49,7 @@
-deprecated([hash/2]).
-% Get rid of autoimports of spawn to avoid clashes with ourselves.
--compile({no_auto_import,[spawn/1]}).
--compile({no_auto_import,[spawn/4]}).
+%% Get rid of autoimports of spawn to avoid clashes with ourselves.
-compile({no_auto_import,[spawn_link/1]}).
-compile({no_auto_import,[spawn_link/4]}).
-compile({no_auto_import,[spawn_opt/2]}).
@@ -59,10 +58,2109 @@
-export_type([timestamp/0]).
+-type ext_binary() :: binary().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Native code BIF stubs and their types
+%% (BIF's actually implemented in this module goes last in the file)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Exports for all native code stubs
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-export([adler32/1, adler32/2, adler32_combine/3, append_element/2]).
+-export([atom_to_binary/2, atom_to_list/1, binary_part/2, binary_part/3]).
+-export([binary_to_atom/2, binary_to_existing_atom/2, binary_to_float/1]).
+-export([binary_to_integer/1,binary_to_integer/2]).
+-export([binary_to_list/1]).
+-export([binary_to_list/3, binary_to_term/1, binary_to_term/2]).
+-export([bit_size/1, bitsize/1, bitstr_to_list/1, bitstring_to_list/1]).
+-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_binary/1, float_to_binary/2,
+ 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_binary/1, integer_to_list/1]).
+-export([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]).
+-export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]).
+-export([list_to_integer/1, list_to_integer/2]).
+-export([list_to_pid/1, list_to_tuple/1, loaded/0]).
+-export([localtime/0, make_ref/0, match_spec_test/3, md5/1, md5_final/1]).
+-export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]).
+-export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2
+]).
+-export([node/0, node/1, now/0, phash/2, phash2/1, phash2/2]).
+-export([pid_to_list/1, port_close/1, port_command/2, port_command/3]).
+-export([port_connect/2, port_control/3, port_get_data/1]).
+-export([port_set_data/2, port_to_list/1, ports/0]).
+-export([posixtime_to_universaltime/1, pre_loaded/0, prepare_loading/2]).
+-export([process_display/2]).
+-export([process_flag/3, process_info/1, processes/0, purge_module/1]).
+-export([put/2, raise/3, read_timer/1, ref_to_list/1, register/2]).
+-export([registered/0, resume_process/1, round/1, self/0, send_after/3]).
+-export([seq_trace/2, seq_trace_print/1, seq_trace_print/2, setnode/2]).
+-export([setnode/3, size/1, spawn/3, spawn_link/3, split_binary/2]).
+-export([start_timer/3, suspend_process/2, system_monitor/0]).
+-export([system_monitor/1, system_monitor/2, system_profile/0]).
+-export([system_profile/2, throw/1, time/0, trace/3, trace_delivered/1]).
+-export([trace_info/2, trunc/1, tuple_size/1, universaltime/0]).
+-export([universaltime_to_posixtime/1, unlink/1, unregister/1, whereis/1]).
+
+-export([abs/1, append/2, element/2, get_module_info/2, hd/1,
+ is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1,
+ is_float/1, is_function/1, is_function/2, is_integer/1,
+ is_list/1, is_number/1, is_pid/1, is_port/1, is_record/2,
+ is_record/3, is_reference/1, is_tuple/1, load_module/2,
+ load_nif/2, localtime_to_universaltime/2, make_fun/3,
+ make_tuple/2, make_tuple/3, nodes/1, open_port/2,
+ port_call/2, port_call/3, port_info/1, port_info/2, process_flag/2,
+ process_info/2, send/2, send/3, seq_trace_info/1,
+ setelement/3, spawn_opt/1,
+ statistics/1, subtract/2, system_flag/2,
+ term_to_binary/1, term_to_binary/2, tl/1, trace_pattern/2,
+ trace_pattern/3, tuple_to_list/1, system_info/1,
+ universaltime_to_localtime/1]).
+-export([dt_get_tag/0, dt_get_tag_data/0, dt_prepend_vm_tag_data/1, dt_append_vm_tag_data/1,
+ dt_put_tag/1, dt_restore_tag/1, dt_spread_tag/1]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Simple native code BIFs
+%%% These are here for the types/specs, the real implementation is in the C code.
+%%% The first chunk is originally auto-generated from
+%%% $ERL_TOP/lib/hipe/cerl/erl_bif_types.erl as released in R15B.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%% types
+
+-type fun_info_item() ::
+ arity |
+ env |
+ index |
+ name |
+ module |
+ new_index |
+ new_uniq |
+ pid |
+ type |
+ uniq.
+
+-type seq_trace_info() ::
+ 'send' |
+ 'receive' |
+ 'print' |
+ 'timestamp' |
+ 'label' |
+ 'serial'.
+
+-type seq_trace_info_returns() ::
+ { seq_trace_info(), non_neg_integer() |
+ boolean() |
+ { non_neg_integer(), non_neg_integer() } } |
+ [].
+
+-type system_profile_option() ::
+ 'exclusive' |
+ 'runnable_ports' |
+ 'runnable_procs' |
+ 'scheduler'.
+
+-type system_monitor_option() ::
+ 'busy_port' |
+ 'busy_dist_port' |
+ {'long_gc', non_neg_integer()} |
+ {'large_heap', non_neg_integer()}.
+
+
+-type raise_stacktrace() ::
+ [{module(), atom(), arity() | [term()]} |
+ {function(), [term()]}] |
+ [{module(), atom(), arity() | [term()], [{atom(),term()}]} |
+ {function(), [term()], [{atom(),term()}]}].
+
+-type bitstring_list() ::
+ maybe_improper_list(byte() | bitstring() | bitstring_list(), bitstring() | []).
+
+-type trace_flag() ::
+ all |
+ send |
+ 'receive' |
+ procs |
+ call |
+ silent |
+ return_to |
+ running |
+ exiting |
+ garbage_collection |
+ timestamp |
+ cpu_timestamp |
+ arity |
+ set_on_spawn |
+ set_on_first_spawn |
+ set_on_link |
+ set_on_first_link |
+ {tracer, pid() | port()}.
+
+-type trace_info_item_result() ::
+ {traced, global | local | false | undefined} |
+ {match_spec, trace_match_spec() | false | undefined} |
+ {meta, pid() | port() | false | undefined | []} |
+ {meta_match_spec, trace_match_spec() | false | undefined} |
+ {call_count, non_neg_integer() | boolean() | undefined} |
+ {call_time, [{pid(), non_neg_integer(),
+ non_neg_integer(), non_neg_integer()}] | boolean() | undefined}.
+
+-type trace_info_flag() ::
+ send |
+ 'receive' |
+ set_on_spawn |
+ call |
+ return_to |
+ procs |
+ set_on_first_spawn |
+ set_on_link |
+ running |
+ garbage_collection |
+ timestamp |
+ arity.
+
+-type trace_info_return() ::
+ undefined |
+ {flags, [trace_info_flag()]} |
+ {tracer, pid() | port() | []} |
+ trace_info_item_result() |
+ {all, [ trace_info_item_result() ] | false | undefined}.
+
+%% Specs and stubs
+%% adler32/1
+-spec erlang:adler32(Data) -> non_neg_integer() when
+ Data :: iodata().
+adler32(_Data) ->
+ erlang:nif_error(undefined).
+
+%% adler32/2
+-spec erlang:adler32(OldAdler, Data) -> non_neg_integer() when
+ OldAdler :: non_neg_integer(),
+ Data :: iodata().
+adler32(_OldAdler, _Data) ->
+ erlang:nif_error(undefined).
+
+%% adler32_combine/3
+-spec erlang:adler32_combine(FirstAdler, SecondAdler, SecondSize) -> non_neg_integer() when
+ FirstAdler :: non_neg_integer(),
+ SecondAdler :: non_neg_integer(),
+ SecondSize :: non_neg_integer().
+adler32_combine(_FirstAdler, _SecondAdler, _SecondSize) ->
+ erlang:nif_error(undefined).
+
+%% append_element/2
+-spec erlang:append_element(Tuple1, Term) -> Tuple2 when
+ Tuple1 :: tuple(),
+ Tuple2 :: tuple(),
+ Term :: term().
+append_element(_Tuple1, _Term) ->
+ erlang:nif_error(undefined).
+
+%% atom_to_binary/2
+-spec atom_to_binary(Atom, Encoding) -> binary() when
+ Atom :: atom(),
+ Encoding :: latin1 | unicode | utf8.
+atom_to_binary(_Atom, _Encoding) ->
+ erlang:nif_error(undefined).
+
+%% atom_to_list/1
+-spec atom_to_list(Atom) -> string() when
+ Atom :: atom().
+atom_to_list(_Atom) ->
+ erlang:nif_error(undefined).
+
+%% binary_part/2
+%% Shadowed by erl_bif_types: erlang:binary_part/2
+-spec binary_part(Subject, PosLen) -> binary() when
+ Subject :: binary(),
+ PosLen :: {Start :: non_neg_integer(), Length :: integer()}.
+binary_part(_Subject, _PosLen) ->
+ erlang:nif_error(undefined).
+
+%% binary_part/3
+%% Shadowed by erl_bif_types: erlang:binary_part/3
+-spec binary_part(Subject, Start, Length) -> binary() when
+ Subject :: binary(),
+ Start :: non_neg_integer(),
+ Length :: integer().
+binary_part(_Subject, _Start, _Length) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_atom/2
+-spec binary_to_atom(Binary, Encoding) -> atom() when
+ Binary :: binary(),
+ Encoding :: latin1 | unicode | utf8.
+binary_to_atom(_Binary, _Encoding) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_existing_atom/2
+-spec binary_to_existing_atom(Binary, Encoding) -> atom() when
+ Binary :: binary(),
+ Encoding :: latin1 | unicode | utf8.
+binary_to_existing_atom(_Binary, _Encoding) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_float/1
+-spec binary_to_float(Binary) -> float() when
+ Binary :: binary().
+binary_to_float(_Binary) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_integer/1
+-spec binary_to_integer(Binary) -> integer() when
+ Binary :: binary().
+binary_to_integer(_Binary) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_integer/2
+-spec binary_to_integer(Binary,Base) -> integer() when
+ Binary :: binary(),
+ Base :: 2..36.
+binary_to_integer(_Binary,_Base) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_list/1
+-spec binary_to_list(Binary) -> [byte()] when
+ Binary :: binary().
+binary_to_list(_Binary) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_list/3
+-spec binary_to_list(Binary, Start, Stop) -> [byte()] when
+ Binary :: binary(),
+ Start :: pos_integer(),
+ Stop :: pos_integer().
+binary_to_list(_Binary, _Start, _Stop) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_term/1
+-spec binary_to_term(Binary) -> term() when
+ Binary :: ext_binary().
+binary_to_term(_Binary) ->
+ erlang:nif_error(undefined).
+
+%% binary_to_term/2
+-spec binary_to_term(Binary, Opts) -> term() when
+ Binary :: ext_binary(),
+ Opts :: [safe].
+binary_to_term(_Binary, _Opts) ->
+ erlang:nif_error(undefined).
+
+%% bit_size/1
+%% Shadowed by erl_bif_types: erlang:bit_size/1
+-spec bit_size(Bitstring) -> non_neg_integer() when
+ Bitstring :: bitstring().
+bit_size(_Bitstring) ->
+ erlang:nif_error(undefined).
+
+%% bitsize/1
+-spec bitsize(P1) -> non_neg_integer() when
+ P1 :: bitstring().
+bitsize(_P1) ->
+ erlang:nif_error(undefined).
+
+%% bitstr_to_list/1
+-spec erlang:bitstr_to_list(P1) -> [byte() | bitstring()] when
+ P1 :: bitstring().
+bitstr_to_list(_P1) ->
+ erlang:nif_error(undefined).
+
+%% bitstring_to_list/1
+-spec bitstring_to_list(Bitstring) -> [byte() | bitstring()] when
+ Bitstring :: bitstring().
+bitstring_to_list(_Bitstring) ->
+ erlang:nif_error(undefined).
+
+%% bump_reductions/1
+-spec erlang:bump_reductions(Reductions) -> true when
+ Reductions :: pos_integer().
+bump_reductions(_Reductions) ->
+ erlang:nif_error(undefined).
+
+%% byte_size/1
+%% Shadowed by erl_bif_types: erlang:byte_size/1
+-spec byte_size(Bitstring) -> non_neg_integer() when
+ Bitstring :: bitstring().
+byte_size(_Bitstring) ->
+ erlang:nif_error(undefined).
+
+%% call_on_load_function/1
+-spec erlang:call_on_load_function(P1) -> term() when
+ P1 :: atom().
+call_on_load_function(_P1) ->
+ erlang:nif_error(undefined).
+
+%% cancel_timer/1
+-spec erlang:cancel_timer(TimerRef) -> Time | false when
+ TimerRef :: reference(),
+ Time :: non_neg_integer().
+cancel_timer(_TimerRef) ->
+ erlang:nif_error(undefined).
+
+%% check_old_code/1
+-spec check_old_code(Module) -> boolean() when
+ Module :: module().
+check_old_code(_Module) ->
+ erlang:nif_error(undefined).
+
+%% check_process_code/2
+-spec check_process_code(Pid, Module) -> boolean() when
+ Pid :: pid(),
+ Module :: module().
+check_process_code(_Pid, _Module) ->
+ erlang:nif_error(undefined).
+
+%% crc32/1
+-spec erlang:crc32(Data) -> non_neg_integer() when
+ Data :: iodata().
+crc32(_Data) ->
+ erlang:nif_error(undefined).
+
+%% crc32/2
+-spec erlang:crc32(OldCrc, Data) -> non_neg_integer() when
+ OldCrc :: non_neg_integer(),
+ Data :: iodata().
+crc32(_OldCrc, _Data) ->
+ erlang:nif_error(undefined).
+
+%% crc32_combine/3
+-spec erlang:crc32_combine(FirstCrc, SecondCrc, SecondSize) -> non_neg_integer() when
+ FirstCrc :: non_neg_integer(),
+ SecondCrc :: non_neg_integer(),
+ SecondSize :: non_neg_integer().
+crc32_combine(_FirstCrc, _SecondCrc, _SecondSize) ->
+ erlang:nif_error(undefined).
+
+%% date/0
+-spec date() -> Date when
+ Date :: calendar:date().
+date() ->
+ erlang:nif_error(undefined).
+
+%% decode_packet/3
+-spec erlang:decode_packet(Type, Bin, Options) ->
+ {ok, Packet, Rest} |
+ {more, Length} |
+ {error, Reason} when
+ Type :: 'raw' | 0 | 1 | 2 | 4 | 'asn1' | 'cdr' | 'sunrm' | 'fcgi'
+ | 'tpkt' | 'line' | 'http' | 'http_bin' | 'httph' | 'httph_bin',
+ Bin :: binary(),
+ Options :: [Opt],
+ Opt :: {packet_size, non_neg_integer()}
+ | {line_length, non_neg_integer()},
+ Packet :: binary() | HttpPacket,
+ Rest :: binary(),
+ Length :: non_neg_integer() | undefined,
+ Reason :: term(),
+ HttpPacket :: HttpRequest
+ | HttpResponse
+ | HttpHeader
+ | 'http_eoh'
+ | HttpError,
+ HttpRequest :: {'http_request', HttpMethod, HttpUri, HttpVersion},
+ HttpResponse :: {'http_response', HttpVersion, integer(), HttpString},
+ HttpHeader :: {'http_header',
+ integer(),
+ HttpField,
+ Reserved :: term(),
+ Value :: HttpString},
+ HttpError :: {'http_error', HttpString},
+ HttpMethod :: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE'
+ | 'TRACE' | HttpString,
+ HttpUri :: '*'
+ | { 'absoluteURI',
+ 'http' | 'https',
+ Host :: HttpString,
+ Port :: inet:port_number() | 'undefined',
+ Path :: HttpString}
+ | {'scheme', Scheme :: HttpString, HttpString}
+ | {'abs_path', HttpString}
+ | HttpString,
+ HttpVersion :: {Major :: non_neg_integer(), Minor :: non_neg_integer()},
+ HttpField :: 'Cache-Control'
+ | 'Connection'
+ | 'Date'
+ | 'Pragma'
+ | 'Transfer-Encoding'
+ | 'Upgrade'
+ | 'Via'
+ | 'Accept'
+ | 'Accept-Charset'
+ | 'Accept-Encoding'
+ | 'Accept-Language'
+ | 'Authorization'
+ | 'From'
+ | 'Host'
+ | 'If-Modified-Since'
+ | 'If-Match'
+ | 'If-None-Match'
+ | 'If-Range'
+ | 'If-Unmodified-Since'
+ | 'Max-Forwards'
+ | 'Proxy-Authorization'
+ | 'Range'
+ | 'Referer'
+ | 'User-Agent'
+ | 'Age'
+ | 'Location'
+ | 'Proxy-Authenticate'
+ | 'Public'
+ | 'Retry-After'
+ | 'Server'
+ | 'Vary'
+ | 'Warning'
+ |'Www-Authenticate'
+ | 'Allow'
+ | 'Content-Base'
+ | 'Content-Encoding'
+ | 'Content-Language'
+ | 'Content-Length'
+ | 'Content-Location'
+ | 'Content-Md5'
+ | 'Content-Range'
+ | 'Content-Type'
+ | 'Etag'
+ | 'Expires'
+ | 'Last-Modified'
+ | 'Accept-Ranges'
+ | 'Set-Cookie'
+ | 'Set-Cookie2'
+ | 'X-Forwarded-For'
+ | 'Cookie'
+ | 'Keep-Alive'
+ | 'Proxy-Connection'
+ | HttpString,
+ HttpString :: string() | binary().
+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().
+delete_module(_Module) ->
+ erlang:nif_error(undefined).
+
+%% demonitor/1
+-spec demonitor(MonitorRef) -> true when
+ MonitorRef :: reference().
+demonitor(_MonitorRef) ->
+ erlang:nif_error(undefined).
+
+%% demonitor/2
+-spec demonitor(MonitorRef, OptionList) -> boolean() when
+ MonitorRef :: reference(),
+ OptionList :: [Option],
+ Option :: flush | info.
+demonitor(_MonitorRef, _OptionList) ->
+ erlang:nif_error(undefined).
+
+%% display/1
+-spec erlang:display(Term) -> true when
+ Term :: term().
+display(_Term) ->
+ erlang:nif_error(undefined).
+
+%% display_nl/0
+-spec erlang:display_nl() -> true.
+display_nl() ->
+ erlang:nif_error(undefined).
+
+%% display_string/1
+-spec erlang:display_string(P1) -> true when
+ P1 :: string().
+display_string(_P1) ->
+ erlang:nif_error(undefined).
+
+%% dist_exit/3
+-spec erlang:dist_exit(P1, P2, P3) -> true when
+ P1 :: pid(),
+ P2 :: kill | noconnection | normal,
+ P3 :: pid() | port().
+dist_exit(_P1, _P2, _P3) ->
+ erlang:nif_error(undefined).
+
+%% dt_append_vm_tag_data/1
+-spec erlang:dt_append_vm_tag_data(IoData) -> IoDataRet when
+ IoData :: iodata(),
+ IoDataRet :: iodata().
+dt_append_vm_tag_data(_IoData) ->
+ erlang:nif_error(undefined).
+
+%% dt_get_tag/0
+-spec erlang:dt_get_tag() -> binary() | undefined.
+dt_get_tag() ->
+ erlang:nif_error(undefined).
+
+%% dt_get_tag_data/0
+-spec erlang:dt_get_tag_data() -> binary() | undefined.
+dt_get_tag_data() ->
+ erlang:nif_error(undefined).
+
+%% dt_prepend_vm_tag_data/1
+-spec erlang:dt_prepend_vm_tag_data(IoData) -> IoDataRet when
+ IoData :: iodata(),
+ IoDataRet :: iodata().
+dt_prepend_vm_tag_data(_IoData) ->
+ erlang:nif_error(undefined).
+
+%% dt_put_tag/1
+-spec erlang:dt_put_tag(IoData) -> binary() | undefined when
+ IoData :: iodata().
+dt_put_tag(_IoData) ->
+ erlang:nif_error(undefined).
+
+%% dt_restore_tag/1
+-spec erlang:dt_restore_tag(TagData) -> true when
+ TagData :: term().
+dt_restore_tag(_TagData) ->
+ erlang:nif_error(undefined).
+
+%% dt_spread_tag/1
+-spec erlang:dt_spread_tag(boolean()) -> TagData when
+ TagData :: term().
+dt_spread_tag(_Bool) ->
+ erlang:nif_error(undefined).
+
+%% erase/0
+-spec erase() -> [{Key, Val}] when
+ Key :: term(),
+ Val :: term().
+erase() ->
+ erlang:nif_error(undefined).
+
+%% erase/1
+-spec erase(Key) -> Val | undefined when
+ Key :: term(),
+ Val :: term().
+erase(_Key) ->
+ erlang:nif_error(undefined).
+
+%% error/1
+%% Shadowed by erl_bif_types: erlang:error/1
+-spec error(Reason) -> no_return() when
+ Reason :: term().
+error(_Reason) ->
+ erlang:nif_error(undefined).
+
+%% error/2
+%% Shadowed by erl_bif_types: erlang:error/2
+-spec error(Reason, Args) -> no_return() when
+ Reason :: term(),
+ Args :: [term()].
+error(_Reason, _Args) ->
+ erlang:nif_error(undefined).
+
+%% exit/1
+%% Shadowed by erl_bif_types: erlang:exit/1
+-spec exit(Reason) -> no_return() when
+ Reason :: term().
+exit(_Reason) ->
+ erlang:nif_error(undefined).
+
+%% exit/2
+-spec exit(Pid, Reason) -> true when
+ Pid :: pid() | port(),
+ Reason :: term().
+exit(_Pid, _Reason) ->
+ erlang:nif_error(undefined).
+
+%% external_size/1
+-spec erlang:external_size(Term) -> non_neg_integer() when
+ Term :: term().
+external_size(_Term) ->
+ erlang:nif_error(undefined).
+
+%% external_size/2
+-spec erlang:external_size(Term, Options) -> non_neg_integer() when
+ Term :: term(),
+ Options :: [{minor_version, Version :: non_neg_integer()}].
+external_size(_Term, _Options) ->
+ erlang:nif_error(undefined).
+
+%% finish_loading/2
+-spec erlang:finish_loading(PreparedCodeBinaries) -> ok | Error when
+ PreparedCodeBinaries :: [PreparedCodeBinary],
+ PreparedCodeBinary :: binary(),
+ ModuleList :: [module()],
+ Error :: {not_purged,ModuleList} | {on_load,ModuleList}.
+finish_loading(_List) ->
+ erlang:nif_error(undefined).
+
+%% finish_after_on_load/2
+-spec erlang:finish_after_on_load(P1, P2) -> true when
+ P1 :: atom(),
+ P2 :: boolean().
+finish_after_on_load(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% float/1
+%% Shadowed by erl_bif_types: erlang:float/1
+-spec float(Number) -> float() when
+ Number :: number().
+float(_Number) ->
+ erlang:nif_error(undefined).
+
+%% float_to_binary/1
+-spec float_to_binary(Float) -> binary() when
+ Float :: float().
+float_to_binary(_Float) ->
+ erlang:nif_error(undefined).
+
+%% float_to_binary/2
+-spec float_to_binary(Float, Options) -> binary() when
+ Float :: float(),
+ Options :: [Option],
+ Option :: {decimals, Decimals :: 0..253} |
+ {scientific, Decimals :: 0..249} |
+ compact.
+float_to_binary(_Float, _Options) ->
+ erlang:nif_error(undefined).
+
+%% float_to_list/1
+-spec float_to_list(Float) -> string() when
+ Float :: float().
+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, Decimals :: 0..253} |
+ {scientific, Decimals :: 0..249} |
+ compact.
+float_to_list(_Float, _Options) ->
+ erlang:nif_error(undefined).
+
+%% fun_info/2
+-spec erlang:fun_info(Fun, Item) -> {Item, Info} when
+ Fun :: function(),
+ Item :: fun_info_item(),
+ Info :: term().
+fun_info(_Fun, _Item) ->
+ erlang:nif_error(undefined).
+
+%% fun_to_list/1
+-spec erlang:fun_to_list(Fun) -> string() when
+ Fun :: function().
+fun_to_list(_Fun) ->
+ erlang:nif_error(undefined).
+
+%% function_exported/3
+-spec erlang:function_exported(Module, Function, Arity) -> boolean() when
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity().
+function_exported(_Module, _Function, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% garbage_collect/0
+-spec garbage_collect() -> true.
+garbage_collect() ->
+ erlang:nif_error(undefined).
+
+%% garbage_collect/1
+-spec garbage_collect(Pid) -> boolean() when
+ Pid :: pid().
+garbage_collect(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% garbage_collect_message_area/0
+-spec erlang:garbage_collect_message_area() -> boolean().
+garbage_collect_message_area() ->
+ erlang:nif_error(undefined).
+
+%% get/0
+-spec get() -> [{Key, Val}] when
+ Key :: term(),
+ Val :: term().
+get() ->
+ erlang:nif_error(undefined).
+
+%% get/1
+-spec get(Key) -> Val | undefined when
+ Key :: term(),
+ Val :: term().
+get(_Key) ->
+ erlang:nif_error(undefined).
+
+%% get_keys/1
+-spec get_keys(Val) -> [Key] when
+ Val :: term(),
+ Key :: term().
+get_keys(_Val) ->
+ erlang:nif_error(undefined).
+
+%% get_module_info/1
+-spec erlang:get_module_info(P1) -> [{atom(), [{atom(), term()}]}] when
+ P1 :: atom().
+get_module_info(_P1) ->
+ erlang:nif_error(undefined).
+
+%% get_stacktrace/0
+-spec erlang:get_stacktrace() -> [stack_item()].
+get_stacktrace() ->
+ erlang:nif_error(undefined).
+
+%% group_leader/0
+-spec group_leader() -> pid().
+group_leader() ->
+ erlang:nif_error(undefined).
+
+%% group_leader/2
+-spec group_leader(GroupLeader, Pid) -> true when
+ GroupLeader :: pid(),
+ Pid :: pid().
+group_leader(_GroupLeader, _Pid) ->
+ erlang:nif_error(undefined).
+
+%% halt/0
+%% Shadowed by erl_bif_types: erlang:halt/0
+-spec halt() -> no_return().
+halt() ->
+ erlang:nif_error(undefined).
+
+%% halt/1
+%% Shadowed by erl_bif_types: erlang:halt/1
+-spec halt(Status) -> no_return() when
+ Status :: non_neg_integer() | 'abort' | string().
+halt(_Status) ->
+ erlang:nif_error(undefined).
+
+%% halt/2
+%% Shadowed by erl_bif_types: erlang:halt/2
+-spec halt(Status, Options) -> no_return() when
+ Status :: non_neg_integer() | 'abort' | string(),
+ Options :: [Option],
+ Option :: {flush, boolean()}.
+halt(_Status, _Options) ->
+ erlang:nif_error(undefined).
+
+%% hash/2
+-spec erlang:hash(Term, Range) -> pos_integer() when
+ Term :: term(),
+ Range :: pos_integer().
+hash(_Term, _Range) ->
+ erlang:nif_error(undefined).
+
+%% hibernate/3
+-spec erlang:hibernate(Module, Function, Args) -> no_return() when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+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_binary/1
+-spec integer_to_binary(Integer) -> binary() when
+ Integer :: integer().
+integer_to_binary(_Integer) ->
+ erlang:nif_error(undefined).
+
+%% integer_to_list/1
+-spec integer_to_list(Integer) -> string() when
+ Integer :: integer().
+integer_to_list(_Integer) ->
+ erlang:nif_error(undefined).
+
+%% iolist_size/1
+-spec iolist_size(Item) -> non_neg_integer() when
+ Item :: iolist() | binary().
+iolist_size(_Item) ->
+ erlang:nif_error(undefined).
+
+%% iolist_to_binary/1
+-spec iolist_to_binary(IoListOrBinary) -> binary() when
+ IoListOrBinary :: iolist() | binary().
+iolist_to_binary(_IoListOrBinary) ->
+ erlang:nif_error(undefined).
+
+%% is_alive/0
+-spec is_alive() -> boolean().
+is_alive() ->
+ erlang:nif_error(undefined).
+
+%% is_builtin/3
+-spec erlang:is_builtin(Module, Function, Arity) -> boolean() when
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity().
+is_builtin(_Module, _Function, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% is_process_alive/1
+-spec is_process_alive(Pid) -> boolean() when
+ Pid :: pid().
+is_process_alive(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% length/1
+%% Shadowed by erl_bif_types: erlang:length/1
+-spec length(List) -> non_neg_integer() when
+ List :: [term()].
+length(_List) ->
+ erlang:nif_error(undefined).
+
+%% link/1
+-spec link(PidOrPort) -> true when
+ PidOrPort :: pid() | port().
+link(_PidOrPort) ->
+ erlang:nif_error(undefined).
+
+%% list_to_atom/1
+-spec list_to_atom(String) -> atom() when
+ String :: string().
+list_to_atom(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_binary/1
+-spec list_to_binary(IoList) -> binary() when
+ IoList :: iolist().
+list_to_binary(_IoList) ->
+ erlang:nif_error(undefined).
+
+%% list_to_bitstr/1
+-spec erlang:list_to_bitstr(P1) -> bitstring() when
+ P1 :: bitstring_list().
+list_to_bitstr(_P1) ->
+ erlang:nif_error(undefined).
+
+%% list_to_bitstring/1
+-spec list_to_bitstring(BitstringList) -> bitstring() when
+ BitstringList :: bitstring_list().
+list_to_bitstring(_BitstringList) ->
+ erlang:nif_error(undefined).
+
+%% list_to_existing_atom/1
+-spec list_to_existing_atom(String) -> atom() when
+ String :: string().
+list_to_existing_atom(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_float/1
+-spec list_to_float(String) -> float() when
+ String :: string().
+list_to_float(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_integer/1
+-spec list_to_integer(String) -> integer() when
+ String :: string().
+list_to_integer(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_integer/2
+-spec list_to_integer(String, Base) -> integer() when
+ String :: string(),
+ Base :: 2..36.
+list_to_integer(_String,_Base) ->
+ erlang:nif_error(undefined).
+
+%% list_to_pid/1
+-spec list_to_pid(String) -> pid() when
+ String :: string().
+list_to_pid(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_tuple/1
+-spec list_to_tuple(List) -> tuple() when
+ List :: [term()].
+list_to_tuple(_List) ->
+ erlang:nif_error(undefined).
+
+%% loaded/0
+-spec erlang:loaded() -> [Module] when
+ Module :: module().
+loaded() ->
+ erlang:nif_error(undefined).
+
+%% localtime/0
+-spec erlang:localtime() -> DateTime when
+ DateTime :: calendar:datetime().
+localtime() ->
+ erlang:nif_error(undefined).
+
+%% make_ref/0
+-spec make_ref() -> reference().
+make_ref() ->
+ erlang:nif_error(undefined).
+
+%% match_spec_test/3
+-spec erlang:match_spec_test(P1, P2, P3) -> TestResult when
+ P1 :: [term()] | tuple(),
+ P2 :: term(),
+ P3 :: table | trace,
+ TestResult :: {ok, term(), [return_trace], [ {error | warning, string()} ]} | {error, [ {error | warning, string()} ]}.
+match_spec_test(_P1, _P2, _P3) ->
+ erlang:nif_error(undefined).
+
+%% md5/1
+-spec erlang:md5(Data) -> Digest when
+ Data :: iodata(),
+ Digest :: binary().
+md5(_Data) ->
+ erlang:nif_error(undefined).
+
+%% md5_final/1
+-spec erlang:md5_final(Context) -> Digest when
+ Context :: binary(),
+ Digest :: binary().
+md5_final(_Context) ->
+ erlang:nif_error(undefined).
+
+%% md5_init/0
+-spec erlang:md5_init() -> Context when
+ Context :: binary().
+md5_init() ->
+ erlang:nif_error(undefined).
+
+%% md5_update/2
+-spec erlang:md5_update(Context, Data) -> NewContext when
+ Context :: binary(),
+ Data :: iodata(),
+ NewContext :: binary().
+md5_update(_Context, _Data) ->
+ erlang:nif_error(undefined).
+
+%% module_loaded/1
+-spec module_loaded(Module) -> boolean() when
+ Module :: module().
+module_loaded(_Module) ->
+ erlang:nif_error(undefined).
+
+%% monitor/2
+-spec monitor(Type, Item) -> MonitorRef when
+ Type :: process,
+ Item :: pid() | Module | {Module, Node},
+ Module :: module(),
+ Node :: node(),
+ MonitorRef :: reference().
+monitor(_Type, _Item) ->
+ erlang:nif_error(undefined).
+
+%% monitor_node/2
+-spec monitor_node(Node, Flag) -> true when
+ Node :: node(),
+ Flag :: boolean().
+monitor_node(_Node, _Flag) ->
+ erlang:nif_error(undefined).
+
+%% monitor_node/3
+-spec erlang:monitor_node(Node, Flag, Options) -> true when
+ Node :: node(),
+ Flag :: boolean(),
+ Options :: [Option],
+ Option :: allow_passive_connect.
+monitor_node(_Node, _Flag, _Options) ->
+ erlang:nif_error(undefined).
+
+%% nif_error/1
+%% Shadowed by erl_bif_types: erlang:nif_error/1
+-spec erlang:nif_error(Reason) -> no_return() when
+ Reason :: term().
+nif_error(_Reason) ->
+ erlang:nif_error(undefined).
+
+%% nif_error/2
+%% Shadowed by erl_bif_types: erlang:nif_error/2
+-spec erlang:nif_error(Reason, Args) -> no_return() when
+ Reason :: term(),
+ Args :: [term()].
+nif_error(_Reason, _Args) ->
+ erlang:nif_error(undefined).
+
+%% node/0
+%% Shadowed by erl_bif_types: erlang:node/0
+-spec node() -> Node when
+ Node :: node().
+node() ->
+ erlang:nif_error(undefined).
+
+%% node/1
+%% Shadowed by erl_bif_types: erlang:node/1
+-spec node(Arg) -> Node when
+ Arg :: pid() | port() | reference(),
+ Node :: node().
+node(_Arg) ->
+ erlang:nif_error(undefined).
+
+%% now/0
+-spec now() -> Timestamp when
+ Timestamp :: timestamp().
+now() ->
+ erlang:nif_error(undefined).
+
+%% phash/2
+-spec erlang:phash(Term, Range) -> Hash when
+ Term :: term(),
+ Range :: pos_integer(),
+ Hash :: pos_integer().
+phash(_Term, _Range) ->
+ erlang:nif_error(undefined).
+
+%% phash2/1
+-spec erlang:phash2(Term) -> Hash when
+ Term :: term(),
+ Hash :: non_neg_integer().
+phash2(_Term) ->
+ erlang:nif_error(undefined).
+
+%% phash2/2
+-spec erlang:phash2(Term, Range) -> Hash when
+ Term :: term(),
+ Range :: pos_integer(),
+ Hash :: non_neg_integer().
+phash2(_Term, _Range) ->
+ erlang:nif_error(undefined).
+
+%% pid_to_list/1
+-spec pid_to_list(Pid) -> string() when
+ Pid :: pid().
+pid_to_list(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% port_to_list/1
+-spec erlang:port_to_list(Port) -> string() when
+ Port :: port().
+port_to_list(_Port) ->
+ erlang:nif_error(undefined).
+
+%% ports/0
+-spec erlang:ports() -> [port()].
+ports() ->
+ erlang:nif_error(undefined).
+
+%% posixtime_to_universaltime/1
+-spec erlang:posixtime_to_universaltime(P1) -> {calendar:date(), calendar:time()} when
+ P1 :: integer().
+posixtime_to_universaltime(_P1) ->
+ erlang:nif_error(undefined).
+
+%% prepare_loading/2
+-spec erlang:prepare_loading(Module, Code) -> PreparedCode | {error, Reason} when
+ Module :: module(),
+ Code :: binary(),
+ PreparedCode :: binary(),
+ Reason :: bad_file.
+prepare_loading(_Module, _Code) ->
+ erlang:nif_error(undefined).
+
+%% pre_loaded/0
+-spec pre_loaded() -> [module()].
+pre_loaded() ->
+ erlang:nif_error(undefined).
+
+%% process_display/2
+-spec erlang:process_display(Pid, Type) -> true when
+ Pid :: pid(),
+ Type :: backtrace.
+process_display(_Pid, _Type) ->
+ erlang:nif_error(undefined).
+
+%% process_flag/3
+-spec process_flag(Pid, Flag, Value) -> OldValue when
+ Pid :: pid(),
+ Flag :: save_calls,
+ Value :: non_neg_integer(),
+ OldValue :: non_neg_integer().
+process_flag(_Pid, _Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+%% process_info/1
+-spec process_info(Pid) -> Info when
+ Pid :: pid(),
+ Info :: [InfoTuple] | undefined,
+ InfoTuple :: process_info_result_item().
+process_info(_Pid) ->
+ erlang:nif_error(undefined).
+
+%% processes/0
+-spec processes() -> [pid()].
+processes() ->
+ erlang:nif_error(undefined).
+
+%% purge_module/1
+-spec purge_module(Module) -> true when
+ Module :: atom().
+purge_module(_Module) ->
+ erlang:nif_error(undefined).
+
+%% put/2
+-spec put(Key, Val) -> term() when
+ Key :: term(),
+ Val :: term().
+put(_Key, _Val) ->
+ erlang:nif_error(undefined).
+
+%% raise/3
+-spec erlang:raise(Class, Reason, Stacktrace) -> no_return() when
+ Class :: error | exit | throw,
+ Reason :: term(),
+ Stacktrace :: raise_stacktrace().
+raise(_Class, _Reason, _Stacktrace) ->
+ erlang:nif_error(undefined).
+
+%% read_timer/1
+-spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when
+ TimerRef :: reference().
+read_timer(_TimerRef) ->
+ erlang:nif_error(undefined).
+
+%% ref_to_list/1
+-spec erlang:ref_to_list(Ref) -> string() when
+ Ref :: reference().
+ref_to_list(_Ref) ->
+ erlang:nif_error(undefined).
+
+%% register/2
+-spec register(RegName, PidOrPort) -> true when
+ RegName :: atom(),
+ PidOrPort :: port() | pid().
+register(_RegName, _PidOrPort) ->
+ erlang:nif_error(undefined).
+
+%% registered/0
+-spec registered() -> [RegName] when
+ RegName :: atom().
+registered() ->
+ erlang:nif_error(undefined).
+
+%% resume_process/1
+-spec erlang:resume_process(Suspendee) -> true when
+ Suspendee :: pid().
+resume_process(_Suspendee) ->
+ erlang:nif_error(undefined).
+
+%% round/1
+%% Shadowed by erl_bif_types: erlang:round/1
+-spec round(Number) -> integer() when
+ Number :: number().
+round(_Number) ->
+ erlang:nif_error(undefined).
+
+%% self/0
+%% Shadowed by erl_bif_types: erlang:self/0
+-spec self() -> pid().
+self() ->
+ erlang:nif_error(undefined).
+
+%% send_after/3
+-spec erlang:send_after(Time, Dest, Msg) -> TimerRef when
+ Time :: non_neg_integer(),
+ Dest :: pid() | atom(),
+ Msg :: term(),
+ TimerRef :: reference().
+send_after(_Time, _Dest, _Msg) ->
+ erlang:nif_error(undefined).
+
+%% seq_trace/2
+-spec erlang:seq_trace(P1, P2) -> seq_trace_info_returns() | {term(), term(), term(), term(), term()} when
+ P1 :: atom(),
+ P2 :: boolean() | {integer(), integer()} | integer() | [].
+seq_trace(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% seq_trace_print/1
+-spec erlang:seq_trace_print(P1) -> boolean() when
+ P1 :: term().
+seq_trace_print(_P1) ->
+ erlang:nif_error(undefined).
+
+%% seq_trace_print/2
+-spec erlang:seq_trace_print(P1, P2) -> boolean() when
+ P1 :: atom() | integer(),
+ P2 :: term().
+seq_trace_print(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% setnode/2
+-spec erlang:setnode(P1, P2) -> true when
+ P1 :: atom(),
+ P2 :: integer().
+setnode(_P1, _P2) ->
+ erlang:nif_error(undefined).
+
+%% setnode/3
+-spec erlang:setnode(P1, P2, P3) -> true when
+ P1 :: atom(),
+ P2 :: port(),
+ P3 :: {term(), term(), term(), term()}.
+setnode(_P1, _P2, _P3) ->
+ erlang:nif_error(undefined).
+
+%% size/1
+%% Shadowed by erl_bif_types: erlang:size/1
+-spec size(Item) -> non_neg_integer() when
+ Item :: tuple() | binary().
+size(_Item) ->
+ erlang:nif_error(undefined).
+
+%% spawn/3
+-spec spawn(Module, Function, Args) -> pid() when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+spawn(_Module, _Function, _Args) ->
+ erlang:nif_error(undefined).
+
+%% spawn_link/3
+-spec spawn_link(Module, Function, Args) -> pid() when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()].
+spawn_link(_Module, _Function, _Args) ->
+ erlang:nif_error(undefined).
+
+%% split_binary/2
+-spec split_binary(Bin, Pos) -> {binary(), binary()} when
+ Bin :: binary(),
+ Pos :: non_neg_integer().
+split_binary(_Bin, _Pos) ->
+ erlang:nif_error(undefined).
+
+%% start_timer/3
+-spec erlang:start_timer(Time, Dest, Msg) -> TimerRef when
+ Time :: non_neg_integer(),
+ Dest :: pid() | atom(),
+ Msg :: term(),
+ TimerRef :: reference().
+start_timer(_Time, _Dest, _Msg) ->
+ erlang:nif_error(undefined).
+
+%% suspend_process/2
+-spec erlang:suspend_process(Suspendee, OptList) -> boolean() when
+ Suspendee :: pid(),
+ OptList :: [Opt],
+ Opt :: unless_suspending | asynchronous.
+suspend_process(_Suspendee, _OptList) ->
+ erlang:nif_error(undefined).
+
+%% system_monitor/0
+-spec erlang:system_monitor() -> MonSettings when
+ MonSettings :: undefined | { MonitorPid, Options },
+ MonitorPid :: pid(),
+ Options :: [ system_monitor_option() ].
+system_monitor() ->
+ erlang:nif_error(undefined).
+
+%% system_monitor/1
+-spec erlang:system_monitor(Arg) -> MonSettings when
+ Arg :: undefined | { MonitorPid, Options },
+ MonSettings :: undefined | { MonitorPid, Options },
+ MonitorPid :: pid(),
+ Options :: [ system_monitor_option() ].
+system_monitor(_Arg) ->
+ erlang:nif_error(undefined).
+
+%% system_monitor/2
+-spec erlang:system_monitor(MonitorPid, Options) -> MonSettings when
+ MonitorPid :: pid(),
+ Options :: [ system_monitor_option() ],
+ MonSettings :: undefined | { OldMonitorPid, OldOptions },
+ OldMonitorPid :: pid(),
+ OldOptions :: [ system_monitor_option() ].
+system_monitor(_MonitorPid, _Options) ->
+ erlang:nif_error(undefined).
+
+%% system_profile/0
+-spec erlang:system_profile() -> ProfilerSettings when
+ ProfilerSettings :: undefined | { ProfilerPid, Options},
+ ProfilerPid :: pid() | port(),
+ Options :: [ system_profile_option() ].
+system_profile() ->
+ erlang:nif_error(undefined).
+
+%% system_profile/2
+-spec erlang:system_profile(ProfilerPid, Options) -> ProfilerSettings when
+ ProfilerPid :: pid() | port() | undefined,
+ Options :: [ system_profile_option() ],
+ ProfilerSettings :: undefined | { pid() | port(), [ system_profile_option() ]}.
+system_profile(_ProfilerPid, _Options) ->
+ erlang:nif_error(undefined).
+
+%% throw/1
+%% Shadowed by erl_bif_types: erlang:throw/1
+-spec throw(Any) -> no_return() when
+ Any :: term().
+throw(_Any) ->
+ erlang:nif_error(undefined).
+
+%% time/0
+-spec time() -> Time when
+ Time :: calendar:time().
+time() ->
+ erlang:nif_error(undefined).
+
+%% trace/3
+-spec erlang:trace(PidSpec, How, FlagList) -> integer() when
+ PidSpec :: pid() | existing | new | all,
+ How :: boolean(),
+ FlagList :: [trace_flag()].
+trace(_PidSpec, _How, _FlagList) ->
+ erlang:nif_error(undefined).
+
+%% trace_delivered/1
+-spec erlang:trace_delivered(Tracee) -> Ref when
+ Tracee :: pid() | all,
+ Ref :: reference().
+trace_delivered(_Tracee) ->
+ erlang:nif_error(undefined).
+
+%% trace_info/2
+-spec erlang:trace_info(PidOrFunc, Item) -> Res when
+ PidOrFunc :: pid() | new | {Module, Function, Arity} | on_load,
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity(),
+ Item :: flags | tracer | traced | match_spec | meta | meta_match_spec | call_count | call_time | all,
+ Res :: trace_info_return().
+trace_info(_PidOrFunc, _Item) ->
+ erlang:nif_error(undefined).
+
+%% trunc/1
+%% Shadowed by erl_bif_types: erlang:trunc/1
+-spec trunc(Number) -> integer() when
+ Number :: number().
+trunc(_Number) ->
+ erlang:nif_error(undefined).
+
+%% tuple_size/1
+%% Shadowed by erl_bif_types: erlang:tuple_size/1
+-spec tuple_size(Tuple) -> non_neg_integer() when
+ Tuple :: tuple().
+tuple_size(_Tuple) ->
+ erlang:nif_error(undefined).
+
+%% universaltime/0
+-spec erlang:universaltime() -> DateTime when
+ DateTime :: calendar:datetime().
+universaltime() ->
+ erlang:nif_error(undefined).
+
+%% universaltime_to_posixtime/1
+-spec erlang:universaltime_to_posixtime(P1) -> integer() when
+ P1 :: {calendar:date(), calendar:time()}.
+universaltime_to_posixtime(_P1) ->
+ erlang:nif_error(undefined).
+
+%% unlink/1
+-spec unlink(Id) -> true when
+ Id :: pid() | port().
+unlink(_Id) ->
+ erlang:nif_error(undefined).
+
+%% unregister/1
+-spec unregister(RegName) -> true when
+ RegName :: atom().
+unregister(_RegName) ->
+ erlang:nif_error(undefined).
+
+%% whereis/1
+-spec whereis(RegName) -> pid() | port() | undefined when
+ RegName :: atom().
+whereis(_RegName) ->
+ erlang:nif_error(undefined).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% More complicated native code BIFs
+%%% These are here for the types/specs, the real implementation is in the C code.
+%%% This chunk is handwritten, i.e. contains more complicated specs.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% types and specs
+
+%% Shadowed by erl_bif_types: erlang:abs/1
+-spec abs(Float) -> float() when
+ Float :: float();
+ (Int) -> non_neg_integer() when
+ Int :: integer().
+abs(_Number) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+%% Shadowed by erl_bif_types: erlang:append/2
+-spec erlang:append(List,Tail) -> maybe_improper_list() when
+ List :: [term()],
+ Tail :: term().
+append(_List,_Tail) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:element/2
+-spec element(N, Tuple) -> term() when
+ N :: pos_integer(),
+ Tuple :: tuple().
+element(_N, _Tuple) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+-spec erlang:get_module_info(Module, Item) -> ModuleInfo when
+ Module :: atom(),
+ Item :: module | imports | exports | functions | attributes | compile | native_addresses,
+ ModuleInfo :: atom() | [] | [{atom(), arity()}] | [{atom(), term()}] | [{atom(), arity(), integer()}].
+get_module_info(_Module, _Item) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:hd/1
+-spec hd(List) -> term() when
+ List :: [term(), ...].
+hd(_List) ->
+ erlang:nif_error(undefined).
+
+%% erlang:info/1 no longer exists!
+
+%% Shadowed by erl_bif_types: erlang:is_atom/1
+-spec is_atom(Term) -> boolean() when
+ Term :: term().
+is_atom(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_binary/1
+-spec is_binary(Term) -> boolean() when
+ Term :: term().
+is_binary(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_bitstring/1
+-spec is_bitstring(Term) -> boolean() when
+ Term :: term().
+is_bitstring(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_boolean/1
+-spec is_boolean(Term) -> boolean() when
+ Term :: term().
+is_boolean(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_float/1
+-spec is_float(Term) -> boolean() when
+ Term :: term().
+is_float(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_function/1
+-spec is_function(Term) -> boolean() when
+ Term :: term().
+is_function(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_function/2
+-spec is_function(Term, Arity) -> boolean() when
+ Term :: term(),
+ Arity :: arity().
+is_function(_Term, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_integer/1
+-spec is_integer(Term) -> boolean() when
+ Term :: term().
+is_integer(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_list/1
+-spec is_list(Term) -> boolean() when
+ Term :: term().
+is_list(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_number/1
+-spec is_number(Term) -> boolean() when
+ Term :: term().
+is_number(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_pid/1
+-spec is_pid(Term) -> boolean() when
+ Term :: term().
+is_pid(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_port/1
+-spec is_port(Term) -> boolean() when
+ Term :: term().
+is_port(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_record/2
+-spec is_record(Term,RecordTag) -> boolean() when
+ Term :: term(),
+ RecordTag :: atom().
+is_record(_Term,_RecordTag) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_record/3
+-spec is_record(Term,RecordTag,Size) -> boolean() when
+ Term :: term(),
+ RecordTag :: atom(),
+ Size :: non_neg_integer().
+is_record(_Term,_RecordTag,_Size) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_reference/1
+-spec is_reference(Term) -> boolean() when
+ Term :: term().
+is_reference(_Term) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:is_tuple/1
+-spec is_tuple(Term) -> boolean() when
+ Term :: term().
+is_tuple(_Term) ->
+ erlang:nif_error(undefined).
+
+-spec load_module(Module, Binary) -> {module, Module} | {error, Reason} when
+ Module :: module(),
+ Binary :: binary(),
+ Reason :: badfile | not_purged | on_load.
+load_module(Mod, Code) ->
+ case erlang:prepare_loading(Mod, Code) of
+ {error,_}=Error ->
+ Error;
+ Bin when erlang:is_binary(Bin) ->
+ case erlang:finish_loading([Bin]) of
+ ok ->
+ {module,Mod};
+ {Error,[Mod]} ->
+ {error,Error}
+ end
+ end.
+
+-spec erlang:load_nif(Path, LoadInfo) -> ok | Error when
+ Path :: string(),
+ LoadInfo :: term(),
+ Error :: {error, {Reason, Text :: string()}},
+ Reason :: load_failed | bad_lib | load | reload | upgrade | old_code.
+load_nif(_Path, _LoadInfo) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:localtime_to_universaltime(Localtime, IsDst) -> Universaltime when
+ Localtime :: calendar:datetime(),
+ Universaltime :: calendar:datetime(),
+ IsDst :: true | false | undefined.
+localtime_to_universaltime(_Localtime, _IsDst) ->
+ erlang:nif_error(undefined).
+
+%% CHECK! Why the strange very thorough specification of the error
+%% condition with disallowed arity in erl_bif_types?
+%% Not documented
+-spec erlang:make_fun(Module, Function, Arity) -> function() when
+ Module :: atom(),
+ Function :: atom(),
+ Arity :: arity().
+make_fun(_Module,_Function, _Arity) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:make_tuple/2
+-spec erlang:make_tuple(Arity, InitialValue) -> tuple() when
+ Arity :: arity(),
+ InitialValue :: term().
+make_tuple(_Arity,_InitialValue) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:make_tuple/3
+-spec erlang:make_tuple(Arity, DefaultValue, InitList) -> tuple() when
+ Arity :: arity(),
+ DefaultValue :: term(),
+ InitList :: [{Position :: pos_integer(), term()}].
+make_tuple(_Arity,_DefaultValue,_InitList) ->
+ erlang:nif_error(undefined).
+
+-spec nodes(Arg) -> Nodes when
+ Arg :: NodeType | [NodeType],
+ NodeType :: visible | hidden | connected | this | known,
+ Nodes :: [node()].
+nodes(_Arg) ->
+ erlang:nif_error(undefined).
+
+-spec open_port(PortName, PortSettings) -> port() when
+ PortName :: {spawn, Command :: string()} |
+ {spawn_driver, Command :: [byte()]} |
+ {spawn_executable, FileName :: file:name() } |
+ {fd, In :: non_neg_integer(), Out :: non_neg_integer()},
+ PortSettings :: [Opt],
+ Opt :: {packet, N :: 1 | 2 | 4}
+ | stream
+ | {line, L :: non_neg_integer()}
+ | {cd, Dir :: string()}
+ | {env, Env :: [{Name :: string(), Val :: string() | false}]}
+ | {args, [string() | binary()]}
+ | {arg0, string() | binary()}
+ | exit_status
+ | use_stdio
+ | nouse_stdio
+ | stderr_to_stdout
+ | in
+ | out
+ | binary
+ | eof
+ | {parallelism, Boolean :: boolean()}
+ | hide.
+open_port(_PortName,_PortSettings) ->
+ erlang:nif_error(undefined).
+
+-type priority_level() ::
+ low | normal | high | max.
+
+-spec process_flag(trap_exit, Boolean) -> OldBoolean when
+ Boolean :: boolean(),
+ OldBoolean :: boolean();
+ (error_handler, Module) -> OldModule when
+ Module :: atom(),
+ OldModule :: atom();
+ (min_heap_size, MinHeapSize) -> OldMinHeapSize when
+ MinHeapSize :: non_neg_integer(),
+ OldMinHeapSize :: non_neg_integer();
+ (min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when
+ MinBinVHeapSize :: non_neg_integer(),
+ OldMinBinVHeapSize :: non_neg_integer();
+ (priority, Level) -> OldLevel when
+ Level :: priority_level(),
+ OldLevel :: priority_level();
+ (save_calls, N) -> OldN when
+ N :: 0..10000,
+ OldN :: 0..10000;
+ (sensitive, Boolean) -> OldBoolean when
+ Boolean :: boolean(),
+ OldBoolean :: boolean();
+ %% Deliberately not documented.
+ ({monitor_nodes, term()}, term()) -> term();
+ (monitor_nodes, term()) -> term().
+
+process_flag(_Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+-type process_info_item() ::
+ backtrace |
+ binary |
+ catchlevel |
+ current_function |
+ current_location |
+ current_stacktrace |
+ dictionary |
+ error_handler |
+ garbage_collection |
+ group_leader |
+ heap_size |
+ initial_call |
+ links |
+ last_calls |
+ memory |
+ message_que_len |
+ messages |
+ min_heap_size |
+ min_bin_vheap_size |
+ monitored_by |
+ monitors |
+ priority |
+ reductions |
+ registered_name |
+ sequential_trace_token |
+ stack_size |
+ status |
+ suspending |
+ total_heap_size |
+ trace |
+ trap_exit.
+
+-type process_info_result_item() ::
+ {backtrace, Bin :: binary()} |
+ {binary, BinInfo :: [{non_neg_integer(),
+ non_neg_integer(),
+ non_neg_integer()}]} |
+ {catchlevel, CatchLevel :: non_neg_integer()} |
+ {current_function,
+ {Module :: module(), Function :: atom(), Arity :: arity()}} |
+ {current_location,
+ {Module :: module(), Function :: atom(), Arity :: arity(),
+ Location :: [{file, Filename :: string()} | % not a stack_item()!
+ {line, Line :: pos_integer()}]}} |
+ {current_stacktrace, Stack :: [stack_item()]} |
+ {dictionary, Dictionary :: [{Key :: term(), Value :: term()}]} |
+ {error_handler, Module :: module()} |
+ {garbage_collection, GCInfo :: [{atom(),non_neg_integer()}]} |
+ {group_leader, GroupLeader :: pid()} |
+ {heap_size, Size :: non_neg_integer()} |
+ {initial_call, mfa()} |
+ {links, PidsAndPorts :: [pid() | port()]} |
+ {last_calls, false | (Calls :: [mfa()])} |
+ {memory, Size :: non_neg_integer()} |
+ {message_que_len, MessageQueueLen :: non_neg_integer()} |
+ {messages, MessageQueue :: [term()]} |
+ {min_heap_size, MinHeapSize :: non_neg_integer()} |
+ {min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} |
+ {monitored_by, Pids :: [pid()]} |
+ {monitors,
+ Monitors :: [{process, Pid :: pid() |
+ {RegName :: atom(), Node :: node()}}]} |
+ {priority, Level :: priority_level()} |
+ {reductions, Number :: non_neg_integer()} |
+ {registered_name, Atom :: atom()} |
+ {sequential_trace_token, [] | (SequentialTraceToken :: term())} |
+ {stack_size, Size :: non_neg_integer()} |
+ {status, Status :: exiting | garbage_collecting | waiting | running | runnable | suspended} |
+ {suspending,
+ SuspendeeList :: [{Suspendee :: pid(),
+ ActiveSuspendCount :: non_neg_integer(),
+ OutstandingSuspendCount ::non_neg_integer()}]} |
+ {total_heap_size, Size :: non_neg_integer()} |
+ {trace, InternalTraceFlags :: non_neg_integer()} |
+ {trap_exit, Boolean :: boolean()}.
+
+-type stack_item() ::
+ {Module :: module(),
+ Function :: atom(),
+ Arity :: arity() | (Args :: [term()]),
+ Location :: [{file, Filename :: string()} |
+ {line, Line :: pos_integer()}]}.
+
+-spec process_info(Pid, Item) ->
+ InfoTuple | [] | undefined when
+ Pid :: pid(),
+ Item :: process_info_item(),
+ InfoTuple :: process_info_result_item();
+ (Pid, ItemList) -> InfoTupleList | [] | undefined when
+ Pid :: pid(),
+ ItemList :: [Item],
+ Item :: process_info_item(),
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: process_info_result_item().
+process_info(_Pid,_ItemSpec) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:send(Dest, Msg) -> Msg when
+ Dest :: dst(),
+ Msg :: term().
+send(_Dest,_Msg) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:send(Dest, Msg, Options) -> Res when
+ Dest :: dst(),
+ Msg :: term(),
+ Options :: [nosuspend | noconnect],
+ Res :: ok | nosuspend | noconnect.
+send(_Dest,_Msg,_Options) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+-spec erlang:seq_trace_info(send) -> {send, boolean()};
+ ('receive') -> {'receive', boolean()};
+ (print) -> {print, boolean()};
+ (timestamp) -> {timestamp, boolean()};
+ (label) -> [] | {label, non_neg_integer()};
+ (serial) -> [] | {serial, {non_neg_integer(), non_neg_integer()}}.
+seq_trace_info(_What) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:setelement/3
+-spec setelement(Index, Tuple1, Value) -> Tuple2 when
+ Index :: pos_integer(),
+ Tuple1 :: tuple(),
+ Tuple2 :: tuple(),
+ Value :: term().
+setelement(_Index, _Tuple1, _Value) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:spawn_opt({Module, Function, Args, Options}) -> pid() | {pid(), reference()} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Options :: [Option],
+ Option :: link | monitor | {priority, Level}
+ | {fullsweep_after, Number :: non_neg_integer()}
+ | {min_heap_size, Size :: non_neg_integer()}
+ | {min_bin_vheap_size, VSize :: non_neg_integer()},
+ Level :: low | normal | high.
+spawn_opt(_Tuple) ->
+ erlang:nif_error(undefined).
+
+-spec statistics(context_switches) -> {ContextSwitches,0} when
+ ContextSwitches :: non_neg_integer();
+ (exact_reductions) -> {Total_Exact_Reductions,
+ Exact_Reductions_Since_Last_Call} when
+ Total_Exact_Reductions :: non_neg_integer(),
+ Exact_Reductions_Since_Last_Call :: non_neg_integer();
+ (garbage_collection) -> {Number_of_GCs, Words_Reclaimed, 0} when
+ Number_of_GCs :: non_neg_integer(),
+ Words_Reclaimed :: non_neg_integer();
+ (io) -> {{input, Input}, {output, Output}} when
+ Input :: non_neg_integer(),
+ Output :: non_neg_integer();
+ (reductions) -> {Total_Reductions,
+ Reductions_Since_Last_Call} when
+ Total_Reductions :: non_neg_integer(),
+ Reductions_Since_Last_Call :: non_neg_integer();
+ (run_queue) -> non_neg_integer();
+ (runtime) -> {Total_Run_Time, Time_Since_Last_Call} when
+ Total_Run_Time :: non_neg_integer(),
+ Time_Since_Last_Call :: non_neg_integer();
+ (scheduler_wall_time) -> [{SchedulerId, ActiveTime, TotalTime}] | undefined when
+ SchedulerId :: pos_integer(),
+ ActiveTime :: non_neg_integer(),
+ TotalTime :: non_neg_integer();
+ (wall_clock) -> {Total_Wallclock_Time,
+ Wallclock_Time_Since_Last_Call} when
+ Total_Wallclock_Time :: non_neg_integer(),
+ Wallclock_Time_Since_Last_Call :: non_neg_integer().
+statistics(_Item) ->
+ erlang:nif_error(undefined).
+
+%% Not documented
+%% Shadowed by erl_bif_types: erlang:subtract/2
+-spec erlang:subtract([term()], [term()]) -> [term()].
+subtract(_,_) ->
+ erlang:nif_error(undefined).
+
+-type scheduler_bind_type() ::
+ 'no_node_processor_spread' |
+ 'no_node_thread_spread' |
+ 'no_spread' |
+ 'processor_spread' |
+ 'spread' |
+ 'thread_spread' |
+ 'thread_no_node_processor_spread' |
+ 'unbound'.
+
+-spec erlang:system_flag(backtrace_depth, Depth) -> OldDepth when
+ Depth :: non_neg_integer(),
+ OldDepth :: non_neg_integer();
+ (cpu_topology, CpuTopology) -> OldCpuTopology when
+ CpuTopology :: cpu_topology(),
+ OldCpuTopology :: cpu_topology();
+ (fullsweep_after, Number) -> OldNumber when
+ Number :: non_neg_integer(),
+ OldNumber :: non_neg_integer();
+ (min_heap_size, MinHeapSize) -> OldMinHeapSize when
+ MinHeapSize :: non_neg_integer(),
+ OldMinHeapSize :: non_neg_integer();
+ (min_bin_vheap_size, MinBinVHeapSize) ->
+ OldMinBinVHeapSize when
+ MinBinVHeapSize :: non_neg_integer(),
+ OldMinBinVHeapSize :: non_neg_integer();
+ (multi_scheduling, BlockState) -> OldBlockState when
+ BlockState :: block | unblock,
+ OldBlockState :: block | unblock | enabled;
+ (scheduler_bind_type, How) -> OldBindType when
+ How :: scheduler_bind_type() | default_bind,
+ OldBindType :: scheduler_bind_type();
+ (scheduler_wall_time, Boolean) -> OldBoolean when
+ Boolean :: boolean(),
+ OldBoolean :: boolean();
+ (schedulers_online, SchedulersOnline) ->
+ OldSchedulersOnline when
+ SchedulersOnline :: pos_integer(),
+ OldSchedulersOnline :: pos_integer();
+ (trace_control_word, TCW) -> OldTCW when
+ TCW :: non_neg_integer(),
+ OldTCW :: non_neg_integer();
+ %% These are deliberately not documented
+ (internal_cpu_topology, term()) -> term();
+ (sequential_tracer, pid() | port() | false) -> pid() | port() | false;
+ (1,0) -> true.
+
+system_flag(_Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+-spec term_to_binary(Term) -> ext_binary() when
+ Term :: term().
+term_to_binary(_Term) ->
+ erlang:nif_error(undefined).
+
+-spec term_to_binary(Term, Options) -> ext_binary() when
+ Term :: term(),
+ Options :: [compressed |
+ {compressed, Level :: 0..9} |
+ {minor_version, Version :: 0..1} ].
+term_to_binary(_Term, _Options) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:tl/1
+-spec tl(List) -> term() when
+ List :: [term(), ...].
+tl(_List) ->
+ erlang:nif_error(undefined).
+
+-type trace_pattern_mfa() ::
+ {atom(),atom(),arity() | '_'} | on_load.
+-type trace_match_spec() ::
+ [{[term()] | '_' ,[term()],[term()]}].
+
+-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
+ MFA :: trace_pattern_mfa(),
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean()
+ | restart
+ | pause.
+trace_pattern(_MFA, _MatchSpec) ->
+ erlang:nif_error(undefined).
+
+-type trace_pattern_flag() ::
+ global | local |
+ meta | {meta, Pid :: pid()} |
+ call_count |
+ call_time.
+
+-spec erlang:trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+ MFA :: trace_pattern_mfa(),
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean()
+ | restart
+ | pause,
+ FlagList :: [ trace_pattern_flag() ].
+trace_pattern(_MFA, _MatchSpec, _FlagList) ->
+ erlang:nif_error(undefined).
+
+%% Shadowed by erl_bif_types: erlang:tuple_to_list/1
+-spec tuple_to_list(Tuple) -> [term()] when
+ Tuple :: tuple().
+tuple_to_list(_Tuple) ->
+ erlang:nif_error(undefined).
+
+-type cpu_topology() ::
+ [LevelEntry :: level_entry()] | undefined.
+-type level_entry() ::
+ {LevelTag :: level_tag(), SubLevel :: sub_level()}
+ | {LevelTag :: level_tag(),
+ InfoList :: info_list(),
+ SubLevel :: sub_level()}.
+-type level_tag() :: core | node | processor | thread.
+-type sub_level() :: [LevelEntry :: level_entry()]
+ | (LogicalCpuId :: {logical, non_neg_integer()}).
+-type info_list() :: [].
+
+%% Note: changing the ordering number of a clause will change the docs!
+%% Shadowed by erl_bif_types: erlang:system_info/1
+-spec erlang:system_info
+ (allocated_areas) -> [ tuple() ];
+ (allocator) ->
+ {Allocator, Version, Features, Settings} when
+ Allocator :: undefined | glibc,
+ Version :: [non_neg_integer()],
+ Features :: [atom()],
+ Settings :: [{Subsystem :: atom(),
+ [{Parameter :: atom(),
+ Value :: term()}]}];
+ (alloc_util_allocators) -> [Alloc] when
+ Alloc :: atom();
+ ({allocator, Alloc}) -> [_] when %% More or less anything
+ Alloc :: atom();
+ ({allocator_sizes, Alloc}) -> [_] when %% More or less anything
+ Alloc :: atom();
+ (build_type) -> opt | debug | purify | quantify | purecov |
+ gcov | valgrind | gprof | lcnt;
+ (c_compiler_used) -> {atom(), term()};
+ (check_io) -> [_];
+ (compat_rel) -> integer();
+ (cpu_topology) -> CpuTopology when
+ CpuTopology :: cpu_topology();
+ ({cpu_topology, defined | detected | used}) -> CpuTopology when
+ CpuTopology :: cpu_topology();
+ (creation) -> integer();
+ (debug_compiled) -> boolean();
+ (dist) -> binary();
+ (dist_ctrl) -> {Node :: node(),
+ ControllingEntity :: port() | pid()};
+ (driver_version) -> string();
+ (dynamic_trace) -> none | dtrace | systemtap;
+ (dynamic_trace_probes) -> boolean();
+ (elib_malloc) -> false;
+ (dist_buf_busy_limit) -> non_neg_integer();
+ (fullsweep_after) -> {fullsweep_after, non_neg_integer()};
+ (garbage_collection) -> [{atom(), integer()}];
+ (heap_sizes) -> [non_neg_integer()];
+ (heap_type) -> private;
+ (info) -> binary();
+ (kernel_poll) -> boolean();
+ (loaded) -> binary();
+ (logical_processors |
+ logical_processors_available |
+ logical_processors_online) -> unknown | pos_integer();
+ (machine) -> string();
+ (min_heap_size) -> {min_heap_size, MinHeapSize :: pos_integer()};
+ (min_bin_vheap_size) -> {min_bin_vheap_size,
+ MinBinVHeapSize :: pos_integer()};
+ (modified_timing_level) -> integer() | undefined;
+ (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();
+ (scheduler_bind_type) -> spread |
+ processor_spread |
+ thread_spread |
+ thread_no_node_processor_spread |
+ no_node_processor_spread |
+ no_node_thread_spread |
+ no_spread |
+ unbound;
+ (scheduler_bindings) -> tuple();
+ (scheduler_id) -> SchedulerId :: pos_integer();
+ (schedulers | schedulers_online) -> pos_integer();
+ (smp_support) -> boolean();
+ (system_version) -> string();
+ (system_architecture) -> string();
+ (threads) -> boolean();
+ (thread_pool_size) -> non_neg_integer();
+ (trace_control_word) -> non_neg_integer();
+ (update_cpu_info) -> changed | unchanged;
+ (version) -> string();
+ (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8.
+system_info(_Item) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:universaltime_to_localtime(Universaltime) -> Localtime when
+ Localtime :: calendar:datetime(),
+ Universaltime :: calendar:datetime().
+universaltime_to_localtime(_Universaltime) ->
+ erlang:nif_error(undefined).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% End of native code BIFs
+%%% Actual Erlang implementation of some BIF's follow
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%%--------------------------------------------------------------------------
-spec apply(Fun, Args) -> term() when
@@ -71,6 +2169,7 @@
apply(Fun, Args) ->
erlang:apply(Fun, Args).
+%% Shadowed by erl_bif_types: erlang:apply/3
-spec apply(Module, Function, Args) -> term() when
Module :: module(),
Function :: atom(),
@@ -82,42 +2181,42 @@ apply(Mod, Name, Args) ->
-spec spawn(Fun) -> pid() when
Fun :: function().
-spawn(F) when is_function(F) ->
- spawn(erlang, apply, [F, []]);
-spawn({M,F}=MF) when is_atom(M), is_atom(F) ->
- spawn(erlang, apply, [MF, []]);
+spawn(F) when erlang:is_function(F) ->
+ erlang:spawn(erlang, apply, [F, []]);
+spawn({M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
+ erlang:spawn(erlang, apply, [MF, []]);
spawn(F) ->
erlang:error(badarg, [F]).
-spec spawn(Node, Fun) -> pid() when
Node :: node(),
Fun :: function().
-spawn(N, F) when N =:= node() ->
- spawn(F);
-spawn(N, F) when is_function(F) ->
- spawn(N, erlang, apply, [F, []]);
-spawn(N, {M,F}=MF) when is_atom(M), is_atom(F) ->
- spawn(N, erlang, apply, [MF, []]);
+spawn(N, F) when N =:= erlang:node() ->
+ erlang:spawn(F);
+spawn(N, F) when erlang:is_function(F) ->
+ erlang:spawn(N, erlang, apply, [F, []]);
+spawn(N, {M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
+ erlang:spawn(N, erlang, apply, [MF, []]);
spawn(N, F) ->
erlang:error(badarg, [N, F]).
-spec spawn_link(Fun) -> pid() when
Fun :: function().
-spawn_link(F) when is_function(F) ->
- spawn_link(erlang, apply, [F, []]);
-spawn_link({M,F}=MF) when is_atom(M), is_atom(F) ->
- spawn_link(erlang, apply, [MF, []]);
+spawn_link(F) when erlang:is_function(F) ->
+ erlang:spawn_link(erlang, apply, [F, []]);
+spawn_link({M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
+ erlang:spawn_link(erlang, apply, [MF, []]);
spawn_link(F) ->
erlang:error(badarg, [F]).
-spec spawn_link(Node, Fun) -> pid() when
Node :: node(),
Fun :: function().
-spawn_link(N, F) when N =:= node() ->
+spawn_link(N, F) when N =:= erlang:node() ->
spawn_link(F);
-spawn_link(N, F) when is_function(F) ->
+spawn_link(N, F) when erlang:is_function(F) ->
spawn_link(N, erlang, apply, [F, []]);
-spawn_link(N, {M,F}=MF) when is_atom(M), is_atom(F) ->
+spawn_link(N, {M,F}=MF) when erlang:is_atom(M), erlang:is_atom(F) ->
spawn_link(N, erlang, apply, [MF, []]);
spawn_link(N, F) ->
erlang:error(badarg, [N, F]).
@@ -126,7 +2225,7 @@ spawn_link(N, F) ->
-spec spawn_monitor(Fun) -> {pid(), reference()} when
Fun :: function().
-spawn_monitor(F) when is_function(F, 0) ->
+spawn_monitor(F) when erlang:is_function(F, 0) ->
erlang:spawn_opt({erlang,apply,[F,[]],[monitor]});
spawn_monitor(F) ->
erlang:error(badarg, [F]).
@@ -135,7 +2234,9 @@ spawn_monitor(F) ->
Module :: module(),
Function :: atom(),
Args :: [term()].
-spawn_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
+spawn_monitor(M, F, A) when erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
erlang:spawn_opt({M,F,A,[monitor]});
spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
@@ -148,9 +2249,9 @@ spawn_monitor(M, F, A) ->
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()},
Level :: low | normal | high.
-spawn_opt(F, O) when is_function(F) ->
+spawn_opt(F, O) when erlang:is_function(F) ->
spawn_opt(erlang, apply, [F, []], O);
-spawn_opt({M,F}=MF, O) when is_atom(M), is_atom(F) ->
+spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
spawn_opt(erlang, apply, [MF, []], O);
spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility
spawn_opt(M, F, A, O);
@@ -166,11 +2267,11 @@ spawn_opt(F, O) ->
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()},
Level :: low | normal | high.
-spawn_opt(N, F, O) when N =:= node() ->
+spawn_opt(N, F, O) when N =:= erlang:node() ->
spawn_opt(F, O);
-spawn_opt(N, F, O) when is_function(F) ->
+spawn_opt(N, F, O) when erlang:is_function(F) ->
spawn_opt(N, erlang, apply, [F, []], O);
-spawn_opt(N, {M,F}=MF, O) when is_atom(M), is_atom(F) ->
+spawn_opt(N, {M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
spawn_opt(N, erlang, apply, [MF, []], O);
spawn_opt(N, F, O) ->
erlang:error(badarg, [N, F, O]).
@@ -182,9 +2283,14 @@ spawn_opt(N, F, O) ->
Module :: module(),
Function :: atom(),
Args :: [term()].
-spawn(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) ->
- spawn(M,F,A);
-spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
+spawn(N,M,F,A) when N =:= erlang:node(),
+ erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
+ erlang:spawn(M,F,A);
+spawn(N,M,F,A) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
case is_well_formed_list(A) of
true ->
ok;
@@ -192,9 +2298,9 @@ spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
erlang:error(badarg, [N, M, F, A])
end,
case catch gen_server:call({net_kernel,N},
- {spawn,M,F,A,group_leader()},
+ {spawn,M,F,A,erlang:group_leader()},
infinity) of
- Pid when is_pid(Pid) ->
+ Pid when erlang:is_pid(Pid) ->
Pid;
Error ->
case remote_spawn_error(Error, {no_link, N, M, F, A, []}) of
@@ -212,9 +2318,14 @@ spawn(N,M,F,A) ->
Module :: module(),
Function :: atom(),
Args :: [term()].
-spawn_link(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) ->
- spawn_link(M,F,A);
-spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
+spawn_link(N,M,F,A) when N =:= erlang:node(),
+ erlang:is_atom(M),
+ erlang:is_atom(F),
+ erlang:is_list(A) ->
+ erlang:spawn_link(M,F,A);
+spawn_link(N,M,F,A) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
case is_well_formed_list(A) of
true ->
ok;
@@ -222,9 +2333,9 @@ spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) ->
erlang:error(badarg, [N, M, F, A])
end,
case catch gen_server:call({net_kernel,N},
- {spawn_link,M,F,A,group_leader()},
+ {spawn_link,M,F,A,erlang:group_leader()},
infinity) of
- Pid when is_pid(Pid) ->
+ Pid when erlang:is_pid(Pid) ->
Pid;
Error ->
case remote_spawn_error(Error, {link, N, M, F, A, []}) of
@@ -268,11 +2379,13 @@ spawn_opt(M, F, A, Opts) ->
| {min_heap_size, Size :: non_neg_integer()}
| {min_bin_vheap_size, VSize :: non_neg_integer()},
Level :: low | normal | high.
-spawn_opt(N, M, F, A, O) when N =:= node(),
- is_atom(M), is_atom(F), is_list(A),
- is_list(O) ->
+spawn_opt(N, M, F, A, O) when N =:= erlang:node(),
+ erlang:is_atom(M), erlang:is_atom(F),
+ erlang:is_list(A), erlang:is_list(O) ->
spawn_opt(M, F, A, O);
-spawn_opt(N, M, F, A, O) when is_atom(N), is_atom(M), is_atom(F) ->
+spawn_opt(N, M, F, A, O) when erlang:is_atom(N),
+ erlang:is_atom(M),
+ erlang:is_atom(F) ->
case {is_well_formed_list(A), is_well_formed_list(O)} of
{true, true} ->
ok;
@@ -291,9 +2404,9 @@ spawn_opt(N, M, F, A, O) when is_atom(N), is_atom(M), is_atom(F) ->
{no_link,[]},
O),
case catch gen_server:call({net_kernel,N},
- {spawn_opt,M,F,A,NO,L,group_leader()},
+ {spawn_opt,M,F,A,NO,L,erlang:group_leader()},
infinity) of
- Pid when is_pid(Pid) ->
+ Pid when erlang:is_pid(Pid) ->
Pid;
Error ->
case remote_spawn_error(Error, {L, N, M, F, A, NO}) of
@@ -331,11 +2444,11 @@ is_well_formed_list(_) ->
crasher(Node,Mod,Fun,Args,[],Reason) ->
error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n",
[Mod,Fun,Args,Node]),
- exit(Reason);
+ erlang:exit(Reason);
crasher(Node,Mod,Fun,Args,Opts,Reason) ->
error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n",
[Mod,Fun,Args,Opts,Node]),
- exit(Reason).
+ erlang:exit(Reason).
-spec erlang:yield() -> 'true'.
yield() ->
@@ -356,7 +2469,7 @@ disconnect_node(Node) ->
Item :: arity | env | index | name
| module | new_index | new_uniq | pid | type | uniq,
Info :: term().
-fun_info(Fun) when is_function(Fun) ->
+fun_info(Fun) when erlang:is_function(Fun) ->
Keys = [type,env,arity,name,uniq,index,new_uniq,new_index,module,pid],
fun_info_1(Keys, Fun, []).
@@ -388,11 +2501,9 @@ send_nosuspend(Pid, Msg, Opts) ->
_ -> false
end.
--spec erlang:localtime_to_universaltime({Date1, Time1}) -> {Date2, Time2} when
- Date1 :: calendar:date(),
- Date2 :: calendar:date(),
- Time1 :: calendar:time(),
- Time2 :: calendar:time().
+-spec erlang:localtime_to_universaltime(Localtime) -> Universaltime when
+ Localtime :: calendar:datetime(),
+ Universaltime :: calendar:datetime().
localtime_to_universaltime(Localtime) ->
erlang:localtime_to_universaltime(Localtime, undefined).
@@ -406,31 +2517,241 @@ 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
%% reactivate the command.
%%
--spec dlink(pid() | port()) -> 'true'.
+-spec erlang:dlink(pid() | port()) -> 'true'.
dlink(Pid) ->
- case net_kernel:connect(node(Pid)) of
- true -> link(Pid);
- false -> erlang:dist_exit(self(), noconnection, Pid), true
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:link(Pid);
+ false -> erlang:dist_exit(erlang:self(), noconnection, Pid), true
end.
%% Can this ever happen?
--spec dunlink(identifier()) -> 'true'.
+-spec erlang:dunlink(identifier()) -> 'true'.
dunlink(Pid) ->
- case net_kernel:connect(node(Pid)) of
- true -> unlink(Pid);
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:unlink(Pid);
false -> true
end.
dmonitor_node(Node, Flag, []) ->
case net_kernel:connect(Node) of
true -> erlang:monitor_node(Node, Flag, []);
- false -> self() ! {nodedown, Node}, true
+ false -> erlang:self() ! {nodedown, Node}, true
end;
dmonitor_node(Node, Flag, Opts) ->
@@ -438,31 +2759,31 @@ dmonitor_node(Node, Flag, Opts) ->
true ->
case net_kernel:passive_cnct(Node) of
true -> erlang:monitor_node(Node, Flag, Opts);
- false -> self() ! {nodedown, Node}, true
+ false -> erlang:self() ! {nodedown, Node}, true
end;
_ ->
dmonitor_node(Node,Flag,[])
end.
dgroup_leader(Leader, Pid) ->
- case net_kernel:connect(node(Pid)) of
- true -> group_leader(Leader, Pid);
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:group_leader(Leader, Pid);
false -> true %% bad arg ?
end.
dexit(Pid, Reason) ->
- case net_kernel:connect(node(Pid)) of
- true -> exit(Pid, Reason);
+ case net_kernel:connect(erlang:node(Pid)) of
+ true -> erlang:exit(Pid, Reason);
false -> true
end.
-dsend(Pid, Msg) when is_pid(Pid) ->
- case net_kernel:connect(node(Pid)) of
+dsend(Pid, Msg) when erlang:is_pid(Pid) ->
+ case net_kernel:connect(erlang:node(Pid)) of
true -> erlang:send(Pid, Msg);
false -> Msg
end;
-dsend(Port, Msg) when is_port(Port) ->
- case net_kernel:connect(node(Port)) of
+dsend(Port, Msg) when erlang:is_port(Port) ->
+ case net_kernel:connect(erlang:node(Port)) of
true -> erlang:send(Port, Msg);
false -> Msg
end;
@@ -473,13 +2794,13 @@ dsend({Name, Node}, Msg) ->
ignored -> Msg % Not distributed.
end.
-dsend(Pid, Msg, Opts) when is_pid(Pid) ->
- case net_kernel:connect(node(Pid)) of
+dsend(Pid, Msg, Opts) when erlang:is_pid(Pid) ->
+ case net_kernel:connect(erlang:node(Pid)) of
true -> erlang:send(Pid, Msg, Opts);
false -> ok
end;
-dsend(Port, Msg, Opts) when is_port(Port) ->
- case net_kernel:connect(node(Port)) of
+dsend(Port, Msg, Opts) when erlang:is_port(Port) ->
+ case net_kernel:connect(erlang:node(Port)) of
true -> erlang:send(Port, Msg, Opts);
false -> ok
end;
@@ -490,21 +2811,23 @@ dsend({Name, Node}, Msg, Opts) ->
ignored -> ok % Not distributed.
end.
--spec dmonitor_p('process', pid() | {atom(),atom()}) -> reference().
+-spec erlang:dmonitor_p('process', pid() | {atom(),atom()}) -> reference().
dmonitor_p(process, ProcSpec) ->
%% ProcSpec = pid() | {atom(),atom()}
%% ProcSpec CANNOT be an atom because a locally registered process
%% is never handled here.
Node = case ProcSpec of
- {S,N} when is_atom(S), is_atom(N), N =/= node() -> N;
- _ when is_pid(ProcSpec) -> node(ProcSpec)
+ {S,N} when erlang:is_atom(S),
+ erlang:is_atom(N),
+ N =/= erlang:node() -> N;
+ _ when erlang:is_pid(ProcSpec) -> erlang:node(ProcSpec)
end,
case net_kernel:connect(Node) of
true ->
erlang:monitor(process, ProcSpec);
false ->
- Ref = make_ref(),
- self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
+ Ref = erlang:make_ref(),
+ erlang:self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
Ref
end.
@@ -512,7 +2835,7 @@ dmonitor_p(process, ProcSpec) ->
%% Trap function used when modified timing has been enabled.
%%
--spec delay_trap(Result, timeout()) -> Result.
+-spec erlang:delay_trap(Result, timeout()) -> Result.
delay_trap(Result, 0) -> erlang:yield(), Result;
delay_trap(Result, Timeout) -> receive after Timeout -> Result end.
@@ -526,12 +2849,12 @@ delay_trap(Result, Timeout) -> receive after Timeout -> Result end.
-spec erlang:set_cookie(Node, Cookie) -> true when
Node :: node(),
Cookie :: atom().
-set_cookie(Node, C) when Node =/= nonode@nohost, is_atom(Node) ->
- case is_atom(C) of
+set_cookie(Node, C) when Node =/= nonode@nohost, erlang:is_atom(Node) ->
+ case erlang:is_atom(C) of
true ->
auth:set_cookie(Node, C);
false ->
- error(badarg)
+ erlang:error(badarg)
end.
-spec erlang:get_cookie() -> Cookie | nocookie when
@@ -545,7 +2868,8 @@ get_cookie() ->
integer_to_list(I, 10) ->
erlang:integer_to_list(I);
integer_to_list(I, Base)
- when is_integer(I), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 ->
+ when erlang:is_integer(I), erlang:is_integer(Base),
+ Base >= 2, Base =< 1+$Z-$A+10 ->
if I < 0 ->
[$-|integer_to_list(-I, Base, [])];
true ->
@@ -568,57 +2892,40 @@ integer_to_list(I0, Base, R0) ->
integer_to_list(I1, Base, R1)
end.
-
--spec list_to_integer(String, Base) -> integer() when
- String :: string(),
+-spec integer_to_binary(Integer, Base) -> binary() when
+ Integer :: integer(),
Base :: 2..36.
-list_to_integer(L, 10) ->
- erlang:list_to_integer(L);
-list_to_integer(L, Base)
- when is_list(L), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 ->
- case list_to_integer_sign(L, Base) of
- I when is_integer(I) ->
- I;
- Fault ->
- erlang:error(Fault, [L,Base])
- end;
-list_to_integer(L, Base) ->
- erlang:error(badarg, [L,Base]).
-
-list_to_integer_sign([$-|[_|_]=L], Base) ->
- case list_to_integer(L, Base, 0) of
- I when is_integer(I) ->
- -I;
- I ->
- I
+integer_to_binary(I, 10) ->
+ erlang:integer_to_binary(I);
+integer_to_binary(I, Base)
+ when erlang:is_integer(I), erlang:is_integer(Base),
+ Base >= 2, Base =< 1+$Z-$A+10 ->
+ if I < 0 ->
+ <<"$-",(integer_to_binary(-I, Base, []))/binary>>;
+ true ->
+ integer_to_binary(I, Base, <<>>)
end;
-list_to_integer_sign([$+|[_|_]=L], Base) ->
- list_to_integer(L, Base, 0);
-list_to_integer_sign([_|_]=L, Base) ->
- list_to_integer(L, Base, 0);
-list_to_integer_sign(_, _) ->
- badarg.
-
-list_to_integer([D|L], Base, I)
- when is_integer(D), D >= $0, D =< $9, D < Base+$0 ->
- list_to_integer(L, Base, I*Base + D-$0);
-list_to_integer([D|L], Base, I)
- when is_integer(D), D >= $A, D < Base+$A-10 ->
- list_to_integer(L, Base, I*Base + D-$A+10);
-list_to_integer([D|L], Base, I)
- when is_integer(D), D >= $a, D < Base+$a-10 ->
- list_to_integer(L, Base, I*Base + D-$a+10);
-list_to_integer([], _, I) ->
- I;
-list_to_integer(_, _, _) ->
- badarg.
+integer_to_binary(I, Base) ->
+ erlang:error(badarg, [I, Base]).
+
+integer_to_binary(0, _Base, R0) ->
+ R0;
+integer_to_binary(I0, Base, R0) ->
+ D = I0 rem Base,
+ I1 = I0 div Base,
+ if D >= 10 ->
+ integer_to_binary(I1,Base,<<(D-10+$A),R0/binary>>);
+ true ->
+ integer_to_binary(I1,Base,<<(D+$0),R0/binary>>)
+ end.
%% erlang:flush_monitor_message/2 is for internal use only!
%%
%% erlang:demonitor(Ref, [flush]) traps to
%% erlang:flush_monitor_message(Ref, Res) when
%% it needs to flush a monitor message.
-flush_monitor_message(Ref, Res) when is_reference(Ref), is_atom(Res) ->
+flush_monitor_message(Ref, Res) when erlang:is_reference(Ref),
+ erlang:is_atom(Res) ->
receive {_, Ref, _, _, _} -> ok after 0 -> ok end,
Res.
@@ -644,7 +2951,7 @@ set_cpu_topology(CpuTopology) ->
cput_e2i_clvl({logical, _}, _PLvl) ->
#cpu.logical;
cput_e2i_clvl([E | _], PLvl) ->
- case element(1, E) of
+ case erlang:element(1, E) of
node -> case PLvl of
0 -> #cpu.node;
#cpu.processor -> #cpu.processor_node
@@ -713,7 +3020,7 @@ cput_e2i({thread, TL}, Nid, PId, #cpu{thread = T0} = CPU, PLvl, #cpu.thread,
cput_e2i(TL, Nid, PId, CPU#cpu{thread = T0+1}, #cpu.thread, Lvl, Res);
cput_e2i({logical, ID}, _Nid, PId, #cpu{processor=P, core=C, thread=T} = CPU,
PLvl, #cpu.logical, Res)
- when PLvl < #cpu.logical, is_integer(ID), 0 =< ID, ID < 65536 ->
+ when PLvl < #cpu.logical, erlang:is_integer(ID), 0 =< ID, ID < 65536 ->
[CPU#cpu{processor = case P of -1 -> PId+1; _ -> P end,
core = case C of -1 -> 0; _ -> C end,
thread = case T of -1 -> 0; _ -> T end,
@@ -738,9 +3045,9 @@ cput_i2e([], _Frst, _Lvl, _TM) ->
cput_i2e([#cpu{logical = LID}| _], _Frst, Lvl, _TM) when Lvl == #cpu.logical ->
{logical, LID};
cput_i2e([#cpu{} = I | Is], Frst, Lvl, TM) ->
- cput_i2e(element(Lvl, I), Frst, Is, [I], Lvl, TM).
+ cput_i2e(erlang:element(Lvl, I), Frst, Is, [I], Lvl, TM).
-cput_i2e(V, Frst, [I | Is], SameV, Lvl, TM) when V =:= element(Lvl, I) ->
+cput_i2e(V, Frst, [I | Is], SameV, Lvl, TM) when V =:= erlang:element(Lvl, I) ->
cput_i2e(V, Frst, Is, [I | SameV], Lvl, TM);
cput_i2e(-1, true, [], SameV, Lvl, TM) ->
cput_i2e(rvrs(SameV), true, Lvl+1, TM);
@@ -754,10 +3061,10 @@ cput_i2e(_V, _Frst, Is, SameV, Lvl, TM) ->
[{cput_i2e_tag(Lvl, TM), cput_i2e(rvrs(SameV), true, Lvl+1, TM)}
| cput_i2e(Is, false, Lvl, TM)].
-cput_i2e_tag_map() -> list_to_tuple([cpu | record_info(fields, cpu)]).
+cput_i2e_tag_map() -> erlang:list_to_tuple([cpu | record_info(fields, cpu)]).
cput_i2e_tag(Lvl, TM) ->
- case element(Lvl, TM) of processor_node -> node; Other -> Other end.
+ case erlang:element(Lvl, TM) of processor_node -> node; Other -> Other end.
rvrs([_] = L) -> L;
rvrs(Xs) -> rvrs(Xs, []).
@@ -776,7 +3083,7 @@ rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]).
%% functions in bif.c. Do not make
%% any changes to it without reading
%% the comment about them in bif.c!
--spec await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term().
+-spec erlang:await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term().
await_proc_exit(Proc, Op, Data) ->
Mon = erlang:monitor(process, Proc),
receive
@@ -814,7 +3121,9 @@ max(A, _) -> A.
%% erts_memory() in $ERL_TOP/erts/emulator/beam/erl_alloc.c
%%
--type memory_type() :: 'total' | 'processes' | 'processes_used' | 'system' | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets' | 'low' | 'maximum'.
+-type memory_type() :: 'total' | 'processes' | 'processes_used' | 'system'
+ | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets'
+ | 'low' | 'maximum'.
-define(CARRIER_ALLOCS, [mseg_alloc, sbmbc_alloc, sbmbc_low_alloc]).
-define(LOW_ALLOCS, [sbmbc_low_alloc, ll_low_alloc, std_low_alloc]).
@@ -833,7 +3142,9 @@ max(A, _) -> A.
low = 0,
maximum = 0}).
--spec memory() -> [{memory_type(), non_neg_integer()}].
+-spec erlang:memory() -> [{Type, Size}] when
+ Type :: memory_type(),
+ Size :: non_neg_integer().
memory() ->
case aa_mem_data(au_mem_data(?ALL_NEEDED_ALLOCS)) of
notsup ->
@@ -858,8 +3169,9 @@ memory() ->
{ets, Mem#memory.ets} | Tail]
end.
--spec memory(memory_type()|[memory_type()]) -> non_neg_integer() | [{memory_type(), non_neg_integer()}].
-memory(Type) when is_atom(Type) ->
+-spec erlang:memory(Type :: memory_type()) -> non_neg_integer();
+ (TypeList :: [memory_type()]) -> [{memory_type(), non_neg_integer()}].
+memory(Type) when erlang:is_atom(Type) ->
{AA, ALCU, ChkSup, BadArgZero} = need_mem_info(Type),
case get_mem_data(ChkSup, ALCU, AA) of
notsup ->
@@ -871,7 +3183,7 @@ memory(Type) when is_atom(Type) ->
_ -> Value
end
end;
-memory(Types) when is_list(Types) ->
+memory(Types) when erlang:is_list(Types) ->
{AA, ALCU, ChkSup, BadArgZeroList} = need_mem_info_list(Types),
case get_mem_data(ChkSup, ALCU, AA) of
notsup ->
@@ -1079,7 +3391,7 @@ au_mem_data(EMD, []) ->
EMD.
au_mem_data(Allocs) ->
- Ref = make_ref(),
+ Ref = erlang:make_ref(),
erlang:system_info({allocator_sizes, Ref, Allocs}),
receive_emd(Ref).
@@ -1165,11 +3477,11 @@ alloc_info(Allocs) ->
alloc_sizes(Allocs) ->
get_alloc_info(allocator_sizes, Allocs).
-get_alloc_info(Type, AAtom) when is_atom(AAtom) ->
+get_alloc_info(Type, AAtom) when erlang:is_atom(AAtom) ->
[{AAtom, Result}] = get_alloc_info(Type, [AAtom]),
Result;
-get_alloc_info(Type, AList) when is_list(AList) ->
- Ref = make_ref(),
+get_alloc_info(Type, AList) when erlang:is_list(AList) ->
+ Ref = erlang:make_ref(),
erlang:system_info({Type, Ref, AList}),
receive_allocator(Ref,
erlang:system_info(schedulers),
@@ -1206,7 +3518,7 @@ receive_allocator(Ref, N, Acc) ->
receive_allocator(Ref, N-1, insert_info(InfoList, Acc))
end.
--spec await_sched_wall_time_modifications(Ref, Result) -> boolean() when
+-spec erlang:await_sched_wall_time_modifications(Ref, Result) -> boolean() when
Ref :: reference(),
Result :: boolean().
@@ -1214,12 +3526,12 @@ await_sched_wall_time_modifications(Ref, Result) ->
sched_wall_time(Ref, erlang:system_info(schedulers)),
Result.
--spec gather_sched_wall_time_result(Ref) -> [{pos_integer(),
- non_neg_integer(),
- non_neg_integer()}] when
+-spec erlang:gather_sched_wall_time_result(Ref) -> [{pos_integer(),
+ non_neg_integer(),
+ non_neg_integer()}] when
Ref :: reference().
-gather_sched_wall_time_result(Ref) when is_reference(Ref) ->
+gather_sched_wall_time_result(Ref) when erlang:is_reference(Ref) ->
sched_wall_time(Ref, erlang:system_info(schedulers), []).
sched_wall_time(_Ref, 0) ->
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 1d1087c7f2..61d8df2428 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.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
@@ -184,12 +184,16 @@ prepare_run_args({run, [M,F|Args]}) ->
[b2a(M), b2a(F) | bs2ss(Args)].
b2a(Bin) when is_binary(Bin) ->
- list_to_atom(binary_to_list(Bin));
+ list_to_atom(b2s(Bin));
b2a(A) when is_atom(A) ->
A.
b2s(Bin) when is_binary(Bin) ->
- binary_to_list(Bin);
+ try
+ unicode:characters_to_list(Bin,file:native_name_encoding())
+ catch
+ _:_ -> binary_to_list(Bin)
+ end;
b2s(L) when is_list(L) ->
L.
@@ -1260,11 +1264,7 @@ 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) -> [try
- unicode:characters_to_list(H,file:native_name_encoding())
- catch
- _:_ -> binary_to_list(H)
- end|to_strings(T)];
+to_strings([H|T]) when is_binary(H) -> [b2s(H)|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 ec158ba970..489e8ca4ea 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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 +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]).
@@ -50,9 +50,9 @@
write_file_info/2, write_file_info/3, write_file_info/4,
make_link/2, make_link/3,
make_symlink/2, make_symlink/3,
- read_link/1, read_link/2,
+ read_link/1, read_link/2, read_link_all/1, read_link_all/2,
read_link_info/1, read_link_info/2, read_link_info/3,
- list_dir/1, list_dir/2]).
+ list_dir/1, list_dir/2, list_dir_all/1, list_dir_all/2]).
%% How to start and stop the ?DRV port.
-export([start/0, stop/1]).
@@ -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).
@@ -147,6 +148,39 @@
-define(POSIX_FADV_NOREUSE, 5).
+%%% BIFs
+
+-export([internal_name2native/1,
+ internal_native2name/1,
+ internal_normalize_utf8/1,
+ is_translatable/1]).
+
+-type prim_file_name() :: string() | unicode:unicode_binary().
+-type prim_file_name_error() :: 'error' | 'ignore' | 'warning'.
+
+-spec internal_name2native(prim_file_name()) -> binary().
+
+internal_name2native(_) ->
+ erlang:nif_error(undefined).
+
+-spec internal_native2name(binary()) ->
+ prim_file_name() | {'error',prim_file_name_error()}.
+
+internal_native2name(_) ->
+ erlang:nif_error(undefined).
+
+-spec internal_normalize_utf8(unicode:unicode_binary()) -> string().
+
+internal_normalize_utf8(_) ->
+ erlang:nif_error(undefined).
+
+-spec is_translatable(prim_file_name()) -> boolean().
+
+is_translatable(_) ->
+ erlang:nif_error(undefined).
+
+%%% End of BIFs
+
%%%-----------------------------------------------------------------
%%% Functions operating on a file through a handle. ?FD_DRV.
%%%
@@ -184,12 +218,7 @@ open(_, _) ->
%% Opens a port that can be used for open/3 or read_file/2.
%% Returns {ok, Port} | {error, Reason}.
open(Portopts) when is_list(Portopts) ->
- case drv_open(?FD_DRV, Portopts) of
- {error, _} = Error ->
- Error;
- Other ->
- Other
- end;
+ drv_open(?FD_DRV, [binary|Portopts]);
open(_) ->
{error, badarg}.
@@ -267,6 +296,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} ->
@@ -576,13 +610,7 @@ sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}},
%% Returns {ok, Port}, the Port should be used as first argument in all
%% the following functions. Returns {error, Reason} upon failure.
start() ->
- try erlang:open_port({spawn, ?DRV}, [binary]) of
- Port ->
- {ok, Port}
- catch
- error:Reason ->
- {error, Reason}
- end.
+ drv_open(?DRV, [binary]).
stop(Port) when is_port(Port) ->
try erlang:port_close(Port) of
@@ -636,7 +664,8 @@ get_cwd_int(Drive) ->
get_cwd_int({?DRV, [binary]}, Drive).
get_cwd_int(Port, Drive) ->
- drv_command(Port, <<?FILE_PWD, Drive>>).
+ drv_command(Port, <<?FILE_PWD, Drive>>,
+ fun handle_fname_response/1).
@@ -648,28 +677,17 @@ 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),
- %% 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)]).
+set_cwd_int(Port, Dir) when is_binary(Dir) ->
+ case prim_file:is_translatable(Dir) of
+ false ->
+ {error, no_translation};
+ true ->
+ drv_command(Port, [?FILE_CHDIR, pathname(Dir)])
+ end;
+set_cwd_int(Port, Dir) when is_list(Dir) ->
+ drv_command(Port, [?FILE_CHDIR, pathname(Dir)]);
+set_cwd_int(_, _) ->
+ {error, badarg}.
@@ -762,7 +780,8 @@ altname(Port, File) when is_port(Port) ->
altname_int(Port, File).
altname_int(Port, File) ->
- drv_command(Port, [?FILE_ALTNAME, pathname(File)]).
+ drv_command(Port, [?FILE_ALTNAME, pathname(File)],
+ fun handle_fname_response/1).
%% write_file_info/{2,3,4}
@@ -855,7 +874,20 @@ read_link(Port, Link) when is_port(Port) ->
read_link_int(Port, Link).
read_link_int(Port, Link) ->
- drv_command(Port, [?FILE_READLINK, pathname(Link)]).
+ drv_command(Port, [?FILE_READLINK, pathname(Link)],
+ fun handle_fname_response/1).
+
+%% read_link_all/{2,3}
+
+read_link_all(Link) ->
+ read_link_all_int({?DRV, [binary]}, Link).
+
+read_link_all(Port, Link) when is_port(Port) ->
+ read_link_all_int(Port, Link).
+
+read_link_all_int(Port, Link) ->
+ drv_command(Port, [?FILE_READLINK, pathname(Link)],
+ fun handle_fname_response_all/1).
@@ -897,20 +929,117 @@ list_dir(Port, Dir) when is_port(Port) ->
list_dir_int(Port, Dir).
list_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_READDIR, pathname(Dir)], []).
-
+ drv_command(Port, [?FILE_READDIR, pathname(Dir)],
+ fun(P) ->
+ case list_dir_response(P, []) of
+ {ok, RawNames} ->
+ try
+ {ok, list_dir_convert(RawNames)}
+ catch
+ throw:Reason ->
+ Reason
+ end;
+ Error ->
+ Error
+ end
+ end).
+
+list_dir_all(Dir) ->
+ list_dir_all_int({?DRV, [binary]}, Dir).
+
+list_dir_all(Port, Dir) when is_port(Port) ->
+ list_dir_all_int(Port, Dir).
+
+list_dir_all_int(Port, Dir) ->
+ drv_command(Port, [?FILE_READDIR, pathname(Dir)],
+ fun(P) ->
+ case list_dir_response(P, []) of
+ {ok, RawNames} ->
+ {ok, list_dir_convert_all(RawNames)};
+ Error ->
+ Error
+ end
+ end).
+
+list_dir_response(Port, Acc0) ->
+ case drv_get_response(Port) of
+ {lfname, []} ->
+ {ok, Acc0};
+ {lfname, Names} ->
+ Acc = [Name || <<L:16,Name:L/binary>> <= Names] ++ Acc0,
+ list_dir_response(Port, Acc);
+ Error ->
+ Error
+ end.
+list_dir_convert([Name|Names]) ->
+ %% If the filename cannot be converted, return error or ignore
+ %% with optional error logger warning, depending on +fn{u|a}{i|e|w}
+ %% emulator switches.
+ case prim_file:internal_native2name(Name) of
+ {error, warning} ->
+ error_logger:warning_msg("Non-unicode filename ~p ignored\n",
+ [Name]),
+ list_dir_convert(Names);
+ {error, ignore} ->
+ list_dir_convert(Names);
+ {error, error} ->
+ throw({error, {no_translation, Name}});
+ Converted when is_list(Converted) ->
+ [Converted|list_dir_convert(Names)]
+ end;
+list_dir_convert([]) -> [].
+
+list_dir_convert_all([Name|Names]) ->
+ %% If the filename cannot be converted, retain the filename as
+ %% a binary.
+ case prim_file:internal_native2name(Name) of
+ {error, _} ->
+ [Name|list_dir_convert_all(Names)];
+ Converted when is_list(Converted) ->
+ [Converted|list_dir_convert_all(Names)]
+ end;
+list_dir_convert_all([]) -> [].
%%%-----------------------------------------------------------------
%%% Functions to communicate with the driver
+handle_fname_response(Port) ->
+ case drv_get_response(Port) of
+ {fname, Name} ->
+ case prim_file:internal_native2name(Name) of
+ {error, warning} ->
+ error_logger:warning_msg("Non-unicode filename ~p "
+ "ignored when reading link\n",
+ [Name]),
+ {error, einval};
+ {error, _} ->
+ {error, einval};
+ Converted when is_list(Converted) ->
+ {ok, Converted}
+ end;
+ Error ->
+ Error
+ end.
+handle_fname_response_all(Port) ->
+ case drv_get_response(Port) of
+ {fname, Name} ->
+ case prim_file:internal_native2name(Name) of
+ {error, _} ->
+ {ok, Name};
+ Converted when is_list(Converted) ->
+ {ok, Converted}
+ end;
+ Error ->
+ Error
+ end.
%% Opens a driver port and converts any problems into {error, emfile}.
%% Returns {ok, Port} when successful.
drv_open(Driver, Portopts) ->
- try erlang:open_port({spawn, Driver}, Portopts) of
+ try erlang:open_port({spawn_driver, Driver}, Portopts) of
Port ->
{ok, Port}
catch
@@ -1015,19 +1144,10 @@ drv_command_nt(Port, Command, R) when is_port(Port) ->
%% Receives the response from a driver port.
%% Returns: {ok, ListOrBinary}|{error, Reason}
-drv_get_response(Port, R) when is_list(R) ->
- case drv_get_response(Port) of
- ok ->
- {ok, R};
- {ok, Name} ->
- drv_get_response(Port, [Name|R]);
- {append, Names} ->
- drv_get_response(Port, append(Names, R));
- Error ->
- Error
- end;
-drv_get_response(Port, _) ->
- drv_get_response(Port).
+drv_get_response(Port, undefined) ->
+ drv_get_response(Port);
+drv_get_response(Port, Fun) when is_function(Fun, 1) ->
+ Fun(Port).
drv_get_response(Port) ->
erlang:bump_reductions(100),
@@ -1047,10 +1167,6 @@ drv_get_response(Port) ->
%%%-----------------------------------------------------------------
%%% Utility functions.
-append([I | Is], R) when is_list(R) -> append(Is, [I | R]);
-append([], R) -> R.
-
-
%% Converts a list of mode atoms into a mode word for the driver.
%% Returns {Mode, Portopts, Setopts} where Portopts is a list of
%% options for erlang:open_port/2 and Setopts is a list of
@@ -1192,18 +1308,10 @@ translate_response(?FILE_RESP_N2DATA = X, L0) when is_list(L0) ->
end;
translate_response(?FILE_RESP_EOF, []) ->
eof;
-translate_response(?FILE_RESP_FNAME, []) ->
- ok;
-translate_response(?FILE_RESP_FNAME, Data) when is_binary(Data) ->
- {ok, prim_file:internal_native2name(Data)};
translate_response(?FILE_RESP_FNAME, Data) ->
- {ok, Data};
-translate_response(?FILE_RESP_LFNAME, []) ->
- ok;
-translate_response(?FILE_RESP_LFNAME, Data) when is_binary(Data) ->
- {append, transform_lfname(Data)};
+ {fname, Data};
translate_response(?FILE_RESP_LFNAME, Data) ->
- {append, transform_lfname(Data)};
+ {lfname, Data};
translate_response(?FILE_RESP_ALL_DATA, Data) ->
{ok, Data};
translate_response(X, Data) ->
@@ -1319,16 +1427,6 @@ transform_ldata(0, List, [Size | Sizes], R) ->
{Front, Rear} = lists_split(List, Size),
transform_ldata(0, Rear, Sizes, [Front | R]).
-transform_lfname(<<>>) -> [];
-transform_lfname(<<L:16, Name:L/binary, Names/binary>>) ->
- [ prim_file:internal_native2name(Name) | transform_lfname(Names)];
-transform_lfname([]) -> [];
-transform_lfname([L1,L2|Names]) ->
- L = (L1 bsl 8) bor L2,
- {Name, Rest} = lists_split(Names, L),
- [Name | transform_lfname(Rest)].
-
-
lists_split(List, 0) when is_list(List) ->
{[], List};
lists_split(List, N) when is_list(List), is_integer(N), N < 0 ->
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 91fcd3ac82..21d23159f0 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -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,7 +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(bit8) -> ?INET_LOPT_BIT8;
+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;
@@ -1116,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;
@@ -1125,7 +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_BIT8) -> bit8;
+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;
@@ -1180,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;
@@ -1220,11 +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(bit8) ->
- {enum,[{clear, ?INET_BIT8_CLEAR},
- {set, ?INET_BIT8_SET},
- {on, ?INET_BIT8_ON},
- {off, ?INET_BIT8_OFF}]};
+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 608679b016..3bf233cbb6 100644
--- a/erts/start_scripts/Makefile
+++ b/erts/start_scripts/Makefile
@@ -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 35538a5c9a..6b409e2f1b 100644
--- a/erts/test/Makefile
+++ b/erts/test/Makefile
@@ -28,7 +28,6 @@ EBIN = .
# ----------------------------------------------------
MODULES= \
- autoimport_SUITE \
erlc_SUITE \
install_SUITE \
nt_SUITE \
@@ -39,14 +38,11 @@ MODULES= \
erlexec_SUITE \
z_SUITE
-ERL_XML = $(ERL_TOP)/erts/doc/src/erlang.xml
-ERL_XML_TARGET = autoimport_SUITE_data/erlang.xml
-
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-EXTRA_FILES = install_SUITE_data/install_bin $(ERL_XML_TARGET)
+EXTRA_FILES = install_SUITE_data/install_bin
# ----------------------------------------------------
# Release directory specification
@@ -68,10 +64,6 @@ install_SUITE_data/install_bin: ../../make/install_bin
rm -f $@
cp -p $< $@
-$(ERL_XML_TARGET): $(ERL_XML)
- rm -f $@
- cp -p $< $@
-
clean:
rm -f $(TARGET_FILES) $(EXTRA_FILES)
rm -f core *~
@@ -87,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/autoimport_SUITE.erl b/erts/test/autoimport_SUITE.erl
deleted file mode 100644
index 71ed5204b1..0000000000
--- a/erts/test/autoimport_SUITE.erl
+++ /dev/null
@@ -1,196 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%% Purpose: Test erlang.xml re autoimports
--module(autoimport_SUITE).
-
--include_lib("test_server/include/test_server.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- autoimports/1]).
--define(TEST_TIMEOUT, ?t:seconds(180)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [autoimports].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(?TEST_TIMEOUT),
- [{watchdog, Dog} | Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- catch test_server:timetrap_cancel(Dog),
- ok.
-
-
-autoimports(suite) ->
- [];
-autoimports(doc) ->
- ["Check that erlang.xml documents autoimports correctly"];
-autoimports(Config) when is_list(Config) ->
- ?line XML = filename:join([?config(data_dir,Config),"erlang.xml"]),
- ?line case xml(XML) of
- [] ->
- ok;
- List ->
- lists:foreach(fun({[],F,A}) ->
- io:format("erlang:~s/~p is wrongly "
- "documented as ~s/~p~n",
- [F,A,F,A]);
- ({"erlang",F,A}) ->
- io:format("~s/~p is wrongly "
- "documented as "
- "erlang:~s/~p~n",
- [F,A,F,A])
- end,List),
- ?t:fail({wrong_autoimports,List})
- end.
-
-%%
-%% Ugly chunk of code to heuristically analyze the erlang.xml
-%% documentation file. Don't view this as an example...
-%%
-
-xml(XMLFile) ->
- {ok,File} = file:open(XMLFile,[read]),
- xskip_to_funcs(file:read_line(File),File),
- DocData = xloop(file:read_line(File),File),
- true = DocData =/= [],
- file:close(File),
- analyze(DocData).
-
-%% Skip lines up to and including the <funcs> tag.
-xskip_to_funcs({ok,Line},File) ->
- case re:run(Line,"\\<funcs\\>",[{capture,none}]) of
- nomatch ->
- xskip_to_funcs(file:read_line(File),File);
- match ->
- ok
- end.
-
-xloop({ok,Line},File) ->
- case re:run(Line,"\\<name\\>",[{capture,none}]) of
- nomatch ->
- xloop(file:read_line(File),File);
- match ->
- X = re:replace(Line,"\\).*",")",[{return,list}]),
- Y = re:replace(X,".*\\>","",[{return,list}]),
- Mod = get_module(Y),
- Rest1 = fstrip(Mod++":",Y),
- Func = get_function(Rest1),
- Rest2 = fstrip(Func++"(", Rest1),
- Argc = count_args(Rest2,1,0,false),
- [{Mod,Func,Argc} |
- xloop(file:read_line(File),File)]
- end;
-xloop(_,_) ->
- [].
-
-analyze([{[],Func,Arity}|T]) ->
- case erl_internal:bif(list_to_atom(Func),Arity) of
- true ->
- analyze(T);
- false ->
- [{[],Func,Arity} |
- analyze(T)]
- end;
-analyze([{"erlang",Func,Arity}|T]) ->
- case erl_internal:bif(list_to_atom(Func),Arity) of
- true ->
- [{"erlang",Func,Arity}|analyze(T)];
- false ->
- analyze(T)
- end;
-analyze([_|T]) ->
- analyze(T);
-analyze([]) ->
- [].
-
-
-count_args([],_,N,false) ->
- N;
-count_args([],_,N,true) ->
- N+1;
-count_args(_,0,N,true) ->
- N+1;
-count_args(_,0,N,false) ->
- N;
-count_args([Par|T],Level,N,Got) when (Par =:= 40) or
- (Par =:= 123) or (Par =:= 91) ->
- count_args(T,Level+1,N,(Level =:= 1) or Got);
-count_args([41|T],1,N,true) ->
- count_args(T,0,N+1,false);
-count_args([Par|T],Level,N, Got) when (Par =:= 41) or
- (Par =:= 125) or (Par =:= 93) ->
- count_args(T,Level-1,N,Got);
-count_args([$,|T],1,N,true) ->
- count_args(T,1,N+1,false);
-count_args([$ |T],A,B,C) ->
- count_args(T,A,B,C);
-count_args([_|T],1,N,_) ->
- count_args(T,1,N,true);
-count_args([_|T],A,B,C) ->
- count_args(T,A,B,C).
-
-fstrip([],X) ->
- X;
-fstrip(_,[]) ->
- [];
-fstrip([H|T1],[H|T2]) ->
- fstrip(T1,T2);
-fstrip(_,L) ->
- L.
-
-get_module(X) ->
- get_module(X,[]).
-get_module([],_) ->
- [];
-get_module([$:|_],Acc) ->
- lists:reverse(Acc);
-get_module([40|_],_) -> %(
- [];
-get_module([H|T],Acc) ->
- get_module(T,[H|Acc]).
-
-get_function(X) ->
- get_function(X,[]).
-get_function([],_) ->
- [];
-get_function([40|_],Acc) -> %(
- lists:reverse(Acc);
-get_function([H|T],Acc) ->
- get_function(T,[H|Acc]).
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 7df611e749..2317b4f6a4 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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;
@@ -202,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),
@@ -262,7 +273,7 @@ call_to_size_1(Config) when is_list(Config) ->
Server = ?config(xref_server, Config),
%% Applications that do not call erlang:size/1:
- Apps = [compiler,debugger,kernel,observer,parsetools,
+ Apps = [asn1,compiler,debugger,kernel,observer,parsetools,
runtime_tools,stdlib,tools,webtool],
Fs = [{erlang,size,1}],
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/vsn.mk b/erts/vsn.mk
index 34410354bc..4cdc20b550 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -17,8 +17,8 @@
# %CopyrightEnd%
#
-VSN = 5.9.3.1
-SYSTEM_VSN = R15B03
+VSN = 5.10.1
+SYSTEM_VSN = R16B
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/Makefile b/lib/Makefile
index 3753bd165b..9ddf3a0544 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
@@ -20,18 +20,17 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
ERTS_SUB_DIRECTORIES = stdlib sasl kernel compiler
-OTHER_SUB_DIRECTORIES = tools test_server common_test runtime_tools
+OTHER_SUB_DIRECTORIES = tools test_server common_test runtime_tools \
+ inets xmerl edoc erl_docgen
ifdef BUILD_ALL
OTHER_SUB_DIRECTORIES += \
snmp otp_mibs appmon erl_interface asn1 jinterface \
- wx debugger reltool gs inets \
+ wx debugger reltool gs \
ic mnesia crypto orber os_mon parsetools syntax_tools \
- pman public_key ssl toolbar tv observer odbc \
- diameter \
+ pman public_key ssl toolbar tv observer odbc diameter \
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco webtool \
- xmerl edoc eunit ssh inviso typer erl_docgen \
- percept eldap dialyzer hipe
+ eunit ssh typer percept eldap dialyzer hipe
EXTRA_FILE := $(wildcard EXTRA-APPLICATIONS)
EXTRA_APPLICATIONS := $(if $(EXTRA_FILE),$(shell cat $(EXTRA_FILE)))
endif
diff --git a/lib/appmon/doc/src/notes.xml b/lib/appmon/doc/src/notes.xml
index 04b2b0d8ba..9987ca9278 100644
--- a/lib/appmon/doc/src/notes.xml
+++ b/lib/appmon/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,31 @@
</header>
<p>This document describes the changes made to the Appmon application.</p>
+<section><title>Appmon 2.1.14.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ <item>
+ <p>
+ The backend module appmon_info.erl is moved from appmon
+ application to runtime_tools. This allows appmon to be
+ run from a remote erlang node towards a target node which
+ does not have appmon (and its dependencies) installed, as
+ long as runtime_tools is installed there.</p>
+ <p>
+ Own Id: OTP-10786</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Appmon 2.1.14.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/appmon/src/Makefile b/lib/appmon/src/Makefile
index 06e61b7cc8..c1620bc37a 100644
--- a/lib/appmon/src/Makefile
+++ b/lib/appmon/src/Makefile
@@ -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
@@ -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/appmon/vsn.mk b/lib/appmon/vsn.mk
index 0654468b42..5585c5e1c2 100644
--- a/lib/appmon/vsn.mk
+++ b/lib/appmon/vsn.mk
@@ -1 +1 @@
-APPMON_VSN = 2.1.14.1
+APPMON_VSN = 2.1.14.2
diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile
index a73d01a83a..70238335c4 100644
--- a/lib/asn1/c_src/Makefile
+++ b/lib/asn1/c_src/Makefile
@@ -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/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index 26803a25a4..b3dd312fed 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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 +27,6 @@
#define ASN1_OK 0
#define ASN1_ERROR -1
#define ASN1_COMPL_ERROR 1
-#define ASN1_MEMORY_ERROR 0
#define ASN1_DECODE_ERROR 2
#define ASN1_TAG_ERROR -3
#define ASN1_LEN_ERROR -4
@@ -851,11 +850,8 @@ int ber_decode_begin(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
};
// The remaining binary after one ASN1 segment has been decoded
- if ((rest_data = enif_make_new_binary(env, in_buf_len - ib_index, &rest))
- == NULL) {
- *term = enif_make_atom(env, "could_not_alloc_binary");
- return ASN1_ERROR;
- }
+ rest_data = enif_make_new_binary(env, in_buf_len - ib_index, &rest);
+ memcpy(rest_data, in_buf+ib_index, in_buf_len - ib_index);
*term = enif_make_tuple2(env, decoded_term, rest);
return ASN1_OK;
@@ -1222,7 +1218,33 @@ static ERL_NIF_TERM encode_per_complete(ErlNifEnv* env, int argc,
return enif_make_binary(env, &out_binary);
}
-static ERL_NIF_TERM decode_ber_tlv(ErlNifEnv* env, int argc,
+static ERL_NIF_TERM
+make_ber_error_term(ErlNifEnv* env, unsigned int return_code,
+ unsigned int err_pos)
+{
+ ERL_NIF_TERM reason;
+ ERL_NIF_TERM t;
+
+ switch (return_code) {
+ case ASN1_TAG_ERROR:
+ reason = enif_make_atom(env, "invalid_tag");
+ break;
+ case ASN1_LEN_ERROR:
+ case ASN1_INDEF_LEN_ERROR:
+ reason = enif_make_atom(env, "invalid_length");
+ break;
+ case ASN1_VALUE_ERROR:
+ reason = enif_make_atom(env, "invalid_value");
+ break;
+ default:
+ reason = enif_make_atom(env, "unknown");
+ break;
+ }
+ t = enif_make_tuple2(env, reason, enif_make_int(env, err_pos));
+ return enif_make_tuple2(env, enif_make_atom(env, "error"), t);
+}
+
+static ERL_NIF_TERM decode_ber_tlv_raw(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[]) {
ErlNifBinary in_binary;
ERL_NIF_TERM return_term;
@@ -1231,11 +1253,11 @@ static ERL_NIF_TERM decode_ber_tlv(ErlNifEnv* env, int argc,
if (!enif_inspect_iolist_as_binary(env, argv[0], &in_binary))
return enif_make_badarg(env);
- if ((return_code = ber_decode_begin(env, &return_term, in_binary.data,
- in_binary.size, &err_pos)) != ASN1_OK
- )
- return enif_make_tuple2(env, enif_make_atom(env,"error"), enif_make_tuple2(env,
- enif_make_int(env, return_code),enif_make_int(env, err_pos)));
+ return_code = ber_decode_begin(env, &return_term, in_binary.data,
+ in_binary.size, &err_pos);
+ if (return_code != ASN1_OK) {
+ return make_ber_error_term(env, return_code, err_pos);
+ }
return return_term;
}
@@ -1298,8 +1320,10 @@ static void unload(ErlNifEnv* env, void* priv_data) {
}
-static ErlNifFunc nif_funcs[] = { { "encode_per_complete", 1,
- encode_per_complete }, { "decode_ber_tlv", 1, decode_ber_tlv }, {
- "encode_ber_tlv", 1, encode_ber_tlv } };
+static ErlNifFunc nif_funcs[] = {
+ { "encode_per_complete", 1, encode_per_complete },
+ { "decode_ber_tlv_raw", 1, decode_ber_tlv_raw },
+ { "encode_ber_tlv", 1, encode_ber_tlv },
+};
ERL_NIF_INIT(asn1rt_nif, nif_funcs, load, NULL, upgrade, unload)
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index 1b399fb641..362ca9330f 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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>
@@ -330,51 +324,14 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
are several places to search in. The compiler will always
search the current directory first.</p>
</item>
- <tag><c>+compact_bit_string</c></tag>
- <item>
- <p>Gives the user the option to use a compact format of the BIT
- STRING type to save memory space, typing space and
- increase encode/decode performance, for details see
- <seealso marker="#BIT STRING">BIT STRING </seealso>type section.</p>
- </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>
@@ -388,22 +345,6 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
list or a binary. Earlier versions of the compiler ignored
those following bytes.</p>
</item>
- <tag><c>{inline,OutputName}</c></tag>
- <item>
- <p>Compiling with this option gives one output module
- containing all asn1 run-time functionality. The asn1 specs
- are provided in a target module <c>Module.set.asn</c> as
- described in the <seealso marker="asn1ct#asn1set">reference manual</seealso>. The name of the resulting module
- containing generated encode/decode functions and inlined
- run-time functions will be <c>OutputName.erl</c>. The
- merging/inlining of code is done by the <c>igor</c> module
- of <c>syntax_tools</c>. By default the functions generated
- from the first asn1 spec in the <c>.set.asn</c> are
- exported, unless a <c>{export,[atom()]}</c> or
- <c>{export_all,true}</c> option are provided. The list of
- atoms are names of choosen asn1 specs from the
- <c>.set.asn</c> file. See further examples of usage <seealso marker="#inlineExamples">below</seealso></p>
- </item>
<tag><c>+'Any Erlc Option'</c></tag>
<item>
<p>You may add any option to the Erlang compiler when
@@ -413,7 +354,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 +369,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 +383,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 +396,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
@@ -754,21 +431,8 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
any reason. Maybe you need to compile the same specs for
different encoding/decoding standards.</item>
<item>You want only one resulting module.</item>
- <item>If it is crucial to have a minimal system. Using
- <c>{inline,OutputModule}</c> includes all necessary run-time
- functions of the asn1 application, but skips those modules not
- used.</item>
- <item>Upgrading issues: Even if you upgrade your Erlang system
- you may want to continue running the old asn1 run-time
- functionality.</item>
- <item>Performance issues: If you have an asn1 system with a lot
- of cross references you may gain in performance. Measurements
- must be done for each case.</item>
</list>
- <p>You may choose either the plain multi file compilation that just
- merges the chosen asn1 specs or the <c>{inline,OutputModule}</c>
- that also includes the used asn1 run-time functionality.</p>
- <p>For both cases you need to specify which asn1 specs you will
+ <p>You need to specify which asn1 specs you will
compile in a module that must have the extension
<c>.set.asn</c>. You chose name of the module and provide the
names of the asn1 specs. For instance, if you have the specs
@@ -782,17 +446,7 @@ File3.asn </pre>
<code type="none">
~> erlc MyModule.set.asn </code>
<p>the result will be one merged module <c>MyModule.erl</c> with
- the generated code from the three asn1 specs. But if you compile
- with:</p>
- <code type="none">
-~> erlc +"{inline,'OutputModule'}" MyModule.set.asn </code>
- <p>the result will be a module <c>OutputModule.erl</c> that
- contains all encode/decode functions for the three asn1 specs and
- all used functions from the asn1 run-time modules, in this case
- <c>asn1rt_ber_bin</c>. In the former case all encode/decode
- functions are exported but in the latter only the encode/decode
- functions of the first spec in the <c>.set.asn</c>, i.e. those
- from <c>File1.asn</c>.
+ the generated code from the three asn1 specs.
</p>
</section>
@@ -868,26 +522,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>
@@ -1008,7 +642,7 @@ Day1 = saturday,
<section>
<marker id="BIT STRING"></marker>
- <title>BIT STRING </title>
+ <title>BIT STRING</title>
<p>The BIT STRING type can be used to model information which
is made up of arbitrary length series of bits. It is intended
to be used for a selection of flags, not for binary files. <br></br>
@@ -1019,56 +653,66 @@ Day1 = saturday,
Bits1 ::= BIT STRING
Bits2 ::= BIT STRING {foo(0),bar(1),gnu(2),gnome(3),punk(14)}
</pre>
- <p>There are four different notations available for representation of
+ <p>There are five different notations available for representation of
BIT STRING values in Erlang and as input to the encode functions.</p>
<list type="ordered">
- <item>A list of binary digits (0 or 1).</item>
- <item>A hexadecimal number (or an integer). This format should be
- avoided, since it is easy to misinterpret a <c>BIT STRING</c>
- value in this format. This format may be withdrawn in a future
- release.</item>
+ <item>A bitstring. By default, a BIT STRING with no
+ symbolic names will be decoded to an Erlang bitstring.</item>
<item>A list of atoms corresponding to atoms in the <c>NamedBitList</c>
- in the BIT STRING definition.</item>
+ in the BIT STRING definition. A BIT STRING with symbolic
+ names will always be decoded to this format.</item>
+ <item>A list of binary digits (0 or 1). This format is always
+ accepted as input to the encode functions. A BIT STRING will
+ be decoded to this format if <em>legacy_bit_string</em> option
+ has been given. <em>This format may be withdrawn in a future
+ release.</em>
+ </item>
<item>As <c>{Unused,Binary}</c> where <c>Unused</c> denotes how
- many trailing zero-bits 0 to 7 that are unused in the least
- significant byte in <c>Binary</c>. This notation is only
- available when the ASN.1 files have been compiled with the
- <em>+compact_bit_string</em> flag in the option list. In
- this case it is possible to use all kinds of notation when
- encoding. But the result when decoding is always in the
- compact form. The benefit from this notation is a more
- compact notation when one has large BIT STRINGs. The
- encode/decode performance is also much better in the case of
- large BIT STRINGs. </item>
+ many trailing zero-bits 0 to 7 that are unused in the least
+ significant byte in <c>Binary</c>. This format is always
+ accepted as input to the encode functions. A BIT STRING will
+ be decoded to this format if <em>compact_bit_string</em> has
+ been given. <em>This format may be withdrawn in a future
+ release.</em>
+ </item>
+ <item>A hexadecimal number (or an integer). This format should be
+ avoided, since it is easy to misinterpret a <c>BIT STRING</c>
+ value in this format. <em>This format may be withdrawn in a future
+ release.</em>
+ </item>
</list>
<note>
- <p>Note that it is advised not to use the integer format of a
- BIT STRING, see the second point above.</p>
+ <p>It is recommended to either use the bitstring format (for
+ BIT STRINGs with no symbolic names) or a list of symbolic
+ names (for BIT STRINGs with symbolic names). The other formats
+ should be avoided since they may be withdrawn in a future
+ release.
+ </p>
</note>
<pre>
-Bits1Val1 = [0,1,0,1,1],
+Bits1Val1 = &lt;&lt;0:1,1:1,0:1,1:1,1:1&gt;&gt;,
Bits1Val2 = 16#1A,
-Bits1Val3 = {3,&lt;&lt;0:1,1:1,0:1,1:1,1:1,0:3&gt;&gt;}
+Bits1Val3 = {3,&lt;&lt;0:1,1:1,0:1,1:1,1:1,0:3&gt;&gt;},
+Bits1Val4 = [0,1,0,1,1]
</pre>
- <p>Note that <c>Bits1Val1</c>, <c>Bits1Val2</c> and <c>Bits1Val3</c>
- denote the same value.</p>
+ <p>Note that <c>Bits1Val1</c>, <c>Bits1Val2</c>, <c>Bits1Val3</c>,
+ and <c>Bits1Val1</c> denote the same value.</p>
<pre>
Bits2Val1 = [gnu,punk],
-Bits2Val2 = 2#1110,
+Bits2Val2 = &lt;&lt;2#1110:4&gt;&gt;,
Bits2Val3 = [bar,gnu,gnome],
-Bits2Val4 = [0,1,1,1]
</pre>
- <p>The above <c>Bits2Val2</c>, <c>Bits2Val3</c> and <c>Bits2Val4</c>
- also all denote the same value.</p>
+ <p><c>Bits2Val2</c> and <c>Bits2Val3</c> above denote the same value.</p>
<p><c>Bits2Val1</c> is assigned symbolic values. The assignment means
that the bits corresponding to <c>gnu</c> and <c>punk</c> i.e. bits
2 and 14 are set to 1 and the rest set to 0. The symbolic values
appear as a list of values. If a named value appears, which is not
specified in the type definition, a run-time error will occur.</p>
<p>The compact notation equivalent to the empty BIT STRING is
- <c><![CDATA[{0,<<>>}]]></c>, which in the other notations is <c>[]</c> or
+ <c><![CDATA[{0,<<>>}]]></c>, which in the other notations is
+ <c><![CDATA[<<>>]]></c>, <c>[]</c>, or
<c>0</c>.</p>
- <p>BIT STRINGS may also be sub-typed with for example a SIZE
+ <p>BIT STRINGS may also be sub-typed with, for example, a SIZE
specification:</p>
<pre>
Bits3 ::= BIT STRING (SIZE(0..31)) </pre>
@@ -1149,7 +793,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 +818,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 +931,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 +1375,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..f04bac9fec 100644
--- a/lib/asn1/doc/src/asn1ct.xml
+++ b/lib/asn1/doc/src/asn1ct.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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,10 @@
<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 |
+ legacy_bit_string |
+ noobj | {n2n, EnumTypeName} |{outdir, Dir} | {i, IncludeDir} |
+ asn1config | undec_rest |
{macro_name_prefix, Prefix} | {record_name_prefix, Prefix} | verbose | warnings_as_errors</v>
<v>OldOption = ber | per</v>
<v>Reason = term()</v>
@@ -107,12 +121,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 +132,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 +145,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
@@ -154,22 +155,26 @@ File3.asn </pre>
<tag><c>compact_bit_string</c></tag>
<item>
<p>
- Makes it possible to use a compact notation for values
- of the BIT STRING type in Erlang. The notation:
+ The BIT STRING type will be decoded to the "compact notation".
+ <em>This option is not recommended for new code.</em>
+ </p>
+ <p>For details see
+ <seealso marker="asn1_ug#BIT STRING">
+ BIT STRING type section in the Users Guide
+ </seealso>.
</p>
- <pre>
-BitString = {Unused, Binary},
-Unused = integer(),
-Binary = binary()
- </pre>
+ </item>
+ <tag><c>legacy_bit_string</c></tag>
+ <item>
<p>
- <c>Unused</c> must be a number in the range 0 to 7. It
- tells how many bits in the least significant byte in
- <c>Binary</c> that is unused.
- For details see
+ The BIT STRING type will be decoded to the legacy
+ format, i.e. a list of zeroes and ones.
+ <em>This option is not recommended for new code.</em>
+ </p>
+ <p>For details see
<seealso marker="asn1_ug#BIT STRING">
- BIT STRING type section in users guide
- </seealso>.
+ BIT STRING type section in the Users Guide
+ </seealso>.
</p>
</item>
<tag><c>{n2n, EnumTypeName}</c></tag>
@@ -206,28 +211,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
@@ -255,32 +238,6 @@ Binary = binary()
list or a binary. Earlier versions of the compiler ignored
those following bytes.</p>
</item>
- <tag><c>{inline, OutputName}</c></tag>
- <item>
- <p>Compiling with this option gives one output module
- containing all asn1 run-time functionality. The asn1 specs
- are provided in a target module Module.set.asn as described
- <seealso marker="#asn1set">above</seealso>. The name of the
- resulting module containing generated encode/decode functions
- and in-lined run-time functions will be
- <c>OutputName.erl</c>. The merging/in-lining of code is done
- by the <c>igor</c> module of <c>syntax_tools</c>. By default
- the functions generated from the first asn1 spec in the
- <c>.set.asn</c> are exported, unless a
- <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>
- <p>It is also possible to use the sole argument <c>inline</c>.
- It is as <c>{inline, OutputName}</c>, but the output file gets the
- default name of the source <c>.set.asn</c> file.</p>
- </item>
<tag><c>{macro_name_prefix, Prefix}</c></tag>
<item>
<p>All macro names generated by the compiler are prefixed with
@@ -327,13 +284,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 +304,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..2b1b108ab1 100644
--- a/lib/asn1/doc/src/asn1rt.xml
+++ b/lib/asn1/doc/src/asn1rt.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 de58aef0e2..e619408591 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,110 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 2.0.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The generated decoder for the 'per' and 'uper'
+ backends did not correctly decode ENUMERATEDs with a
+ single value.</p>
+ <p>The generated encoder for the 'per' and 'uper'
+ backends generated an empty binary for a top-level type
+ that did not need to be encoded (such as an ENUMERATED
+ with a single value). The correct result should be a
+ binary containing a 0 byte.</p>
+ <p>
+ Own Id: OTP-10916 Aux Id: seq12270 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Asn1 2.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed broken table constraints within a SET OF or
+ SEQUENCE OF for the BER backend.</p>
+ <p>
+ Own Id: OTP-10853 Aux Id: seq12245 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Asn1 2.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Encoding SEQUENCEs with multiple extension addition
+ groups with optional values could fail (depending both on
+ the specification and whether all values were provided).</p>
+ <p>
+ Own Id: OTP-10664</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The options for the ASN.1 compiler has been
+ drastically simplified. The backend is chosen by using
+ <c>ber</c>, <c>per</c>, or <c>uper</c>. The options
+ <c>optimize</c>, <c>nif</c>, and <c>driver</c> are no
+ longer needed. The old options will still work, but will
+ issue a warning.</p>
+ <p>Another change is that generated <c>encode/2</c>
+ function will always return a binary (some backends used
+ to return an iolist).</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10410 Aux Id: kunagi-254 [165] </p>
+ </item>
+ <item>
+ <p>
+ The ASN.1 compiler generates faster decode functions for
+ PER and UPER. Some minor improvements have also been made
+ for PER/UPER encoding, and to the BER backend.</p>
+ <p>
+ Own Id: OTP-10519 Aux Id: kunagi-322 [233] </p>
+ </item>
+ <item>
+ <p>The ASN.1 compiler will now always include necessary
+ run-time functions in the generated Erlang modules
+ (except for <c>asn1rt_nif</c> which is still neeeded). If
+ the option '<c>inline</c>' is used the ASN.1 compiler
+ will generate a warning. But if
+ '<c>{inline,OutputFile}</c>' is use, the ASN.1 compiler
+ will refuse to compile the file. (Use a <c>.set.asn</c>
+ file if you need to remove the output file.)</p>
+ <p>The '<c>BIT STRING</c>' type will now be decoded as
+ Erlang bitstrings by default. Use the new
+ <c>legacy_bit_string</c> option to encode as lists of
+ ones and zeroes. (The <c>compact_bit_string</c> option
+ still works as before.)</p>
+ <p>Open types are now always returned as binaries (when
+ there is no information allowing them to be decoded).</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10588 Aux Id: kunagi-341 [252] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 1.8.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -512,7 +616,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/.gitignore b/lib/asn1/src/.gitignore
new file mode 100644
index 0000000000..621f8f3623
--- /dev/null
+++ b/lib/asn1/src/.gitignore
@@ -0,0 +1,2 @@
+/asn1ct_rtt.erl
+/asn1ct_eval_*.erl
diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile
index 4bd49aa93b..9607799401 100644
--- a/lib/asn1/src/Makefile
+++ b/lib/asn1/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -42,42 +42,35 @@ RELSYSDIR = $(RELEASE_PATH)/lib/asn1-$(VSN)
#
EBIN = ../ebin
+
+EVAL_CT_MODULES = asn1ct_eval_ext \
+ asn1ct_eval_per \
+ asn1ct_eval_uper
+
CT_MODULES= \
asn1ct \
asn1ct_check \
asn1_db \
asn1ct_pretty_format \
+ asn1ct_func \
asn1ct_gen \
asn1ct_gen_per \
asn1ct_gen_per_rt2ct \
asn1ct_name \
asn1ct_constructed_per \
- asn1ct_constructed_ber \
- asn1ct_gen_ber \
asn1ct_constructed_ber_bin_v2 \
asn1ct_gen_ber_bin_v2 \
+ asn1ct_imm \
+ asn1ct_rtt \
asn1ct_value \
asn1ct_tok \
asn1ct_parser2 \
- asn1ct_table
+ asn1ct_table \
+ $(EVAL_CT_MODULES)
RT_MODULES= \
asn1rt \
- asn1rt_per_bin \
- asn1rt_ber_bin \
- asn1rt_ber_bin_v2 \
- asn1rt_per_bin_rt2ct \
- asn1rt_uper_bin \
- asn1rt_check \
asn1rt_nif
-# asn1_sup \
-# asn1_app \
-# asn1_server
-
-
-# the rt module to use is defined in asn1_records.hrl
-# and must be updated when an incompatible change is done in the rt modules
-
MODULES= $(CT_MODULES) $(RT_MODULES)
@@ -112,7 +105,7 @@ endif
ERL_COMPILE_FLAGS += \
-I$(ERL_TOP)/lib/stdlib \
- +warn_unused_vars
+ -Werror
YRL_FLAGS =
@@ -138,13 +131,20 @@ 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)\" $<
+
+$(EBIN)/asn1ct_func.$(EMULATOR): asn1ct_func.erl
+ $(ERLC) -o$(EBIN) $(ERL_COMPILE_FLAGS) -I../rt_templates $<
+
+asn1ct_eval_%.erl: asn1ct_eval_%.funcs
+ erl -pa $(EBIN) -noshell -noinput \
+ -run prepare_templates gen_asn1ct_eval $< >$@
$(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 +166,61 @@ release_spec: opt
release_docs_spec:
+#
+# Run-time library template files.
+#
+RT_TEMPLATES = asn1rtt_check \
+ asn1rtt_ext \
+ asn1rtt_per_common \
+ asn1rtt_real_common \
+ asn1rtt_ber \
+ asn1rtt_per \
+ asn1rtt_uper
+RT_TEMPLATES_ERL = $(RT_TEMPLATES:%=%.erl)
+RT_TEMPLATES_TARGET = $(RT_TEMPLATES:%=%.$(EMULATOR))
+asn1ct_rtt.erl: prepare_templates.$(EMULATOR) $(RT_TEMPLATES_TARGET)
+ erl -noshell -noinput -run prepare_templates gen_asn1ct_rtt \
+ $(RT_TEMPLATES_TARGET) >asn1ct_rtt.erl
+prepare_templates.$(EMULATOR): prepare_templates.erl
+ erlc prepare_templates.erl
+asn1rtt_%.$(EMULATOR): asn1rtt_%.erl
+ erlc +debug_info $<
+$(EVAL_CT_MODULES:%=%.erl): prepare_templates.$(EMULATOR) \
+ $(EBIN)/asn1ct_rtt.$(EMULATOR)
+#
+# 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_func.beam: asn1ct_func.erl
+$(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..f2ee8deb75 100644
--- a/lib/asn1/src/asn1.app.src
+++ b/lib/asn1/src/asn1.app.src
@@ -3,12 +3,6 @@
{vsn, "%VSN%"},
{modules, [
asn1rt,
- asn1rt_per_bin,
- asn1rt_per_bin_rt2ct,
- asn1rt_uper_bin,
- asn1rt_ber_bin,
- asn1rt_ber_bin_v2,
- asn1rt_check,
asn1rt_nif
]},
{registered, [
diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl
index 59a9acb7e7..16d14c2e7b 100644
--- a/lib/asn1/src/asn1_records.hrl
+++ b/lib/asn1/src/asn1_records.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. 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
@@ -24,12 +24,6 @@
-define(dbg(Fmt, Args), no_debug).
-endif.
--define('RT_BER_BIN',"asn1rt_ber_bin").
--define('RT_PER_BIN',"asn1rt_per_bin").
-
-%% Some encoding are common for BER and PER. Shared code are in RT_COMMON
--define('RT_COMMON',asn1rt_ber_bin).
-
-define('COMPLETE_ENCODE',1).
-define('TLV_DECODE',2).
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 8e971a1c76..770b92cbc3 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -41,6 +41,7 @@
maybe_rename_function/3,latest_sindex/0,current_sindex/0,
set_current_sindex/1,next_sindex/0,maybe_saved_sindex/2,
parse_and_save/2,verbose/3,warning/3,warning/4,error/3]).
+-export([get_bit_string_format/0]).
-include("asn1_records.hrl").
-include_lib("stdlib/include/erl_compile.hrl").
@@ -85,17 +86,15 @@
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),
- Options2 = includes(File,Options1),
- Includes = strip_includes(Options2),
- in_process(fun() -> compile_proc(File, Includes, Options2) end).
+compile(File, Options0) when is_list(Options0) ->
+ try translate_options(Options0) of
+ Options1 ->
+ Options2 = includes(File,Options1),
+ Includes = strip_includes(Options2),
+ in_process(fun() -> compile_proc(File, Includes, Options2) end)
+ catch throw:Error ->
+ Error
+ end.
compile_proc(File, Includes, Options) ->
case input_file_type(File, Includes) of
@@ -121,63 +120,18 @@ compile1(File,Options) when is_list(Options) ->
DbFile = outfile(Base,"asn1db",Options),
Includes = [I || {i,I} <- Options],
EncodingRule = get_rule(Options),
- asn1ct_table:new(asn1_functab),
Continue1 = scan(File,Options),
Continue2 = parse(Continue1,File,Options),
Continue3 = check(Continue2,File,OutFile,Includes,EncodingRule,
DbFile,Options,[]),
Continue4 = generate(Continue3,OutFile,EncodingRule,Options),
- asn1ct_table:delete(asn1_functab),
- Ret = compile_erl(Continue4,OutFile,Options),
- case inline(is_inline(Options),
- inline_output(Options,filename:rootname(File)),
- lists:concat([OutFile,".erl"]),Options) of
- false ->
- Ret;
- InlineRet ->
- InlineRet
- end.
+ compile_erl(Continue4, OutFile, Options).
%%****************************************************************************%%
%% functions dealing with compiling of several input files to one output file %%
%%****************************************************************************%%
-%%%
-%% inline/4
-%% merges the resulting erlang modules with
-%% the appropriate run-time modules so the resulting module contains all
-%% run-time asn1 functionality. Then compiles the resulting file to beam code.
-%% The merging is done by the igor module. If this function is used in older
-%% versions than R10B the igor module, part of user contribution syntax_tools,
-%% must be provided. It is possible to pass options for the ASN1 compiler
-%% Types:
-%% Name -> atom()
-%% Modules -> [filename()]
-%% Options -> [term()]
-%% filename() -> file:filename()
-inline(true,Name,Module,Options) ->
- RTmodule = get_runtime_mod(Options),
- IgorOptions = igorify_options(remove_asn_flags(Options)),
- IgorName = list_to_atom(filename:rootname(filename:basename(Name))),
-% io:format("*****~nName: ~p~nModules: ~p~nIgorOptions: ~p~n*****~n",
-% [IgorName,Modules++RTmodule,IgorOptions]),
- verbose("Inlining modules: ~p in ~p~n",[[Module]++RTmodule,IgorName],Options),
- case catch igor:merge(IgorName,[Module]++RTmodule,[{preprocess,true},{stubs,false},{backups,false}]++IgorOptions) of
- {'EXIT',{undef,Reason}} -> %% module igor first in R10B
- error("Module igor in syntax_tools must be available:~n~p~n",
- [Reason],Options),
- {error,'no_compilation'};
- {'EXIT',Reason} ->
- error("Merge by igor module failed due to ~p~n",[Reason],Options),
- {error,'no_compilation'};
- _ ->
-%% io:format("compiling output module: ~p~n",[generated_file(Name,IgorOptions)]),
- erl_compile(generated_file(Name,IgorOptions),Options)
- end;
-inline(_,_,_,_) ->
- false.
-
%% compile_set/3 merges and compiles a number of asn1 modules
%% specified in a .set.asn file to one .erl file.
compile_set(SetBase,Files,Options)
@@ -189,7 +143,6 @@ compile_set(SetBase,Files,Options)
DbFile = outfile(SetBase,"asn1db",Options),
Includes = [I || {i,I} <- Options],
EncodingRule = get_rule(Options),
- asn1ct_table:new(asn1_functab),
ScanRes = scan_set(Files,Options),
ParseRes = parse_set(ScanRes,Options),
Result =
@@ -214,7 +167,6 @@ compile_set(SetBase,Files,Options)
{error,{'unexpected error in scan/parse phase',
lists:map(fun(X)->element(3,X) end,Other)}}
end,
- asn1ct_table:delete(asn1_functab),
Result.
check_set(ParseRes,SetBase,OutFile,Includes,EncRule,DbFile,
@@ -228,15 +180,7 @@ check_set(ParseRes,SetBase,OutFile,Includes,EncRule,DbFile,
asn1ct_table:delete([renamed_defs, original_imports, automatic_tags]),
- Ret = compile_erl(Continue2,OutFile,Options),
- case inline(is_inline(Options),
- inline_output(Options,filename:rootname(OutFile)),
- lists:concat([OutFile,".erl"]),Options) of
- false ->
- Ret;
- InlineRet ->
- InlineRet
- end.
+ compile_erl(Continue2, OutFile, Options).
%% merge_modules/2 -> returns a module record where the typeorval lists are merged,
%% the exports lists are merged, the imports lists are merged when the
@@ -823,12 +767,9 @@ check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) ->
check({false,M},_,_,_,_,_,_,_) ->
{false,M}.
-generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
+generate({true,{M,_Module,GenTOrV}}, OutFile, EncodingRule, Options) ->
debug_on(Options),
- case lists:member(compact_bit_string,Options) of
- true -> put(compact_bit_string,true);
- _ -> ok
- end,
+ setup_bit_string_format(Options),
put(encoding_options,Options),
asn1ct_table:new(check_functions),
@@ -850,10 +791,10 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
ok
end,
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
+ cleanup_bit_string_format(),
+ erase(tlv_format), % used in ber
+ erase(class_default_type),% used in ber
asn1ct_table:delete(check_functions),
case Result of
{error,_} ->
@@ -869,6 +810,26 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
generate({false,M},_,_,_) ->
{false,M}.
+setup_bit_string_format(Opts) ->
+ Format = case {lists:member(compact_bit_string, Opts),
+ lists:member(legacy_bit_string, Opts)} of
+ {false,false} -> bitstring;
+ {true,false} -> compact;
+ {false,true} -> legacy;
+ {true,true} ->
+ Message = "Contradicting options given: "
+ "compact_bit_string and legacy_bit_string",
+ exit({error,{asn1,Message}})
+ end,
+ put(bit_string_format, Format).
+
+cleanup_bit_string_format() ->
+ erase(bit_string_format).
+
+get_bit_string_format() ->
+ get(bit_string_format).
+
+
%% parse_and_save parses an asn1 spec and saves the unchecked parse
%% tree in a data base file.
%% Does not support multifile compilation files
@@ -876,14 +837,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 +1025,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|_] ->
@@ -1076,22 +1036,33 @@ get_rule(Options) ->
ber
end.
-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"]
- end,
- RtMod1++["asn1rt_check.erl","asn1rt.erl"].
-
+%% translate_options(NewOptions) -> OldOptions
+%% Translate the new option names to the old option name.
+
+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([inline|T]) ->
+ io:format("Warning: The option 'inline' is no longer needed.\n"),
+ translate_options(T);
+translate_options([{inline,_}|_]) ->
+ io:format("ERROR: The option {inline,OutputFilename} is no longer supported.\n"),
+ throw({error,{unsupported_option,inline}});
+translate_options([H|T]) ->
+ [H|translate_options(T)];
+translate_options([]) -> [].
erl_compile(OutFile,Options) ->
% io:format("Options:~n~p~n",[Options]),
@@ -1114,8 +1085,8 @@ remove_asn_flags(Options) ->
X /= get_rule(Options),
X /= optimize,
X /= compact_bit_string,
+ X /= legacy_bit_string,
X /= debug,
- X /= keyed_list,
X /= asn1config,
X /= record_name_prefix].
@@ -1125,34 +1096,10 @@ 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) ->
- case lists:keysearch(outdir,1,Options) of
- {value,{_,Dir}} ->
- Options1 = lists:keydelete(outdir,1,Options),
- [{dir,Dir}|Options1];
- _ ->
- Options
- end.
-
-generated_file(Name,Options) ->
- case lists:keysearch(dir,1,Options) of
- {value,{_,Dir}} ->
- filename:join([Dir,filename:basename(Name)]);
- _ ->
- Name
end.
debug_off(_Options) ->
- erase(asndebug),
- erase(asn_keyed_list).
+ erase(asndebug).
outfile(Base, Ext, Opts) ->
@@ -1168,13 +1115,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),
@@ -1198,21 +1138,6 @@ option_add(Option, Options, Fun) ->
strip_includes(Includes) ->
[I || {i, I} <- Includes].
-is_inline(Options) ->
- case lists:member(inline,Options) of
- true -> true;
- _ ->
- lists:keymember(inline,1,Options)
- end.
-
-inline_output(Options,Default) ->
- case [X||{inline,X}<-Options] of
- [OutputName] ->
- OutputName;
- _ ->
- Default
- end.
-
%% compile(AbsFileName, Options)
%% Compile entry point for erl_compile.
@@ -1284,12 +1209,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 +1220,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 +1320,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 +1371,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 +1413,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 59e82b7a57..e1911d8170 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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).
@@ -1263,13 +1263,13 @@ check_object_list(S,ClassRef,[ObjOrSet|Objs],Acc) ->
check_object_list(S,ClassRef,Objs,[{{no_mod,no_name},Def}|Acc]);
{'ObjectSetFromObjects',Os,FieldName} when is_tuple(Os) ->
NewSet =
- check_ObjectSetFromObjects(S,element(size(Os),Os),
+ check_ObjectSetFromObjects(S, element(tuple_size(Os), Os),
FieldName,[]),
check_object_list(S,ClassRef,Objs,NewSet++Acc);
{{'ObjectSetFromObjects',Os,FieldName},InterSection}
when is_tuple(Os) ->
NewSet =
- check_ObjectSetFromObjects(S, element(size(Os),Os),
+ check_ObjectSetFromObjects(S, element(tuple_size(Os), Os),
FieldName,InterSection),
check_object_list(S,ClassRef,Objs,NewSet++Acc);
Other ->
@@ -1570,7 +1570,7 @@ gen_incl_set(S,Fields,#typedef{typespec=#type{def=Eref}})
gen_incl_set(S,Fields,CDef);
gen_incl_set(S,Fields,ClassDef) ->
case catch get_unique_fieldname(S,ClassDef) of
- Tuple when is_tuple(Tuple), size(Tuple) =:= 3 ->
+ Tuple when tuple_size(Tuple) =:= 3 ->
false;
_ ->
gen_incl_set1(S,Fields,
@@ -1589,7 +1589,7 @@ gen_incl_set1(_,['EXTENSIONMARK'],_) ->
gen_incl_set1(_,['EXTENSIONMARK'|_],_) ->
true;
gen_incl_set1(S,[Object|Rest],CFields)->
- Fields = element(size(Object),Object),
+ Fields = element(tuple_size(Object), Object),
case gen_incl1(S,Fields,CFields) of
true ->
true;
@@ -3028,7 +3028,7 @@ is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) ->
_ -> false
end;
is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) ->
- (size(Value) =:= (NumComps + 1)) andalso (element(1,Value)=:=Name);
+ (tuple_size(Value) =:= (NumComps + 1)) andalso (element(1, Value) =:= Name);
is_record_normalized(_,_,_,_) ->
false.
@@ -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
@@ -3712,7 +3720,7 @@ maybe_open_type(S,ClassSpec=#objectclass{fields=Fs},
{typefieldreference,_} ->
case {catch get_unique_fieldname(S,#classdef{typespec=ClassSpec}),
asn1ct_gen:get_constraint(Constr,componentrelation)}of
- {Tuple,_} when is_tuple(Tuple), size(Tuple) =:= 3 ->
+ {Tuple,_} when tuple_size(Tuple) =:= 3 ->
OCFT#'ObjectClassFieldType'{fieldname=FieldNames,
type='ASN1_OPEN_TYPE'};
{_,no} ->
@@ -4159,7 +4167,7 @@ check_constraint(S,Ext) when is_record(Ext,'Externaltypereference') ->
check_constraint(S,{'SizeConstraint',{Lb,Ub}})
- when is_list(Lb);is_tuple(Lb),size(Lb)==2 ->
+ when is_list(Lb); tuple_size(Lb) =:= 2 ->
NewLb = range_check(resolv_tuple_or_list(S,Lb)),
NewUb = range_check(resolv_tuple_or_list(S,Ub)),
{'SizeConstraint',{NewLb,NewUb}};
@@ -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).
@@ -5193,7 +5217,7 @@ imported1(_Name,[]) ->
check_integer(_S,[],_C) ->
[];
check_integer(S,NamedNumberList,_C) ->
- case [X||X<-NamedNumberList,is_tuple(X),size(X)=:=2] of
+ case [X || X <- NamedNumberList, tuple_size(X) =:= 2] of
NamedNumberList ->
%% An already checked integer with NamedNumberList
NamedNumberList;
@@ -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..341a04761b 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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,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).
@@ -67,9 +66,9 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
ValName =
case Typename of
['EXTERNAL'] ->
- emit([indent(4),
- "NewVal = asn1rt_check:transform_to_EXTERNAL1990(Val),",
- nl]),
+ emit([indent(4),"NewVal = ",
+ {call,ext,transform_to_EXTERNAL1990,["Val"]},
+ com,nl]),
"NewVal";
_ ->
"Val"
@@ -163,7 +162,8 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
emit([nl," BytesSoFar = "]),
case SeqOrSet of
'SET' when (D#type.def)#'SET'.sorted == dynamic ->
- emit("asn1rt_check:dynamicsort_SET_components(["),
+ asn1ct_func:need({ber,dynamicsort_SET_components,1}),
+ emit("dynamicsort_SET_components(["),
mkvlist(asn1ct_name:all(encBytes)),
emit(["]),",nl]);
_ ->
@@ -178,8 +178,8 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
mkvplus(AllLengths)
end,
emit([",",nl]),
- emit(["?RT_BER:encode_tags(TagIn, BytesSoFar, LenSoFar)."
- ,nl]).
+ call(encode_tags, ["TagIn","BytesSoFar","LenSoFar"]),
+ emit([".",nl]).
gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:start(),
@@ -208,7 +208,8 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
_ ->
emit([{curr,tlv}," = "])
end,
- emit(["?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ call(match_tags, [{prev,tlv},"TagIn"]),
+ emit([com,nl]),
asn1ct_name:new(tlv),
asn1ct_name:new(v),
@@ -288,8 +289,9 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
"', "]),
mkvlist(asn1ct_name:all(term)),
emit(["},",nl]),
- emit([" asn1rt_check:transform_to_EXTERNAL1994",
- "(OldFormat).",nl]);
+ emit([" ",
+ {call,ext,transform_to_EXTERNAL1994,
+ ["OldFormat"]},".",nl]);
_ ->
emit([" {'",RecordName,"', "]),
mkvlist(asn1ct_name:all(term)),
@@ -372,7 +374,8 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
_ ->
emit([{curr,tlv}," = "])
end,
- emit(["?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ call(match_tags, [{prev,tlv},"TagIn"]),
+ emit([com,nl]),
asn1ct_name:new(v),
@@ -493,7 +496,8 @@ gen_encode_sof(Erules,Typename,_InnerTypename,D) when is_record(D,type) ->
emit([" {EncBytes,EncLen} = 'enc_",asn1ct_gen:list2name(Typename),
"_components'(Val",Objfun,",[],0),",nl]),
- emit([" ?RT_BER:encode_tags(TagIn, EncBytes, EncLen).",nl,nl]),
+ emit([" ",{call,ber,encode_tags,["TagIn","EncBytes","EncLen"]},
+ ".",nl,nl]),
gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont).
@@ -513,8 +517,8 @@ gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) ->
emit([" %%-------------------------------------------------",nl]),
asn1ct_name:new(tlv),
- emit([{curr,tlv},
- " = ?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ emit([{curr,tlv}," = ",
+ {call,ber,match_tags,[{prev,tlv},"TagIn"]},com,nl]),
asn1ct_name:new(v),
emit(["["]),
@@ -524,14 +528,7 @@ gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) ->
Atom when is_atom(Atom) -> Atom;
_ -> TypeNameSuffix
end,
-%% fix me
- ObjFun =
- case D#type.tablecinf of
- [{objfun,_}|_R] ->
- ", ObjFun";
- _ ->
- []
- end,
+ ObjFun = false,
gen_dec_line(Erules,TypeName,ContName,[],Cont,mandatory,ObjFun),
%% gen_dec_line_sof(Erules,Typename,ContName,Cont,ObjFun),
emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]).
@@ -552,8 +549,9 @@ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
case catch lists:member(der,get(encoding_options)) of
true when SeqOrSetOf=='SET OF'->
+ asn1ct_func:need({ber,dynamicsort_SETOF,1}),
emit([indent(3),
- "{asn1rt_check:dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
+ "{dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
_ ->
emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
end,
@@ -673,8 +671,9 @@ gen_dec_sequence_call2(Erules,TopType,{Root1,EList,Root2},_Ext,DecObjInf) ->
%% including the first mandatory element.
TagList = get_root2_taglist(Root2,[]),
emit({com,nl}),
- emit([{curr,tlv}," = ?RT_BER:skip_ExtensionAdditions(",
- {prev,tlv},", ",{asis,TagList},"),",nl]),
+ emit([{curr,tlv}," = ",
+ {call,ber,skip_ExtensionAdditions,
+ [{prev,tlv},{asis,TagList}]},com,nl]),
asn1ct_name:new(tlv),
gen_dec_sequence_call1(Erules,TopType,Root2,
length(Root1)+length(EList),noext,
@@ -806,8 +805,8 @@ gen_enc_choice1(Erules,TopType,_Tag,CompList,_Ext) ->
emit([" {EncBytes,EncLen} = case element(1,Val) of",nl]),
gen_enc_choice2(Erules,TopType,CompList),
emit([nl," end,",nl,nl]),
-
- emit(["?RT_BER:encode_tags(TagIn, EncBytes, EncLen).",nl]).
+ call(encode_tags, ["TagIn","EncBytes","EncLen"]),
+ emit([".",nl]).
gen_enc_choice2(Erules,TopType,[H1|T]) when is_record(H1,'ComponentType') ->
@@ -860,8 +859,8 @@ gen_enc_choice2(_Erules,_TopType,[]) ->
gen_dec_choice(Erules,TopType, _ChTag, CompList, Ext) ->
asn1ct_name:clear(),
asn1ct_name:new(tlv),
- emit([{curr,tlv},
- " = ?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ emit([{curr,tlv}," = ",
+ {call,ber,match_tags,[{prev,tlv},"TagIn"]},com,nl]),
asn1ct_name:new(tlv),
asn1ct_name:new(v),
emit(["case (case ",{prev,tlv},
@@ -877,8 +876,8 @@ gen_dec_choice(Erules,TopType, _ChTag, CompList, Ext) ->
emit([indent(9),"exit({error,{asn1,{invalid_choice_tag,",
{curr,else},"}}})",nl]);
_ ->
- emit([indent(9),"{asn1_ExtAlt, ?RT_BER:encode(",{curr,else},
- asn1ct_gen:nif_parameter(),")}",nl])
+ emit([indent(9),"{asn1_ExtAlt,",
+ {call,ber,ber_encode,[{curr,else}]},"}",nl])
end,
emit([indent(3),"end",nl]),
asn1ct_name:new(tag),
@@ -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
@@ -1001,29 +1018,20 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
case OptOrMand of
mandatory ->
emit(["{",{curr,encBytes},",",{curr,encLen},
- "} = "]),
- emit(["?RT_BER:encode_open_type(",{curr,tmpBytes},
- ",",{asis,Tag},")"]);
+ "} = ",
+ {call,ber,encode_open_type,
+ [{curr,tmpBytes},{asis,Tag}]},nl]);
_ ->
-% emit(["{",{next,tmpBytes},", _} = "]),
emit(["{",{next,tmpBytes},",",{curr,tmpLen},
- "} = "]),
- emit(["?RT_BER:encode_open_type(",{curr,tmpBytes},
- ",",{asis,Tag},"),",nl]),
+ "} = ",
+ {call,ber,encode_open_type,
+ [{curr,tmpBytes},{asis,Tag}]},com,nl]),
emit(IndDeep),
emit(["{",{next,tmpBytes},", ",{curr,tmpLen},"}"])
end;
Err ->
throw({asn1,{'internal error',Err}})
end;
-%% {{#'ObjectClassFieldType'{type={objectfield,PrimFieldName1,
-%% PFNList}},_},
-%% {componentrelation,_,_}} ->
-%% %% this is when the dotted list in the FieldName has more
-%% %% than one element
-%% {_LeadingAttrName,Fun} = EncObj,
-%% emit(["?RT_BER:encode_open_type(",Fun,"(",{asis,PrimFieldName1},
-%% ", ",Element,", ",{asis,PFNList},"))"]);
_ ->
case WhatKind of
{primitive,bif} ->
@@ -1221,15 +1229,11 @@ gen_dec_call({typefield,_},_,_,_Cname,Type,BytesVar,Tag,_,_,false,_) ->
asn1ct_name:new(tmptlv),
{FirstPFName,RestPFName} =
-% asn1ct_gen:get_constraint(Type#type.constraint,
-% tableconstraint_info),
(Type#type.def)#'ObjectClassFieldType'.fieldname,
emit([nl,indent(6),"begin",nl]),
-% emit([indent(9),{curr,opendec}," = ?RT_BER:decode_open_type(",
- emit([indent(9),{curr,tmptlv}," = ?RT_BER:decode_open_type(",
- BytesVar,",",{asis,Tag},asn1ct_gen:nif_parameter(),"),",nl]),
-% emit([indent(9),"{",{curr,tmptlv},",_} = ?RT_BER:decode(",
-% {curr,opendec},"),",nl]),
+ emit([indent(9),{curr,tmptlv}," = ",
+ {call,ber,decode_open_type,
+ [BytesVar,{asis,Tag}]},com,nl]),
emit([indent(9),"case (catch ObjFun(",{asis,FirstPFName},
", ",{curr,tmptlv},", ",{asis,RestPFName},
@@ -1242,8 +1246,7 @@ gen_dec_call({typefield,_},_,_,_Cname,Type,BytesVar,Tag,_,_,false,_) ->
emit([indent(9),"end",nl,indent(6),"end",nl]),
[];
gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandComp) ->
- emit(["?RT_BER:decode_open_type(",BytesVar,",",{asis,Tag},
- asn1ct_gen:nif_parameter(),")"]),
+ call(decode_open_type, [BytesVar,{asis,Tag}]),
RefedFieldName =
% asn1ct_gen:get_constraint(Type#type.constraint,
% tableconstraint_info),
@@ -1251,8 +1254,7 @@ gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandC
[{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)),
asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
gen_dec_call({objectfield,PrimFieldName,PFNList},_,_,Cname,_,BytesVar,Tag,_,_,_,OptOrMandComp) ->
- emit(["?RT_BER:decode_open_type(",BytesVar,",",{asis,Tag},
- asn1ct_gen:nif_parameter(),")"]),
+ call(decode_open_type, [BytesVar,{asis,Tag}]),
[{Cname,{PrimFieldName,PFNList},asn1ct_gen:mk_var(asn1ct_name:curr(term)),
asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand,
@@ -1284,7 +1286,6 @@ gen_dec_call1({primitive,bif},InnerType,Erules,TopType,Cname,Type,BytesVar,
asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
Tag,Type}),
asn1ct:update_gen_state(namelist,Rest),
-% emit(["?RT_BER:match_tags(",BytesVar,",",{asis,Tag},")"]);
emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
BytesVar,"}"]);
{_,{fixedtypevaluefield,_,Btype}} ->
@@ -1303,7 +1304,6 @@ gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,TopType,Cname,Type,BytesVar,
asn1ct:update_gen_state(namelist,Rest),
emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
BytesVar,"}"]);
-% emit(["?RT_BER:match_tags(",BytesVar,",",{asis,Tag},")"]);
{_,#'ObjectClassFieldType'{type=OpenType}} ->
?ASN1CT_GEN_BER:gen_dec_prim(Erules,#type{def=OpenType},
BytesVar,Tag,[],
@@ -1376,7 +1376,8 @@ gen_dec_call1(WhatKind,_,_Erules,TopType,Cname,Type,BytesVar,
parts,
[],Type}),
emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',"]),
- EmitDecFunCall("?RT_BER:match_tags"),
+ asn1ct_func:need({ber,match_tags,2}),
+ EmitDecFunCall("match_tags"),
emit("}");
_ ->
{DecFunName,_,_}=
@@ -1493,10 +1494,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) ->
@@ -1509,3 +1506,6 @@ value_match1(Value,[],Acc,Depth) ->
Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
+
+call(F, Args) ->
+ asn1ct_func:call(ber, F, Args).
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index 5de29a9c77..aa5ee18c80 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-2012. 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
@@ -32,6 +32,7 @@
%-compile(export_all).
-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/0]).
+-import(asn1ct_func, [call/3]).
%% ENCODE GENERATOR FOR SEQUENCE TYPE ** **********
@@ -66,9 +67,9 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
end,
case Typename of
['EXTERNAL'] ->
- emit({{next,val},
- " = asn1rt_check:transform_to_EXTERNAL1990(",
- {curr,val},"),",nl}),
+ emit([{next,val}," = ",
+ {call,ext,transform_to_EXTERNAL1990,
+ [{curr,val}]},com,nl]),
asn1ct_name:new(val);
_ ->
ok
@@ -76,13 +77,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) ->
@@ -90,7 +87,8 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
end,asn1ct_name:all(fixopt)),
emit({"{",{next,val},",Opt} = {",{curr,val},",[",FixOpts,"]},",nl});
{_,_,false} ->
- Fixoptcall = ",Opt} = ?RT_PER:fixoptionals(",
+ asn1ct_func:need({Erule,fixoptionals,3}),
+ Fixoptcall = ",Opt} = fixoptionals(",
emit({"{",{next,val},Fixoptcall,
{asis,Optionals},",",length(Optionals),
",",{curr,val},"),",nl})
@@ -125,8 +123,9 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
lists:foreach(ExtGroupFun,ExtGroupPosLenList)
end,
asn1ct_name:new(tmpval),
- emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},",",
- {curr,val},"),",nl]);
+ emit(["Extensions = ",
+ {call,Erule,fixextensions,[{asis,Ext},{curr,val}]},
+ com,nl]);
_ -> true
end,
EncObj =
@@ -158,7 +157,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));
@@ -195,10 +194,10 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
MaybeComma1 =
case Ext of
{ext,_Pos,NumExt2} when NumExt2 > 0 ->
- emit({"?RT_PER:setext(Extensions =/= [])"}),
+ call(Erule, setext, ["Extensions =/= []"]),
", ";
{ext,_Pos,_} ->
- emit({"?RT_PER:setext(false)"}),
+ call(Erule, setext, ["false"]),
", ";
_ ->
""
@@ -224,9 +223,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} ->
@@ -236,27 +300,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,
@@ -288,13 +344,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;
@@ -324,9 +386,10 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
"'"}),
mkvlist(asn1ct_name:all(term)),
emit({"},",nl}),
- emit({" ASN11994Format =",nl,
- " asn1rt_check:transform_to_EXTERNAL1994",
- "(OldFormat),",nl}),
+ emit([" ASN11994Format =",nl,
+ " ",
+ {call,ext,transform_to_EXTERNAL1994,
+ ["OldFormat"]},com,nl]),
emit(" {ASN11994Format,");
_ ->
emit(["{{'",RecordName,"'"]),
@@ -336,8 +399,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;
@@ -455,7 +517,7 @@ gen_encode_sof(Erule,Typename,SeqOrSetOf,D) when is_record(D,type) ->
_->
""
end,
- gen_encode_length(SizeConstraint, is_optimized(Erule)),
+ gen_encode_length(Erule, SizeConstraint),
emit({indent(3),"'enc_",asn1ct_gen:list2name(Typename),
"_components'(Val",ObjFun,", [])"}),
emit({nl,"].",nl}),
@@ -469,7 +531,7 @@ gen_encode_sof(Erule,Typename,SeqOrSetOf,D) when is_record(D,type) ->
%% Logic copied from asn1_per_bin_rt2ct:encode_constrained_number
-gen_encode_length({Lb,Ub},true) when Ub =< 65535, Lb >= 0 ->
+gen_encode_length(per, {Lb,Ub}) when Ub =< 65535, Lb >= 0 ->
Range = Ub - Lb + 1,
V2 = ["(length(Val) - ",Lb,")"],
Encode = if
@@ -496,12 +558,20 @@ gen_encode_length({Lb,Ub},true) when Ub =< 65535, Lb >= 0 ->
Range =< 65536 ->
{"[20,2,<<",V2,":16>>]"};
true ->
- {"?RT_PER:encode_length(",{asis,{Lb,Ub}},",length(Val))"}
+ {call,per,encode_length,
+ [{asis,{Lb,Ub}},"length(Val)"]}
end,
emit({nl,Encode,",",nl});
-gen_encode_length(SizeConstraint,_) ->
- emit({nl,indent(3),"?RT_PER:encode_length(",
- {asis,SizeConstraint},",length(Val)),",nl}).
+gen_encode_length(Erules, SizeConstraint) ->
+ emit([nl,indent(3),
+ case SizeConstraint of
+ undefined ->
+ {call,Erules,encode_length,["length(Val)"]};
+ _ ->
+ {call,Erules,encode_length,
+ [{asis,SizeConstraint},"length(Val)"]}
+ end,
+ com,nl]).
gen_decode_sof(Erules,Typename,SeqOrSetOf,D) when is_record(D,type) ->
asn1ct_name:start(),
@@ -519,10 +589,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}->
@@ -531,40 +601,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} =
@@ -586,8 +629,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");
@@ -617,16 +659,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} ->
@@ -650,7 +691,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}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -721,10 +762,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),
@@ -892,7 +948,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}),
@@ -912,7 +968,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}),
@@ -925,7 +981,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}),
@@ -945,11 +1001,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}} ->
@@ -960,7 +1015,9 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
case Ext of
{ext,_Ep1,_} ->
- emit(["?RT_PER:encode_open_type(dummy,?RT_PER:complete("]);
+ asn1ct_func:need({Erule,encode_open_type,1}),
+ asn1ct_func:need({Erule,complete,1}),
+ emit(["encode_open_type(complete("]);
_ -> true
end,
@@ -972,7 +1029,9 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
{notype,T} ->
throw({error,{notype,type_from_object,T}});
{Name,RestFieldNames} when is_atom(Name) ->
- emit({"?RT_PER:encode_open_type([],?RT_PER:complete(",nl}),
+ asn1ct_func:need({Erule,complete,1}),
+ asn1ct_func:need({Erule,encode_open_type,1}),
+ emit({"encode_open_type(complete(",nl}),
emit({" ",Fun,"(",{asis,Name},", ",
Element,", ",{asis,RestFieldNames},")))"});
Other ->
@@ -982,8 +1041,10 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
{objectfield,PrimFieldName1,PFNList} ->
case DynamicEnc of
{_LeadingAttrName,Fun} ->
- emit({"?RT_PER:encode_open_type([],"
- "?RT_PER:complete(",nl}),
+ asn1ct_func:need({Erule,complete,1}),
+ asn1ct_func:need({Erule,encode_open_type,1}),
+ emit({"encode_open_type("
+ "complete(",nl}),
emit({" ",Fun,"(",{asis,PrimFieldName1},
", ",Element,", ",{asis,PFNList},")))"})
end;
@@ -1039,53 +1100,81 @@ 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},"= "]),
+ call(Erule, 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} ->
@@ -1094,109 +1183,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},
+ OptOrDef|Lines]++
+ [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),
@@ -1216,9 +1324,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}} ->
@@ -1227,201 +1340,260 @@ 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),
+ 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} ->
+ asn1ct_gen_per:gen_dec_imm(Erule, Btype);
+ _ ->
+ asn1ct_gen_per:gen_dec_imm(Erule, Type)
+ end;
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OpenType} ->
+ asn1ct_gen_per:gen_dec_imm(Erule, #type{def=OpenType});
+ _ ->
+ asn1ct_gen_per:gen_dec_imm(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_enc_choice(Erule,TopType,CompList,Ext) ->
- gen_enc_choice_tag(CompList, [], Ext),
+ gen_enc_choice_tag(Erule, CompList, [], Ext),
emit({com,nl}),
emit({"case element(1,Val) of",nl}),
gen_enc_choice2(Erule,TopType, CompList, Ext),
emit({nl,"end"}).
-gen_enc_choice_tag({C1,C2},_,_) ->
+gen_enc_choice_tag(Erule, {C1,C2}, _, _) ->
N1 = get_name_list(C1),
N2 = get_name_list(C2),
- emit(["?RT_PER:set_choice(element(1,Val),",
- {asis,{N1,N2}},", ",{asis,{length(N1),length(N2)}},")"]);
-
-gen_enc_choice_tag({C1,C2,C3},_,_) ->
+ call(Erule,set_choice,
+ ["element(1, Val)",
+ {asis,{N1,N2}},
+ {asis,{length(N1),length(N2)}}]);
+gen_enc_choice_tag(Erule, {C1,C2,C3}, _, _) ->
N1 = get_name_list(C1),
N2 = get_name_list(C2),
N3 = get_name_list(C3),
Root = N1 ++ N3,
- emit(["?RT_PER:set_choice(element(1,Val),",
- {asis,{Root,N2}},", ",{asis,{length(Root),length(N2)}},")"]);
-gen_enc_choice_tag(C,_,_) ->
+ call(Erule,set_choice,
+ ["element(1, Val)",
+ {asis,{Root,N2}},
+ {asis,{length(Root),length(N2)}}]);
+gen_enc_choice_tag(Erule, C, _, _) ->
N = get_name_list(C),
- emit(["?RT_PER:set_choice(element(1,Val),",
- {asis,N},", ",{asis,length(N)},")"]).
+ call(Erule,set_choice,
+ ["element(1, Val)",
+ {asis,N},{asis,length(N)}]).
get_name_list(L) ->
get_name_list(L,[]).
@@ -1490,17 +1662,18 @@ gen_enc_choice2(_Erule,_,[], _, _) ->
true.
gen_dec_choice(Erule,TopType,CompList,{ext,Pos,NumExt}) ->
- emit({"{Ext,",{curr,bytes},"} = ?RT_PER:getbit(Bytes),",nl}),
+ emit(["{Ext,",{curr,bytes},"} = ",
+ {call,Erule,getbit,["Bytes"]},com,nl]),
asn1ct_name:new(bytes),
gen_dec_choice1(Erule,TopType,CompList,{ext,Pos,NumExt});
gen_dec_choice(Erule,TopType,CompList,noext) ->
gen_dec_choice1(Erule,TopType,CompList,noext).
gen_dec_choice1(Erule,TopType,CompList,noext) ->
- emit({"{Choice,",{curr,bytes},
- "} = ?RT_PER:getchoice(",{prev,bytes},",",
- length(CompList),", 0),",nl}),
- emit({"{Cname,{Val,NewBytes}} = case Choice of",nl}),
+ emit(["{Choice,",{curr,bytes},
+ "} = ",{call,Erule,getchoice,
+ [{prev,bytes},length(CompList),"0"]},com,nl,
+ "{Cname,{Val,NewBytes}} = case Choice of",nl]),
gen_dec_choice2(Erule,TopType,CompList,noext),
emit({nl,"end,",nl}),
emit({nl,"{{Cname,Val},NewBytes}"});
@@ -1511,25 +1684,20 @@ gen_dec_choice1(Erule,TopType,{RootList,ExtList,RootList2},Ext) ->
NewList = RootList ++ RootList2 ++ ExtList,
gen_dec_choice1(Erule,TopType, NewList, Ext);
gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) ->
- emit({"{Choice,",{curr,bytes},
- "} = ?RT_PER:getchoice(",{prev,bytes},",",
- length(CompList)-ExtNum,",Ext ),",nl}),
+ emit(["{Choice,",{curr,bytes},"} = ",
+ {call,Erule,getchoice,
+ [{prev,bytes},length(CompList)-ExtNum,"Ext"]},com,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) ->
@@ -1581,22 +1749,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),
@@ -1654,22 +1817,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));
@@ -1686,7 +1839,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_eval_ext.funcs b/lib/asn1/src/asn1ct_eval_ext.funcs
new file mode 100644
index 0000000000..5761901f89
--- /dev/null
+++ b/lib/asn1/src/asn1ct_eval_ext.funcs
@@ -0,0 +1 @@
+{ext,transform_to_EXTERNAL1994,1}.
diff --git a/lib/asn1/src/asn1ct_eval_per.funcs b/lib/asn1/src/asn1ct_eval_per.funcs
new file mode 100644
index 0000000000..a1ea5cd043
--- /dev/null
+++ b/lib/asn1/src/asn1ct_eval_per.funcs
@@ -0,0 +1,2 @@
+{per,encode_constrained_number,2}.
+{per,encode_small_number,1}.
diff --git a/lib/asn1/src/asn1ct_eval_uper.funcs b/lib/asn1/src/asn1ct_eval_uper.funcs
new file mode 100644
index 0000000000..884a486f40
--- /dev/null
+++ b/lib/asn1/src/asn1ct_eval_uper.funcs
@@ -0,0 +1,2 @@
+{uper,encode_constrained_number,2}.
+{uper,encode_small_number,1}.
diff --git a/lib/asn1/src/asn1ct_func.erl b/lib/asn1/src/asn1ct_func.erl
new file mode 100644
index 0000000000..2d221ca1b9
--- /dev/null
+++ b/lib/asn1/src/asn1ct_func.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(asn1ct_func).
+-export([start_link/0,need/1,call/3,generate/1]).
+-export([init/1,handle_call/3,handle_cast/2,terminate/2]).
+
+start_link() ->
+ {ok,Pid} = gen_server:start_link(?MODULE, [], []),
+ put(?MODULE, Pid),
+ ok.
+
+call(M, F, Args) ->
+ MFA = {M,F,length(Args)},
+ need(MFA),
+ asn1ct_gen:emit([F,"(",call_args(Args, ""),")"]).
+
+need(MFA) ->
+ asn1ct_rtt:assert_defined(MFA),
+ cast({need,MFA}).
+
+generate(Fd) ->
+ req({generate,Fd}),
+ erase(?MODULE),
+ ok.
+
+req(Req) ->
+ gen_server:call(get(?MODULE), Req, infinity).
+
+cast(Req) ->
+ gen_server:cast(get(?MODULE), Req).
+
+%%% Internal functions.
+
+-record(st, {used}).
+
+init([]) ->
+ St = #st{used=gb_sets:empty()},
+ {ok,St}.
+
+handle_cast({need,MFA}, #st{used=Used0}=St) ->
+ case gb_sets:is_member(MFA, Used0) of
+ false ->
+ Used = pull_in_deps(gb_sets:singleton(MFA), Used0),
+ {noreply,St#st{used=Used}};
+ true ->
+ {noreply,St}
+ end.
+
+handle_call({generate,Fd}, _From, #st{used=Used}=St) ->
+ generate(Fd, Used),
+ {stop,normal,ok,St}.
+
+terminate(_, _) ->
+ ok.
+
+call_args([A|As], Sep) ->
+ [Sep,A|call_args(As, ", ")];
+call_args([], _) -> [].
+
+generate(Fd, Used0) ->
+ Used1 = gb_sets:to_list(Used0),
+ Used = sofs:set(Used1, [mfa]),
+ Code = sofs:relation(asn1ct_rtt:code(), [{mfa,code}]),
+ Funcs0 = sofs:image(Code, Used),
+ Funcs = sofs:to_external(Funcs0),
+ io:put_chars(Fd, Funcs).
+
+pull_in_deps(Ws0, Used0) ->
+ case gb_sets:is_empty(Ws0) of
+ true ->
+ Used0;
+ false ->
+ {MFA,Ws1} = gb_sets:take_smallest(Ws0),
+ Used = gb_sets:add(MFA, Used0),
+ Needs = asn1ct_rtt:dependencies(MFA),
+ Ws = update_worklist(Needs, Used, Ws1),
+ pull_in_deps(Ws, Used)
+ end.
+
+update_worklist([H|T], Used, Ws) ->
+ case gb_sets:is_member(H, Used) of
+ false ->
+ update_worklist(T, Used, gb_sets:add(H, Ws));
+ true ->
+ update_worklist(T, Used, Ws)
+ end;
+update_worklist([], _, Ws) -> Ws.
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 64a3555f62..76c4182160 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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,
@@ -47,12 +46,11 @@
un_hyphen_var/1]).
-export([gen_encode_constructed/4,
gen_decode_constructed/4]).
--export([nif_parameter/0]).
%% 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()]
@@ -79,21 +77,26 @@ pgen_module(OutFile,Erules,Module,
ErlFile = lists:concat([OutFile,".erl"]),
Fid = fopen(ErlFile,[write]),
put(gen_file_out,Fid),
+ asn1ct_func:start_link(),
gen_head(Erules,Module,HrlGenerated),
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),
+ emit([nl,
+ "%%%",nl,
+ "%%% Run-time functions.",nl,
+ "%%%",nl]),
+ asn1ct_func:generate(Fid),
file:close(Fid),
asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options).
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),
@@ -196,7 +199,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(_,_,_) ->
@@ -240,7 +243,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;
@@ -296,8 +299,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{} ->
@@ -457,7 +459,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;
@@ -531,7 +533,8 @@ gen_part_decode_funcs({constructed,bif},TypeName,
{_Name,parts,Tag,_Type}) ->
emit([" case Data of",nl,
" L when is_list(L) ->",nl,
- " 'dec_",TypeName,"'(lists:map(fun(X)->element(1,?RT_BER:decode(X)) end,L),",{asis,Tag},");",nl,
+ " 'dec_",TypeName,"'(lists:map(fun(X) -> element(1, ",
+ {call,ber,ber_decode_erlang,["X"]},") end, L),",{asis,Tag},");",nl,
" _ ->",nl,
" [Res] = 'dec_",TypeName,"'([Data],",{asis,Tag},"),",nl,
" Res",nl,
@@ -552,20 +555,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(),
@@ -574,8 +574,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(),
@@ -658,9 +657,13 @@ gen_check_sof(Name,SOF,Type) ->
end,
emit({" ",{asis,NewName},"(DVs,Vs).",nl,nl}).
+gen_check_sequence(Name, []) ->
+ emit([{asis,ensure_atom(Name)},"(_,_) ->",nl,
+ " throw(badval).",nl,nl]);
gen_check_sequence(Name,Components) ->
emit([{asis,ensure_atom(Name)},"(DefaultValue,Value) ->",nl]),
gen_check_sequence(Name,Components,1).
+
gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
InnerType = get_inner(Type#type.def),
NthDefV = ["element(",Num+1,",DefaultValue)"],
@@ -672,9 +675,7 @@ gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
_ ->
emit({",",nl}),
gen_check_sequence(Name,Cs,Num+1)
- end;
-gen_check_sequence(_,[],_) ->
- ok.
+ end.
gen_check_choice(Name,CList=[#'ComponentType'{}|_Cs]) ->
emit([{asis,ensure_atom(Name)},"({Id,DefaultValue},{Id,Value}) ->",nl]),
@@ -754,8 +755,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),
@@ -787,7 +787,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' ->
@@ -810,7 +810,7 @@ gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) ->
pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
- emit({"-export([encoding_rule/0]).",nl}),
+ emit(["-export([encoding_rule/0,bit_string_format/0]).",nl]),
case Types of
[] -> ok;
_ ->
@@ -818,27 +818,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;
@@ -863,16 +847,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
@@ -941,27 +920,25 @@ gen_selected_decode_exports1([{FuncName,_}|Rest]) ->
gen_selected_decode_exports1(Rest).
pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) ->
- emit(["encoding_rule() ->",nl]),
- emit([{asis,Erules},".",nl,nl]);
+ gen_info_functions(Erules);
pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
- emit(["-export([encode/2,decode/2,encode_disp/2,decode_disp/2]).",nl,nl]),
- emit(["encoding_rule() ->",nl]),
- emit([" ",{asis,Erules},".",nl,nl]),
+ emit(["-export([encode/2,decode/2]).",nl,nl]),
+ gen_info_functions(Erules),
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 ->
+ asn1ct_func:need({Erules,complete,1}),
+ {["complete(encode_disp(Type, Data))"],"Bytes"};
+ ber ->
+ {"encode_disp(Type,Data)","iolist_to_binary(Bytes)"};
+ uper when NoFinalPadding == true ->
+ asn1ct_func:need({Erules,complete_NFP,1}),
+ {"complete_NFP(encode_disp(Type, Data))","Bytes"};
+ uper ->
+ asn1ct_func:need({Erules,complete,1}),
+ {["complete(encode_disp(Type, Data))"],"Bytes"}
+ end,
emit(["encode(Type,Data) ->",nl,
"case catch ",Call," of",nl,
" {'EXIT',{error,Reason}} ->",nl,
@@ -969,53 +946,33 @@ 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} ->
- io_lib:format("~s~s~s~n",
- ["element(1,?RT_BER:decode(Data",
- nif_parameter(),"))"]);
- {ber_bin_v2,true} ->
- emit(["{Data,Rest} = ?RT_BER:decode(Data0",
- nif_parameter(),"),",nl]),
+ {ber,false} ->
+ asn1ct_func:need({ber,ber_decode_nif,1}),
+ "element(1, ber_decode_nif(Data))";
+ {ber,true} ->
+ asn1ct_func:need({ber,ber_decode_nif,1}),
+ emit(["{Data,Rest} = ber_decode_nif(Data0),",nl]),
"Data";
_ ->
"Data"
end,
DecWrap = case Erules of
- ber -> "wrap_decode(Data)";
- ber_bin_v2 ->
+ ber ->
DecAnonymous;
- per -> "list_to_binary(Data)";
_ -> "Data"
end,
@@ -1025,32 +982,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,
@@ -1059,7 +1002,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,
@@ -1067,34 +1010,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();
@@ -1103,17 +1026,15 @@ 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_info_functions(Erules) ->
+ emit(["encoding_rule() -> ",
+ {asis,Erules},".",nl,nl,
+ "bit_string_format() -> ",
+ {asis,asn1ct:get_bit_string_format()},".",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,_} ->
@@ -1121,34 +1042,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,
+ " ",{call,ber,decode_primitive_incomplete,
+ ["Pattern","Data0"]},com,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, ",
+ {call,ber,ber_decode_nif,["Data0"]},")) 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.
@@ -1186,24 +1108,6 @@ gen_partial_inc_dispatcher([],_) ->
emit(["decode_partial_inc_disp(Type,_Data) ->",nl,
" 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.
-
-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);
@@ -1213,19 +1117,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) ->
@@ -1269,6 +1170,9 @@ emit({var,Variable}) ->
emit({asis,What}) ->
format(get(gen_file_out),"~w",[What]);
+emit({call,M,F,A}) ->
+ asn1ct_func:call(M, F, A);
+
emit(nl) ->
nl(get(gen_file_out));
@@ -1493,55 +1397,31 @@ gen_record(_,_,_,NumRecords) -> % skip CLASS etc for now.
gen_head(Erules,Mod,Hrl) ->
Options = get(encoding_options),
- {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 ->
- 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 ->
- emit(["%% Generated by the Erlang ASN.1 UNALIGNED"
- " PER-compiler version, utilizing"
- " bit-syntax:",
- asn1ct:vsn(),nl]),
- {"RT_PER","asn1rt_uper_bin"}
+ case Erules of
+ per ->
+ emit(["%% Generated by the Erlang ASN.1 PER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl]);
+ ber ->
+ emit(["%% Generated by the Erlang ASN.1 BER_V2-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl]);
+ uper ->
+ emit(["%% Generated by the Erlang ASN.1 UNALIGNED"
+ " PER-compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl])
end,
emit({"%% Purpose: encoder and decoder to the types in mod ",Mod,nl,nl}),
emit({"-module('",Mod,"').",nl}),
put(currmod,Mod),
- %emit({"-compile(export_all).",nl}),
- case {Hrl,lists:member(inline,get(encoding_options))} of
- {0,_} -> true;
- {_,true} -> true;
- _ ->
- emit({"-include(\"",Mod,".hrl\").",nl})
+ emit({"-compile(nowarn_unused_vars).",nl}),
+ case Hrl of
+ 0 -> ok;
+ _ -> emit({"-include(\"",Mod,".hrl\").",nl})
end,
- 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) ->
@@ -1619,50 +1499,41 @@ gen_check_call(TopType,Cname,Type,InnerType,WhatKind,DefaultValue,Element) ->
emit(["fun() -> true end ()"])
end.
-gen_prim_check_call(PrimType,DefaultValue,Element,Type) ->
+gen_prim_check_call(PrimType, Default, Element, Type) ->
case unify_if_string(PrimType) of
'BOOLEAN' ->
- emit({"asn1rt_check:check_bool(",DefaultValue,", ",
- Element,")"});
+ check_call(check_bool, [Default,Element]);
'INTEGER' ->
- NNL =
- case Type#type.def of
- {_,NamedNumberList} -> NamedNumberList;
- _ -> []
- end,
- emit({"asn1rt_check:check_int(",DefaultValue,", ",
- Element,", ",{asis,NNL},")"});
+ NNL = case Type#type.def of
+ {_,NamedNumberList} -> NamedNumberList;
+ _ -> []
+ end,
+ check_call(check_int, [Default,Element,{asis,NNL}]);
'BIT STRING' ->
{_,NBL} = Type#type.def,
- emit({"asn1rt_check:check_bitstring(",DefaultValue,", ",
- Element,", ",{asis,NBL},")"});
+ check_call(check_bitstring, [Default,Element,{asis,NBL}]);
'OCTET STRING' ->
- emit({"asn1rt_check:check_octetstring(",DefaultValue,", ",
- Element,")"});
+ check_call(check_octetstring, [Default,Element]);
'NULL' ->
- emit({"asn1rt_check:check_null(",DefaultValue,", ",
- Element,")"});
+ check_call(check_null, [Default,Element]);
'OBJECT IDENTIFIER' ->
- emit({"asn1rt_check:check_objectidentifier(",DefaultValue,
- ", ",Element,")"});
+ check_call(check_objectidentifier, [Default,Element]);
'RELATIVE-OID' ->
- emit({"asn1rt_check:check_objectidentifier(",DefaultValue,
- ", ",Element,")"});
+ check_call(check_objectidentifier, [Default,Element]);
'ObjectDescriptor' ->
- emit({"asn1rt_check:check_objectdescriptor(",DefaultValue,
- ", ",Element,")"});
+ check_call(check_objectdescriptor, [Default,Element]);
'REAL' ->
- emit({"asn1rt_check:check_real(",DefaultValue,
- ", ",Element,")"});
+ check_call(check_real, [Default,Element]);
'ENUMERATED' ->
{_,Enumerations} = Type#type.def,
- emit({"asn1rt_check:check_enum(",DefaultValue,
- ", ",Element,", ",{asis,Enumerations},")"});
+ check_call(check_enum, [Default,Element,{asis,Enumerations}]);
restrictedstring ->
- emit({"asn1rt_check:check_restrictedstring(",DefaultValue,
- ", ",Element,")"})
+ check_call(check_restrictedstring, [Default,Element])
end.
+check_call(F, Args) ->
+ asn1ct_func:call(check, F, Args).
+
%% lokahead_innertype/3 traverses Type and checks if check functions
%% have to be generated, i.e. for all constructed or referenced types.
lookahead_innertype(Name,'SEQUENCE',Type) ->
@@ -2019,43 +1890,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 3ccfca3784..de0adef2b2 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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
@@ -219,139 +170,84 @@ gen_encode_user(Erules,D) when is_record(D,typedef) ->
end.
gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
-
-%%% Constraint is currently not used for BER (except for BitString) and therefore replaced
-%%% with [] as a placeholder
BitStringConstraint = D#type.constraint,
- Constraint = [],
asn1ct_name:new(enumval),
- case D#type.def of
+ Type = case D#type.def of
+ 'OCTET STRING' -> restricted_string;
+ 'ObjectDescriptor'-> restricted_string;
+ 'NumericString' -> restricted_string;
+ 'TeletexString' -> restricted_string;
+ 'T61String' -> restricted_string;
+ 'VideotexString' -> restricted_string;
+ 'GraphicString' -> restricted_string;
+ 'VisibleString' -> restricted_string;
+ 'GeneralString' -> restricted_string;
+ 'PrintableString' -> restricted_string;
+ 'IA5String' -> restricted_string;
+ Other -> Other
+ end,
+ case Type of
+ restricted_string ->
+ call(encode_restricted_string, [Value,DoTag]);
'BOOLEAN' ->
- emit_encode_func('boolean',Value,DoTag);
+ call(encode_boolean, [Value,DoTag]);
'INTEGER' ->
- emit_encode_func('integer',Constraint,Value,DoTag);
+ call(encode_integer, [Value,DoTag]);
{'INTEGER',NamedNumberList} ->
- emit_encode_func('integer',Constraint,Value,
- NamedNumberList,DoTag);
+ call(encode_integer, [Value,{asis,NamedNumberList}, DoTag]);
{'ENUMERATED',NamedNumberList={_,_}} ->
-
emit(["case ",Value," of",nl]),
emit_enc_enumerated_cases(NamedNumberList,DoTag);
{'ENUMERATED',NamedNumberList} ->
-
emit(["case ",Value," of",nl]),
emit_enc_enumerated_cases(NamedNumberList,DoTag);
-
'REAL' ->
- emit_encode_func('real',Constraint,Value,DoTag);
-
+ emit([{call,ber,encode_tags,
+ [DoTag,{call,real_common,ber_encode_real,[Value]}]}]);
{'BIT STRING',NamedNumberList} ->
- emit_encode_func('bit_string',BitStringConstraint,Value,
- NamedNumberList,DoTag);
+ call(encode_bit_string,
+ [{asis,BitStringConstraint},Value,
+ {asis,NamedNumberList},DoTag]);
'ANY' ->
- emit_encode_func('open_type', Value,DoTag);
+ call(encode_open_type, [Value,DoTag]);
'NULL' ->
- emit_encode_func('null',Value,DoTag);
+ call(encode_null, [Value,DoTag]);
'OBJECT IDENTIFIER' ->
- emit_encode_func("object_identifier",Value,DoTag);
+ call(encode_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);
+ call(encode_relative_oid, [Value,DoTag]);
'UniversalString' ->
- emit_encode_func('universal_string',Constraint,Value,DoTag);
+ call(encode_universal_string, [Value,DoTag]);
'UTF8String' ->
- emit_encode_func('UTF8_string',Constraint,Value,DoTag);
+ call(encode_UTF8_string, [Value,DoTag]);
'BMPString' ->
- emit_encode_func('BMP_string',Constraint,Value,DoTag);
+ call(encode_BMP_string, [Value,DoTag]);
'UTCTime' ->
- emit_encode_func('utc_time',Constraint,Value,DoTag);
+ call(encode_utc_time, [Value,DoTag]);
'GeneralizedTime' ->
- emit_encode_func('generalized_time',Constraint,Value,DoTag);
+ call(encode_generalized_time, [Value,DoTag]);
'ASN1_OPEN_TYPE' ->
- emit_encode_func('open_type', Value,DoTag);
+ call(encode_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})
+ call(encode_open_type, [Value,DoTag])
+ end
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])
-%% emit([";",nl,"{asn1_enum,",{curr,enumval},"} -> ",
-%% "?RT_BER:encode_enumerated(",{curr,enumval},",",Tags,");",nl]),
-%% asn1ct_name:new(enumval)
- end,
+emit_enc_enumerated_cases([{EnumName,EnumVal}|T], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ",
+ {call,ber,encode_enumerated,[EnumVal,Tags]},";",nl]),
+ emit_enc_enumerated_cases(T, Tags, Ext);
+emit_enc_enumerated_cases([], _Tags, _Ext) ->
+ %% FIXME: Should extension be handled?
emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]),
emit([nl,"end"]).
@@ -414,9 +310,10 @@ gen_decode_selected(Erules,Type,FuncName) ->
{value,{_,P}} -> P;
false -> exit({error,{internal,no_pattern_saved}})
end,
- emit([" case ?RT_BER:decode_selective(",{asis,Pattern},",Bin) of",nl,
+ emit([" case ",{call,ber,decode_selective,
+ [{asis,Pattern},"Bin"]}," of",nl,
" {ok,Bin2} when is_binary(Bin2) ->",nl,
- " {Tlv,_} = ?RT_BER:decode(Bin2",asn1ct_gen:nif_parameter(),"),",nl]),
+ " {Tlv,_} = ", {call,ber,ber_decode_nif,["Bin2"]},com,nl]),
emit("{ok,"),
gen_decode_selected_type(Erules,Type),
emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl,
@@ -598,147 +495,123 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) ->
_ -> ""
end,
NewTypeName = case Typename of
- 'ANY' -> 'ASN1_OPEN_TYPE';
- _ -> Typename
+ 'ANY' -> 'ASN1_OPEN_TYPE';
+ 'OCTET STRING' -> restricted_string;
+ 'NumericString' -> restricted_string;
+ 'TeletexString' -> restricted_string;
+ 'T61String' -> restricted_string;
+ 'VideotexString' -> restricted_string;
+ 'GraphicString' -> restricted_string;
+ 'VisibleString' -> restricted_string;
+ 'GeneralString' -> restricted_string;
+ 'PrintableString' -> restricted_string;
+ 'IA5String' -> restricted_string;
+ _ -> Typename
end,
-% DoLength =
case NewTypeName of
'BOOLEAN'->
- emit({"?RT_BER:decode_boolean(",BytesVar,","}),
- add_func({decode_boolean,2});
+ emit(["decode_boolean(",BytesVar,","]),
+ need(decode_boolean, 2);
'INTEGER' ->
- emit({"?RT_BER:decode_integer(",BytesVar,",",
- {asis,int_constr(SingleValue,ValueRange)},","}),
- add_func({decode_integer,3});
+ emit(["decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},","]),
+ need(decode_integer, 3);
{'INTEGER',NamedNumberList} ->
- emit({"?RT_BER:decode_integer(",BytesVar,",",
+ emit(["decode_integer(",BytesVar,",",
{asis,int_constr(SingleValue,ValueRange)},",",
- {asis,NamedNumberList},","}),
- add_func({decode_integer,4});
+ {asis,NamedNumberList},","]),
+ need(decode_integer, 4);
{'ENUMERATED',NamedNumberList} ->
- emit({"?RT_BER:decode_enumerated(",BytesVar,",",
- {asis,Constraint},",",
- {asis,NamedNumberList},","}),
- add_func({decode_enumerated,4});
+ emit(["decode_enumerated(",BytesVar,",",
+ {asis,NamedNumberList},","]),
+ need(decode_enumerated, 3);
'REAL' ->
- emit({"?RT_BER:decode_real(",BytesVar,","}),
- add_func({decode_real,3});
- {'BIT STRING',NamedNumberList} ->
- case get(compact_bit_string) of
- true ->
- emit({"?RT_BER:decode_compact_bit_string(",
- BytesVar,",",{asis,Constraint},",",
- {asis,NamedNumberList},","}),
- add_func({decode_compact_bit_string,4});
- _ ->
- emit({"?RT_BER:decode_bit_string(",BytesVar,",",
- {asis,Constraint},",",
- {asis,NamedNumberList},","}),
- add_func({decode_bit_string,4})
- end;
+ ok;
+ {'BIT STRING',_NamedNumberList} ->
+ ok;
'NULL' ->
- emit({"?RT_BER:decode_null(",BytesVar,","}),
- add_func({decode_null,2});
+ emit(["decode_null(",BytesVar,","]),
+ need(decode_null, 2);
'OBJECT IDENTIFIER' ->
- emit({"?RT_BER:decode_object_identifier(",BytesVar,","}),
- add_func({decode_object_identifier,2});
+ emit(["decode_object_identifier(",BytesVar,","]),
+ need(decode_object_identifier, 2);
'RELATIVE-OID' ->
- emit({"?RT_BER:decode_relative_oid(",BytesVar,","}),
- add_func({decode_relative_oid,2});
+ emit(["decode_relative_oid(",BytesVar,","]),
+ need(decode_relative_oid, 2);
'ObjectDescriptor' ->
- emit({"?RT_BER:decode_restricted_string(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_ObjectDescriptor},","}),
- add_func({decode_restricted_string,4});
- 'OCTET STRING' ->
- emit({"?RT_BER:decode_octet_string",AsBin,"(",BytesVar,",",{asis,Constraint},","}),
- add_func({decode_octet_string,3});
- 'NumericString' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_NumericString},","}),
- add_func({decode_restricted_string,4});
- TString when TString == 'TeletexString';
- TString == 'T61String' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_TeletexString},","}),
- add_func({decode_restricted_string,4});
- 'VideotexString' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_VideotexString},","}),
- add_func({decode_restricted_string,4});
- 'GraphicString' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_GraphicString},","}),
- add_func({decode_restricted_string,4});
- 'VisibleString' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_VisibleString},","}),
- add_func({decode_restricted_string,4});
- 'GeneralString' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_GeneralString},","}),
- add_func({decode_restricted_string,4});
- 'PrintableString' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_PrintableString},","}),
- add_func({decode_restricted_string,4});
- 'IA5String' ->
- emit({"?RT_BER:decode_restricted_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},",",{asis,?T_IA5String},","}),
- add_func({decode_restricted_string,4}) ;
+ emit(["decode_restricted_string(",
+ BytesVar,",",{asis,Constraint},","]),
+ need(decode_restricted_string, 3);
+ restricted_string ->
+ emit(["decode_restricted_string",AsBin,"(",BytesVar,","]),
+ case Constraint of
+ [] ->
+ need(decode_restricted_string, 2);
+ _ ->
+ emit([{asis,Constraint},","]),
+ need(decode_restricted_string, 3)
+ end;
'UniversalString' ->
- emit({"?RT_BER:decode_universal_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},","}),
- add_func({decode_universal_string,3});
+ emit(["decode_universal_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","]),
+ need(decode_universal_string, 3);
'UTF8String' ->
- emit({"?RT_BER:decode_UTF8_string",AsBin,"(",
- BytesVar,","}),
- add_func({decode_UTF8_string,2});
+ emit(["decode_UTF8_string",AsBin,"(",
+ BytesVar,","]),
+ need(decode_UTF8_string, 2);
'BMPString' ->
- emit({"?RT_BER:decode_BMP_string",AsBin,"(",
- BytesVar,",",{asis,Constraint},","}),
- add_func({decode_BMP_string,3});
+ emit(["decode_BMP_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","]),
+ need(decode_BMP_string, 3);
'UTCTime' ->
- emit({"?RT_BER:decode_utc_time",AsBin,"(",
- BytesVar,",",{asis,Constraint},","}),
- add_func({decode_utc_time,3});
+ emit(["decode_utc_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","]),
+ need(decode_utc_time, 3);
'GeneralizedTime' ->
- emit({"?RT_BER:decode_generalized_time",AsBin,"(",
- BytesVar,",",{asis,Constraint},","}),
- add_func({decode_generalized_time,3});
+ emit(["decode_generalized_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","]),
+ need(decode_generalized_time, 3);
'ASN1_OPEN_TYPE' ->
- emit(["?RT_BER:decode_open_type_as_binary(",
+ emit(["decode_open_type_as_binary(",
BytesVar,","]),
- add_func({decode_open_type_as_binary,3});
+ need(decode_open_type_as_binary, 2);
#'ObjectClassFieldType'{} ->
case asn1ct_gen:get_inner(Att#type.def) of
{fixedtypevaluefield,_,InnerType} ->
gen_dec_prim(Erules,InnerType,BytesVar,DoTag,TagIn,Form,OptOrMand);
'ASN1_OPEN_TYPE' ->
- emit(["?RT_BER:decode_open_type_as_binary(",
+ emit(["decode_open_type_as_binary(",
BytesVar,","]),
- add_func({decode_open_type_as_binary,3});
+ need(decode_open_type_as_binary, 2);
Other ->
- exit({'can not decode' ,Other})
+ exit({'cannot decode',Other})
end;
Other ->
- exit({'can not decode' ,Other})
+ exit({'cannot decode',Other})
end,
- case {DoTag,NewTypeName} of
- {_,#'ObjectClassFieldType'{}} ->
+ TagStr = case DoTag of
+ {string,Tag1} -> Tag1;
+ _ when is_list(DoTag) -> {asis,DoTag}
+ end,
+ case NewTypeName of
+ {'BIT STRING',NNL} ->
+ gen_dec_bit_string(BytesVar, Constraint, NNL, TagStr);
+ 'REAL' ->
+ asn1ct_name:new(tmpbuf),
+ emit(["begin",nl,
+ {curr,tmpbuf}," = ",
+ {call,ber,match_tags,[BytesVar,TagStr]},com,nl,
+ {call,real_common,decode_real,[{curr,tmpbuf}]},nl,
+ "end",nl]);
+ #'ObjectClassFieldType'{} ->
case asn1ct_gen:get_inner(Att#type.def) of
'ASN1_OPEN_TYPE' ->
- emit([{asis,DoTag},asn1ct_gen:nif_parameter(),")"]);
+ emit([TagStr,")"]);
_ -> ok
end;
- {{string,TagStr},'ASN1_OPEN_TYPE'} ->
- emit([TagStr,asn1ct_gen:nif_parameter(),")"]);
- {_,'ASN1_OPEN_TYPE'} ->
- emit([{asis,DoTag},asn1ct_gen:nif_parameter(),")"]);
- {{string,TagStr},_} ->
- emit([TagStr,")"]);
- _ when is_list(DoTag) ->
- emit([{asis,DoTag},")"])
+ _ ->
+ emit([TagStr,")"])
end.
@@ -750,6 +623,23 @@ int_constr(SingleValue,[]) ->
SingleValue;
int_constr(SV,VR) ->
[SV,VR].
+
+gen_dec_bit_string(BytesVar, _Constraint, [_|_]=NNL, TagStr) ->
+ call(decode_named_bit_string,
+ [BytesVar,{asis,NNL},TagStr]);
+gen_dec_bit_string(BytesVar, Constraint, [], TagStr) ->
+ case asn1ct:get_bit_string_format() of
+ compact ->
+ call(decode_compact_bit_string,
+ [BytesVar,{asis,Constraint},TagStr]);
+ legacy ->
+ call(decode_legacy_bit_string,
+ [BytesVar,{asis,Constraint},TagStr]);
+ bitstring ->
+ call(decode_native_bit_string,
+ [BytesVar,{asis,Constraint},TagStr])
+ end.
+
%% Object code generating for encoding and decoding
%% ------------------------------------------------
@@ -1064,7 +954,7 @@ emit_tlv_format_function() ->
end.
emit_tlv_format_function1() ->
emit(["tlv_format(Bytes) when is_binary(Bytes) ->",nl,
- " {Tlv,_}=?RT_BER:decode(Bytes",asn1ct_gen:nif_parameter(),"),",nl,
+ " {Tlv,_} = ",{call,ber,ber_decode_nif,["Bytes"]},com,nl,
" Tlv;",nl,
"tlv_format(Bytes) ->",nl,
" Bytes.",nl]).
@@ -1272,7 +1162,7 @@ gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
emit({indent(3),"fun(_, Val, _RestPrimFieldName) ->",nl}),
emit({indent(6),"Len = case Val of",nl,indent(9),
- "Bin when is_binary(Bin) -> size(Bin);",nl,indent(9),
+ "Bin when is_binary(Bin) -> byte_size(Bin);",nl,indent(9),
"_ -> length(Val)",nl,indent(6),"end,"}),
emit({indent(6),"{Val,Len}",nl}),
emit({indent(3),"end.",nl,nl}),
@@ -1380,7 +1270,7 @@ gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
%% 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),"Bin when is_binary(Bin) -> byte_size(Bin);",nl,
indent(15),"_ -> length(Val)",nl,indent(12),"end,",nl,
indent(12),"{Val,Len}"]),
{Acc,0}
@@ -1498,38 +1388,22 @@ gen_objset_dec(_,ObjSetName,UniqueName,[{ObjName,Val,Fields}],
emit_default_getdec(ObjSetName,UniqueName),
emit([".",nl,nl]),
ok;
-gen_objset_dec(Erules,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
_ClFields,_NthObj) ->
emit(["'getdec_",ObjSetName,"'(_, _) ->",nl]),
emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
- case Erules of
- ber_bin_v2 ->
- emit([indent(4),"case Bytes of",nl,
- indent(6),"Bin when is_binary(Bin) -> ",nl,
- indent(8),"Bin;",nl,
- indent(6),"_ ->",nl,
- indent(8),"?RT_BER:encode(Bytes",driver_parameter(),")",nl,
- indent(4),"end",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(4),"{Bytes,[],Len}",nl])
- end,
+ emit([indent(4),"case Bytes of",nl,
+ indent(6),"Bin when is_binary(Bin) -> ",nl,
+ indent(8),"Bin;",nl,
+ indent(6),"_ ->",nl,
+ indent(8),{call,ber,ber_encode,["Bytes"]},nl,
+ indent(4),"end",nl]),
emit([indent(2),"end.",nl,nl]),
ok;
gen_objset_dec(_,_,_,[],_,_,_) ->
ok.
-driver_parameter() ->
- Options = get(encoding_options),
- case {lists:member(driver,Options),lists:member(nif,Options)} of
- {true,_} -> ",nif";
- {_,true} -> ",nif";
- _ -> ",erlang"
- end.
-
emit_default_getdec(ObjSetName,UniqueName) ->
emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]),
emit([indent(2), "fun(C,V,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
@@ -1575,7 +1449,7 @@ gen_inlined_dec_funs(Fields,[{typefield,Name,Prop}|Rest],
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),"B when is_binary(B) -> byte_size(B);",nl,
indent(15),"_ -> length(Bytes)",nl,
indent(12),"end,",nl,
indent(12),"{Bytes,[],Len}"]),
@@ -1772,19 +1646,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 ->
@@ -1833,9 +1694,6 @@ mk_object_val(0, Ack, Len) ->
mk_object_val(Val, Ack, Len) ->
mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
-add_func(F={_Func,_Arity}) ->
- asn1ct_table:insert(asn1_functab, {F}).
-
%% For BER the ExtensionAdditionGroup notation has no impact on the encoding/decoding
%% and therefore we only filter away the ExtensionAdditionGroup start and end markers
extaddgroup2sequence(ExtList) when is_list(ExtList) ->
@@ -1847,4 +1705,8 @@ extaddgroup2sequence(ExtList) when is_list(ExtList) ->
true
end, ExtList).
+call(F, Args) ->
+ asn1ct_func:call(ber, F, Args).
+need(F, Arity) ->
+ asn1ct_func:need({ber,F,Arity}).
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index bd5b81991d..fac233532b 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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]).
@@ -34,6 +35,7 @@
-export([extaddgroup2sequence/1]).
-import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_func, [call/3]).
%% pgen(Erules, Module, TypeOrVal)
%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
@@ -78,18 +80,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);
@@ -103,13 +93,6 @@ gen_encode_user(Erules,D) when is_record(D,typedef) ->
Typename = [D#typedef.name],
Def = D#typedef.typespec,
InnerType = asn1ct_gen:get_inner(Def#type.def),
- case InnerType of
- 'SET' -> true;
- 'SEQUENCE' -> true;
- _ ->
- emit({nl,"'enc_",asn1ct_gen:list2name(Typename),"'({'",asn1ct_gen:list2name(Typename),"',Val}) ->",nl}),
- emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val);",nl,nl})
- end,
emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
case asn1ct_gen:type(InnerType) of
{primitive,bif} ->
@@ -145,105 +128,96 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
asn1ct_name:new(enumval),
case D#type.def of
'INTEGER' ->
- emit({"?RT_PER:encode_integer(", %fel
- {asis,effective_constraint(integer,Constraint)},",",Value,")"});
+ Args = [{asis,asn1ct_imm:effective_constraint(integer,Constraint)},
+ Value],
+ call(Erules, encode_integer, Args);
{'INTEGER',NamedNumberList} ->
- emit({"?RT_PER:encode_integer(",
- {asis,effective_constraint(integer,Constraint)},",",Value,",",
- {asis,NamedNumberList},")"});
+ Args = [{asis,asn1ct_imm:effective_constraint(integer,Constraint)},
+ Value,{asis,NamedNumberList}],
+ call(Erules, encode_integer, Args);
{'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 ->
- emit(["case ",Value," of",nl]);
- _ ->
- emit(["case (case ",Value," of {_,",{curr,enumval},"}-> ",
- {curr,enumval},";_->", Value," end) of",nl]),
- asn1ct_name:new(enumval)
- end,
-%% emit_enc_enumerated_cases(Erules,NewC, NewList++[{asn1_enum,length(Nlist1)-1}], 0);
- emit_enc_enumerated_cases(Erules,NewC, NewList, 0);
+ NewList = [{0,X} || {X,_} <- Nlist1] ++ ['EXT_MARK'] ++
+ [{1,X} || {X,_} <- Nlist2],
+ NewC = {0,length(Nlist1)-1},
+ emit(["case ",Value," of",nl]),
+ emit_enc_enumerated_cases(Erules, NewC, NewList, 0);
{'ENUMERATED',NamedNumberList} ->
- NewList = [X||{X,_} <- NamedNumberList],
- NewC = [{'ValueRange',{0,length(NewList)-1}}],
- case Erules of
- uper_bin ->
- emit(["case ",Value," of",nl]);
- _ ->
- emit(["case (case ",Value," of {_,",{curr,enumval},
- "}->",{curr,enumval},";_->",Value," end) of",nl])
- end,
- emit_enc_enumerated_cases(Erules,NewC, NewList, 0);
+ NewList = [X || {X,_} <- NamedNumberList],
+ NewC = {0,length(NewList)-1},
+ emit(["case ",Value," of",nl]),
+ emit_enc_enumerated_cases(Erules, NewC, NewList, 0);
'REAL' ->
- emit({"?RT_PER:encode_real(",Value,")"});
+ emit_enc_real(Erules, Value);
{'BIT STRING',NamedNumberList} ->
- emit({"?RT_PER:encode_bit_string(",
- {asis,Constraint},",",Value,",",
- {asis,NamedNumberList},")"});
+ SizeConstr = get_constraint(Constraint, 'SizeConstraint'),
+ call(Erules, encode_bit_string,
+ [{asis,SizeConstr},Value,
+ {asis,NamedNumberList}]);
'NULL' ->
- emit({"?RT_PER:encode_null(",Value,")"});
+ emit("[]");
'OBJECT IDENTIFIER' ->
- emit({"?RT_PER:encode_object_identifier(",Value,")"});
+ call(Erules, encode_object_identifier, [Value]);
'RELATIVE-OID' ->
- emit({"?RT_PER:encode_relative_oid(",Value,")"});
+ call(Erules, encode_relative_oid, [Value]);
'ObjectDescriptor' ->
- emit({"?RT_PER:encode_ObjectDescriptor(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_ObjectDescriptor,
+ [{asis,Constraint},Value]);
'BOOLEAN' ->
- emit({"?RT_PER:encode_boolean(",Value,")"});
+ call(Erules, encode_boolean, [Value]);
'OCTET STRING' ->
- emit({"?RT_PER:encode_octet_string(",{asis,Constraint},",",Value,")"});
+ case get_constraint(Constraint, 'SizeConstraint') of
+ 0 ->
+ emit("[]");
+ no ->
+ call(Erules, encode_octet_string, [Value]);
+ C ->
+ call(Erules, encode_octet_string, [{asis,C},Value])
+ end;
'NumericString' ->
- emit({"?RT_PER:encode_NumericString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_NumericString, [{asis,Constraint},Value]);
TString when TString == 'TeletexString';
TString == 'T61String' ->
- emit({"?RT_PER:encode_TeletexString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_TeletexString, [{asis,Constraint},Value]);
'VideotexString' ->
- emit({"?RT_PER:encode_VideotexString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_VideotexString, [{asis,Constraint},Value]);
'UTCTime' ->
- emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_VisibleString, [{asis,Constraint},Value]);
'GeneralizedTime' ->
- emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_VisibleString, [{asis,Constraint},Value]);
'GraphicString' ->
- emit({"?RT_PER:encode_GraphicString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_GraphicString, [{asis,Constraint},Value]);
'VisibleString' ->
- emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_VisibleString, [{asis,Constraint},Value]);
'GeneralString' ->
- emit({"?RT_PER:encode_GeneralString(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_GeneralString, [{asis,Constraint},Value]);
'PrintableString' ->
- emit({"?RT_PER:encode_PrintableString(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_PrintableString, [{asis,Constraint},Value]);
'IA5String' ->
- emit({"?RT_PER:encode_IA5String(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_IA5String, [{asis,Constraint},Value]);
'BMPString' ->
- emit({"?RT_PER:encode_BMPString(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_BMPString, [{asis,Constraint},Value]);
'UniversalString' ->
- emit({"?RT_PER:encode_UniversalString(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_UniversalString, [{asis,Constraint},Value]);
'UTF8String' ->
- emit({"?RT_PER:encode_UTF8String(",Value,")"});
+ call(Erules, encode_UTF8String, [Value]);
'ANY' ->
- emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
- Value, ")"]);
+ call(Erules, encode_open_type, [Value]);
'ASN1_OPEN_TYPE' ->
NewValue = case Constraint of
[#'Externaltypereference'{type=Tname}] ->
- io_lib:format(
- "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ asn1ct_func:need({Erules,complete,1}),
+ io_lib:format(
+ "complete(enc_~s(~s))",[Tname,Value]);
[#type{def=#'Externaltypereference'{type=Tname}}] ->
+ asn1ct_func:need({Erules,complete,1}),
io_lib:format(
- "?RT_PER:complete(enc_~s(~s))",
+ "complete(enc_~s(~s))",
[Tname,Value]);
_ -> Value
end,
- emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
- NewValue, ")"]);
+ call(Erules, encode_open_type, [NewValue]);
#'ObjectClassFieldType'{} ->
case asn1ct_gen:get_inner(D#type.def) of
{fixedtypevaluefield,_,InnerType} ->
@@ -255,147 +229,58 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
exit({asn1_error,nyi,XX})
end.
-
-emit_enc_enumerated_cases(Erule,C, [H], Count) ->
- emit_enc_enumerated_case(Erule,C, H, Count),
- case H of
- 'EXT_MARK' -> ok;
- _ ->
- emit([";",nl])
- end,
- emit([nl,"EnumVal -> exit({error,{asn1, {enumerated_not_in_range, EnumVal}}})"]),
- emit([nl,"end"]);
-emit_enc_enumerated_cases(Erule, C, ['EXT_MARK'|T], _Count) ->
- emit_enc_enumerated_cases(Erule, C, T, 0);
-emit_enc_enumerated_cases(Erule, C, [H1,H2|T], Count) ->
- emit_enc_enumerated_case(Erule, C, H1, Count),
+emit_enc_real(Erules, Real) ->
+ asn1ct_name:new(tmpval),
+ asn1ct_name:new(tmplen),
+ emit(["begin",nl,
+ "{",{curr,tmpval},com,{curr,tmplen},"} = ",
+ {call,real_common,encode_real,[Real]},com,nl,
+ "[",{call,Erules,encode_length,[{curr,tmplen}]},",",
+ {curr,tmpval},"]",nl,
+ "end"]).
+
+emit_enc_enumerated_cases(Erules, C, ['EXT_MARK'|T], _Count) ->
+ %% Reset enumeration counter.
+ emit_enc_enumerated_cases(Erules, C, T, 0);
+emit_enc_enumerated_cases(Erules, C, [H|T], Count) ->
+ emit_enc_enumerated_case(Erules, C, H, Count),
emit([";",nl]),
- emit_enc_enumerated_cases(Erule, C, [H2|T], Count+1).
+ emit_enc_enumerated_cases(Erules, C, T, Count+1);
+emit_enc_enumerated_cases(_Erules, _, [], _Count) ->
+ emit(["EnumVal -> "
+ "exit({error,{asn1,{enumerated_not_in_range, EnumVal}}})",nl,
+ "end"]).
-
-
-emit_enc_enumerated_case(uper_bin,_C, {asn1_enum,High}, _) ->
- emit([
- "{asn1_enum,EnumV} when is_integer(EnumV), EnumV > ",High," -> ",
- "[<<1:1>>,?RT_PER:encode_small_number(EnumV)]"]);
-emit_enc_enumerated_case(_Per,_C, {asn1_enum,High}, _) ->
- emit([
- "{asn1_enum,EnumV} when is_integer(EnumV), EnumV > ",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(["'",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(["'",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 before generating SingleValue or ValueRange
- %% In case of ValueRange, also check for 'MIN and 'MAX'
- case lists:usort(SVList) of
- [N] ->
- [{'SingleValue',N}];
- L when is_list(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(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)
- 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])
+emit_enc_enumerated_case(Erules, C, {0,EnumName}, Count) ->
+ %% ENUMERATED with extensionmark; the value lies within then extension root
+ Enc = enc_ext_and_val(Erules, 0, encode_constrained_number, [C,Count]),
+ emit(["'",EnumName,"' -> ",{asis,Enc}]);
+emit_enc_enumerated_case(Erules, _C, {1,EnumName}, Count) ->
+ %% ENUMERATED with extensionmark; the value is higher than extension root
+ Enc = enc_ext_and_val(Erules, 1, encode_small_number, [Count]),
+ emit(["'",EnumName,"' -> ",{asis,Enc}]);
+emit_enc_enumerated_case(Erules, C, EnumName, Count) ->
+ %% ENUMERATED without extension
+ EvalMod = eval_module(Erules),
+ emit(["'",EnumName,"' -> ",
+ {asis,EvalMod:encode_constrained_number(C, Count)}]).
+
+enc_ext_and_val(per, E, F, Args) ->
+ [E|apply(asn1ct_eval_per, F, Args)];
+enc_ext_and_val(uper, E, F, Args) ->
+ <<E:1,(apply(asn1ct_eval_uper, F, Args))/bitstring>>.
+
+
+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.
-
%% Object code generating for encoding and decoding
%% ------------------------------------------------
@@ -442,7 +327,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}]")
@@ -546,7 +431,7 @@ gen_encode_field_call(ObjName,FieldName,Type) ->
Def = Type#typedef.typespec,
case Type#typedef.name of
{primitive,bif} ->
- gen_encode_prim(per,Def,"false",
+ gen_encode_prim(uper,Def,"false",
"Val"),
[];
{constructed,bif} ->
@@ -684,7 +569,7 @@ gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
Def = Type#typedef.typespec,
case Type#typedef.name of
{primitive,bif} ->
- gen_dec_prim(per,Def,Bytes),
+ gen_dec_prim(uper, Def, Bytes),
[];
{constructed,bif} ->
emit({" 'dec_",ObjName,'_',FieldName,
@@ -833,7 +718,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])
@@ -883,7 +768,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]),
@@ -921,7 +806,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]),
@@ -945,7 +830,7 @@ emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
case {ExtMod,Name} of
{primitive,bif} ->
emit(indent(12)),
- gen_encode_prim(per,Type,dotag,"Val"),
+ gen_encode_prim(uper,Type,dotag,"Val"),
{[],0};
{constructed,bif} ->
emit([indent(12),"'enc_",
@@ -1031,7 +916,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;
@@ -1047,83 +931,49 @@ 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) ->
case {ExtName,Name} of
{primitive,bif} ->
emit(indent(12)),
- gen_dec_prim(per,Type,"Val"),
+ gen_dec_prim(uper, Type, "Val"),
0;
{constructed,bif} ->
emit({indent(12),"'dec_",
@@ -1141,7 +991,7 @@ emit_inner_of_decfun(Type,_) when is_record(Type,type) ->
case Type#type.def of
Def when is_atom(Def) ->
emit({indent(9),Def," ->",nl,indent(12)}),
- gen_dec_prim(erules,Type,"Val");
+ gen_dec_prim(uper, Type, "Val");
TRef when is_record(TRef,typereference) ->
T = TRef#typereference.val,
emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
@@ -1228,141 +1078,132 @@ gen_decode_user(Erules,D) when is_record(D,typedef) ->
exit({error,{asn1,{unknown,Other}}})
end.
-
-gen_dec_prim(Erules,Att,BytesVar) ->
- Typename = Att#type.def,
- Constraint = Att#type.constraint,
- case Typename of
- 'INTEGER' ->
- emit({"?RT_PER:decode_integer(",BytesVar,",",
- {asis,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,")"});
-
- {'BIT STRING',NamedNumberList} ->
- case get(compact_bit_string) of
- true ->
- emit({"?RT_PER:decode_compact_bit_string(",
- BytesVar,",",{asis,Constraint},",",
- {asis,NamedNumberList},")"});
- _ ->
- emit({"?RT_PER:decode_bit_string(",BytesVar,",",
- {asis,Constraint},",",
- {asis,NamedNumberList},")"})
- end;
- 'NULL' ->
- emit({"?RT_PER:decode_null(",
- BytesVar,")"});
- 'OBJECT IDENTIFIER' ->
- emit({"?RT_PER:decode_object_identifier(",
- BytesVar,")"});
- 'RELATIVE-OID' ->
- emit({"?RT_PER:decode_relative_oid(",
- 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},")"});
- TString when TString == 'TeletexString';
- TString == 'T61String' ->
- emit({"?RT_PER:decode_TeletexString(",BytesVar,",",
- {asis,Constraint},")"});
- 'VideotexString' ->
- emit({"?RT_PER:decode_VideotexString(",BytesVar,",",
- {asis,Constraint},")"});
- 'UTCTime' ->
- emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
- {asis,Constraint},")"});
- 'GeneralizedTime' ->
- emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
- {asis,Constraint},")"});
- 'GraphicString' ->
- emit({"?RT_PER:decode_GraphicString(",BytesVar,",",
- {asis,Constraint},")"});
- 'VisibleString' ->
- emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
- {asis,Constraint},")"});
- 'GeneralString' ->
- emit({"?RT_PER:decode_GeneralString(",BytesVar,",",
- {asis,Constraint},")"});
- 'PrintableString' ->
- emit({"?RT_PER:decode_PrintableString(",BytesVar,",",{asis,Constraint},")"});
- 'IA5String' ->
- emit({"?RT_PER:decode_IA5String(",BytesVar,",",{asis,Constraint},")"});
- 'BMPString' ->
- emit({"?RT_PER:decode_BMPString(",BytesVar,",",
- {asis,Constraint},")"});
- 'UniversalString' ->
- emit({"?RT_PER:decode_UniversalString(",BytesVar,
- ",",{asis,Constraint},")"});
- '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
+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({'BIT STRING',NNL}, Constr0, Aligned) ->
+ Constr = get_constraint(Constr0, 'SizeConstraint'),
+ Imm = asn1ct_imm:per_dec_raw_bitstring(Constr, Aligned),
+ case NNL of
+ [] ->
+ case asn1ct:get_bit_string_format() of
+ compact ->
+ gen_dec_bit_string(decode_compact_bit_string,
+ Imm);
+ legacy ->
+ gen_dec_bit_string(decode_legacy_bit_string,
+ Imm);
+ bitstring ->
+ gen_dec_copy_bitstring(Imm)
end;
- #'ObjectClassFieldType'{} ->
- case asn1ct_gen:get_inner(Att#type.def) of
- {fixedtypevaluefield,_,InnerType} ->
- gen_dec_prim(Erules,InnerType,BytesVar);
- T ->
- gen_dec_prim(Erules,Att#type{def=T},BytesVar)
- end;
- Other ->
- exit({'cant decode' ,Other})
+ [_|_] ->
+ D = fun(V, Buf) ->
+ As = [V,{asis,NNL}],
+ Call = {call,per_common,decode_named_bit_string,As},
+ emit(["{",Call,com,Buf,"}"])
+ end,
+ {call,D,Imm}
+ end;
+gen_dec_imm_1('NULL', _Constr, _Aligned) ->
+ {value,'NULL'};
+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('BMPString'=Type, Constraint, Aligned) ->
+ gen_dec_k_m_string(Type, Constraint, Aligned);
+gen_dec_imm_1('NumericString'=Type, Constraint, Aligned) ->
+ gen_dec_k_m_string(Type, Constraint, Aligned);
+gen_dec_imm_1('PrintableString'=Type, Constraint, Aligned) ->
+ gen_dec_k_m_string(Type, Constraint, Aligned);
+gen_dec_imm_1('VisibleString'=Type, Constraint, Aligned) ->
+ gen_dec_k_m_string(Type, Constraint, Aligned);
+gen_dec_imm_1('IA5String'=Type, Constraint, Aligned) ->
+ gen_dec_k_m_string(Type, Constraint, Aligned);
+gen_dec_imm_1('UniversalString'=Type, Constraint, Aligned) ->
+ gen_dec_k_m_string(Type, Constraint, Aligned);
+gen_dec_imm_1('UTCTime', Constraint, Aligned) ->
+ gen_dec_k_m_string('VisibleString', Constraint, Aligned);
+gen_dec_imm_1('GeneralizedTime', Constraint, Aligned) ->
+ gen_dec_k_m_string('VisibleString', Constraint, 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('TeletexString', _Constraint, Aligned) ->
+ gen_dec_restricted_string(Aligned);
+gen_dec_imm_1('T61String', _Constraint, Aligned) ->
+ gen_dec_restricted_string(Aligned);
+gen_dec_imm_1('VideotexString', _Constraint, Aligned) ->
+ gen_dec_restricted_string(Aligned);
+gen_dec_imm_1('GraphicString', _Constraint, Aligned) ->
+ gen_dec_restricted_string(Aligned);
+gen_dec_imm_1('GeneralString', _Constraint, Aligned) ->
+ gen_dec_restricted_string(Aligned);
+gen_dec_imm_1('ObjectDescriptor', _Constraint, Aligned) ->
+ gen_dec_restricted_string(Aligned);
+gen_dec_imm_1('OBJECT IDENTIFIER', _Constraint, Aligned) ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,per_common,decode_oid,[V]},com,
+ Buf,"}"])
+ end,
+ {call,Dec,gen_dec_restricted_string(Aligned)};
+gen_dec_imm_1('RELATIVE-OID', _Constraint, Aligned) ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,per_common,decode_relative_oid,[V]},com,
+ Buf,"}"])
+ end,
+ {call,Dec,gen_dec_restricted_string(Aligned)};
+gen_dec_imm_1('UTF8String', _Constraint, Aligned) ->
+ asn1ct_imm:per_dec_restricted_string(Aligned);
+gen_dec_imm_1('REAL', _Constraint, Aligned) ->
+ asn1ct_imm:per_dec_real(Aligned);
+gen_dec_imm_1(#'ObjectClassFieldType'{}=TypeName, _Constraint, Aligned) ->
+ case asn1ct_gen:get_inner(TypeName) of
+ {fixedtypevaluefield,_,#type{def=InnerType,constraint=C}} ->
+ gen_dec_imm_1(InnerType, C, Aligned);
+ #type{def=T,constraint=C} ->
+ gen_dec_imm_1(T, C, Aligned)
end.
+gen_dec_bit_string(F, Imm) ->
+ D = fun(V, Buf) ->
+ emit(["{",{call,per_common,F,[V]},com,Buf,"}"])
+ end,
+ {call,D,Imm}.
+
+gen_dec_copy_bitstring(Imm) ->
+ D = fun(V, Buf) ->
+ emit(["{list_to_bitstring([",V,"]),",Buf,"}"])
+ end,
+ {call,D,Imm}.
+
+gen_dec_k_m_string(Type, Constraint, Aligned) ->
+ asn1ct_imm:per_dec_k_m_string(Type, Constraint, Aligned).
+
+gen_dec_restricted_string(Aligned) ->
+ Imm = asn1ct_imm:per_dec_restricted_string(Aligned),
+ {convert,binary_to_list,Imm}.
+
+gen_dec_prim(Erule, Type, BytesVar) ->
+ Imm = gen_dec_imm(Erule, Type),
+ asn1ct_imm:dec_code_gen(Imm, BytesVar).
is_already_generated(Operation,Name) ->
case get(class_default_type) of
@@ -1417,3 +1258,25 @@ 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)}.
+
+eval_module(per) -> asn1ct_eval_per;
+eval_module(uper) -> asn1ct_eval_uper.
diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
index 16eec92847..81d8cdcae6 100644
--- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
+++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -34,6 +34,7 @@
-import(asn1ct_gen, [emit/1,demit/1]).
-import(asn1ct_gen_per, [is_already_generated/2,more_genfields/1,
get_class_fields/1,get_object_field/2]).
+-import(asn1ct_func, [call/3]).
%% pgen(Erules, Module, TypeOrVal)
%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
@@ -69,18 +70,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);
@@ -94,13 +83,6 @@ gen_encode_user(Erules,D) when is_record(D,typedef) ->
Typename = [D#typedef.name],
Def = D#typedef.typespec,
InnerType = asn1ct_gen:get_inner(Def#type.def),
- case InnerType of
- 'SET' -> true;
- 'SEQUENCE' -> true;
- _ ->
- emit({nl,"'enc_",asn1ct_gen:list2name(Typename),"'({'",asn1ct_gen:list2name(Typename),"',Val}) ->",nl}),
- emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val);",nl,nl})
- end,
emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
case asn1ct_gen:type(InnerType) of
{primitive,bif} ->
@@ -149,41 +131,30 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
emit([" %%INTEGER with effective constraint: ",
{asis,EffectiveConstr},nl]),
emit_enc_integer_NNL(Erules,EffectiveConstr,Value,NamedNumberList);
- {'ENUMERATED',{Nlist1,Nlist2}} ->
- NewList = lists:append([[{0,X}||{X,_} <- Nlist1],['EXT_MARK'],[{1,X}||{X,_} <- Nlist2]]),
- NewC = [{'ValueRange',{0,length(Nlist1)-1}}],
- emit(["case ",Value," of",nl]),
-%% emit_enc_enumerated_cases(Erules,NewC, NewList++[{asn1_enum,length(Nlist1)-1}], 0);
- emit_enc_enumerated_cases(Erules,NewC, NewList, 0);
- {'ENUMERATED',NamedNumberList} ->
- NewList = [X||{X,_} <- NamedNumberList],
- NewC = effective_constraint(integer,
- [{'ValueRange',
- {0,length(NewList)-1}}]),
- NewVal = enc_enum_cases(Value,NewList),
- emit_enc_integer(Erules,NewC,NewVal);
-
+ {'ENUMERATED',_} ->
+ asn1ct_gen_per:gen_encode_prim(Erules, D, DoTag, Value);
'REAL' ->
- emit({"?RT_PER:encode_real(",Value,")"});
+ emit_enc_real(Erules, Value);
{'BIT STRING',NamedNumberList} ->
EffectiveC = effective_constraint(bitstring,Constraint),
case EffectiveC of
- 0 -> emit({"[]"});
+ 0 ->
+ emit({"[]"});
_ ->
- emit({"?RT_PER:encode_bit_string(",
- {asis,EffectiveC},",",Value,",",
- {asis,NamedNumberList},")"})
+ call(Erules, encode_bit_string,
+ [{asis,EffectiveC},Value,
+ {asis,NamedNumberList}])
end;
'NULL' ->
- emit({"?RT_PER:encode_null(",Value,")"});
+ emit("[]");
'OBJECT IDENTIFIER' ->
- emit({"?RT_PER:encode_object_identifier(",Value,")"});
+ call(Erules, encode_object_identifier, [Value]);
'RELATIVE-OID' ->
- emit({"?RT_PER:encode_relative_oid(",Value,")"});
+ call(Erules, encode_relative_oid, [Value]);
'ObjectDescriptor' ->
- emit({"?RT_PER:encode_ObjectDescriptor(",{asis,Constraint},
- ",",Value,")"});
+ call(Erules, encode_ObjectDescriptor,
+ [{asis,Constraint},Value]);
'BOOLEAN' ->
emit({"case ",Value," of",nl,
" true -> [1];",nl,
@@ -197,19 +168,19 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
emit_enc_known_multiplier_string('NumericString',Constraint,Value);
TString when TString == 'TeletexString';
TString == 'T61String' ->
- emit({"?RT_PER:encode_TeletexString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_TeletexString, [{asis,Constraint},Value]);
'VideotexString' ->
- emit({"?RT_PER:encode_VideotexString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_VideotexString, [{asis,Constraint},Value]);
'UTCTime' ->
emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
'GeneralizedTime' ->
emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
'GraphicString' ->
- emit({"?RT_PER:encode_GraphicString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_GraphicString, [{asis,Constraint},Value]);
'VisibleString' ->
emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
'GeneralString' ->
- emit({"?RT_PER:encode_GeneralString(",{asis,Constraint},",",Value,")"});
+ call(Erules, encode_GeneralString, [{asis,Constraint},Value]);
'PrintableString' ->
emit_enc_known_multiplier_string('PrintableString',Constraint,Value);
'IA5String' ->
@@ -219,23 +190,23 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
'UniversalString' ->
emit_enc_known_multiplier_string('UniversalString',Constraint,Value);
'UTF8String' ->
- emit({"?RT_PER:encode_UTF8String(",Value,")"});
+ call(Erules, encode_UTF8String, [Value]);
'ANY' ->
- emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
- Value, ")"]);
+ call(Erules, encode_open_type, [Value]);
'ASN1_OPEN_TYPE' ->
NewValue = case Constraint of
[#'Externaltypereference'{type=Tname}] ->
- io_lib:format(
- "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ asn1ct_func:need({Erules,complete,1}),
+ io_lib:format(
+ "complete(enc_~s(~s))",[Tname,Value]);
[#type{def=#'Externaltypereference'{type=Tname}}] ->
+ asn1ct_func:need({Erules,complete,1}),
io_lib:format(
- "?RT_PER:complete(enc_~s(~s))",
+ "complete(enc_~s(~s))",
[Tname,Value]);
_ -> Value
end,
- emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
- NewValue, ")"]);
+ call(Erules, encode_open_type, [NewValue]);
#'ObjectClassFieldType'{} ->
case asn1ct_gen:get_inner(D#type.def) of
{fixedtypevaluefield,_,InnerType} ->
@@ -247,6 +218,17 @@ gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
exit({asn1_error,nyi,XX})
end.
+emit_enc_real(Erules, Real) ->
+ asn1ct_name:new(tmpval),
+ asn1ct_name:new(tmplen),
+ emit(["begin",nl,
+ "{",{curr,tmpval},com,{curr,tmplen},"} = ",
+ {call,real_common,encode_real,[Real]},com,nl,
+ "[",{call,Erules,encode_length,[{curr,tmplen}]},",",nl,
+ {call,Erules,octets_to_complete,
+ [{curr,tmplen},{curr,tmpval}]},"]",nl,
+ "end"]).
+
emit_enc_known_multiplier_string(StringType,C,Value) ->
SizeC =
case get_constraint(C,'SizeConstraint') of
@@ -266,62 +248,34 @@ emit_enc_known_multiplier_string(StringType,C,Value) ->
NumBits = get_NumBits(C,StringType),
CharOutTab = get_CharOutTab(C,StringType),
%% NunBits and CharOutTab for chars_encode
- emit_enc_k_m_string(StringType,SizeC,NumBits,CharOutTab,Value).
+ emit_enc_k_m_string(SizeC, NumBits, CharOutTab, Value).
-emit_enc_k_m_string(_StringType,0,_NumBits,_CharOutTab,_Value) ->
+emit_enc_k_m_string(0, _NumBits, _CharOutTab, _Value) ->
emit({"[]"});
-emit_enc_k_m_string(StringType,SizeC,NumBits,CharOutTab,Value) ->
- emit({"?RT_PER:encode_known_multiplier_string(",{asis,StringType},",",
- {asis,SizeC},",",NumBits,",",{asis,CharOutTab},",",Value,")"}).
-
-emit_dec_known_multiplier_string(StringType,C,BytesVar) ->
- SizeC = get_constraint(C,'SizeConstraint'),
- PAlphabC = get_constraint(C,'PermittedAlphabet'),
- case {StringType,PAlphabC} of
- {'BMPString',{_,_}} ->
- exit({error,{asn1,
- {'not implemented',
- "BMPString with PermittedAlphabet "
- "constraint"}}});
- _ ->
- ok
- end,
- NumBits = get_NumBits(C,StringType),
- CharInTab = get_CharInTab(C,StringType),
- case SizeC of
- 0 ->
- emit({"{[],",BytesVar,"}"});
- _ ->
- emit({"?RT_PER:decode_known_multiplier_string(",
- {asis,StringType},",",{asis,SizeC},",",NumBits,
- ",",{asis,CharInTab},",",BytesVar,")"})
- end.
+emit_enc_k_m_string(SizeC, NumBits, CharOutTab, Value) ->
+ call(per, encode_known_multiplier_string,
+ [{asis,SizeC},NumBits,{asis,CharOutTab},Value]).
%% copied from run time module
-get_CharOutTab(C,StringType) ->
- get_CharTab(C,StringType,out).
-
-get_CharInTab(C,StringType) ->
- get_CharTab(C,StringType,in).
-
-get_CharTab(C,StringType,InOut) ->
+get_CharOutTab(C, StringType) ->
case get_constraint(C,'PermittedAlphabet') of
{'SingleValue',Sv} ->
- get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ get_CharTab2(C, StringType, hd(Sv), lists:max(Sv), Sv);
no ->
case StringType of
'IA5String' ->
{0,16#7F,notab};
'VisibleString' ->
- get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ get_CharTab2(C, StringType, 16#20, 16#7F, notab);
'PrintableString' ->
Chars = lists:sort(
" '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
- get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ get_CharTab2(C, StringType, hd(Chars),
+ lists:max(Chars), Chars);
'NumericString' ->
- get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ get_CharTab2(C, StringType, 16#20, $9, " 0123456789");
'UniversalString' ->
{0,16#FFFFFFFF,notab};
'BMPString' ->
@@ -329,18 +283,13 @@ get_CharTab(C,StringType,InOut) ->
end
end.
-get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+get_CharTab2(C, StringType, Min, Max, Chars) ->
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
+ {Min,Max,create_char_tab(Min,Chars)}
end.
create_char_tab(Min,L) ->
@@ -409,7 +358,7 @@ charbits1(NumOfChars) ->
%% copied from run time module
-emit_enc_octet_string(_Erules,Constraint,Value) ->
+emit_enc_octet_string(Erules, Constraint, Value) ->
case get_constraint(Constraint,'SizeConstraint') of
0 ->
emit({" []"});
@@ -417,48 +366,49 @@ 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})
+ call(Erules, encode_octet_string,
+ [{asis,C},false,Value])
end.
emit_enc_integer_case(Value) ->
@@ -545,71 +495,12 @@ emit_enc_integer(_Erule,[{_,{Lb,Ub},Range,_}],Value) when Range =< 65536 ->
nl," end",nl]),
emit_enc_integer_end_case();
+emit_enc_integer(Erule, [{'ValueRange',{Lb,Ub}=VR}], Value)
+ when is_integer(Lb), is_integer(Ub) ->
+ call(Erule, encode_constrained_number, [{asis,VR},Value]);
-emit_enc_integer(_Erule,C,Value) ->
- emit({" ?RT_PER:encode_integer(",{asis,C},",",Value,")"}).
-
-
-
-
-enc_enum_cases(Value,NewList) ->
- asn1ct_name:new(tmpval),
- TmpVal = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
- Cases=enc_enum_cases1(NewList),
- lists:flatten(io_lib:format("(case ~s of "++Cases++
- "~s ->exit({error,"
- "{asn1,{enumerated,~s}}})"
- " end)",
- [Value,TmpVal,TmpVal])).
-enc_enum_cases1(NNL) ->
- enc_enum_cases1(NNL,0).
-enc_enum_cases1([H|T],Index) ->
- io_lib:format("~w->~w;",[H,Index])++enc_enum_cases1(T,Index+1);
-enc_enum_cases1([],_) ->
- "".
-
-
-emit_enc_enumerated_cases(Erule, C, [H], Count) ->
- emit_enc_enumerated_case(Erule, C, H, Count),
- case H of
- 'EXT_MARK' ->
- ok;
- _ ->
- emit([";",nl])
- end,
- emit([nl,"EnumVal -> exit({error,{asn1, {enumerated_not_in_range, EnumVal}}})"]),
- emit([nl,"end"]);
-emit_enc_enumerated_cases(Erule, C, ['EXT_MARK'|T], _Count) ->
- emit_enc_enumerated_cases(Erule, C, T, 0);
-emit_enc_enumerated_cases(Erule, C, [H1,H2|T], Count) ->
- emit_enc_enumerated_case(Erule, C, H1, Count),
- emit([";",nl]),
- emit_enc_enumerated_cases(Erule, C, [H2|T], Count+1).
-
-
-%% The function clauses matching on tuples with first element
-%% asn1_enum, 1 or 0 and the atom 'EXT_MARK' are for ENUMERATED
-%% with extension mark.
-%% emit_enc_enumerated_case(_Erule,_C, {asn1_enum,High}, _) ->
-%% %% ENUMERATED with extensionmark
-%% %% value higher than the extension base and not
-%% %% present in the extension range.
-%% emit(["{asn1_enum,EnumV} when is_integer(EnumV), EnumV > ",High," -> ",
-%% "[1,?RT_PER:encode_small_number(EnumV)]"]);
-emit_enc_enumerated_case(_Erule,_C, {1,EnumName}, Count) ->
- %% ENUMERATED with extensionmark
- %% values higher than extension root
- emit(["'",EnumName,"' -> [1,?RT_PER:encode_small_number(",Count,")]"]);
-emit_enc_enumerated_case(_Erule,C, {0,EnumName}, Count) ->
- %% ENUMERATED with extensionmark
- %% values within extension root
-%% emit(["'",EnumName,"' -> [0,?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
- emit(["'",EnumName,"' -> ",{asis,[0|asn1rt_per_bin_rt2ct:encode_integer(C,Count)]}]);
-emit_enc_enumerated_case(_Erule, _C, 'EXT_MARK', _Count) ->
- true.
-%% %% This clause is invoked in case of an ENUMERATED without extension mark
-%% emit_enc_enumerated_case(_Erule,_C, EnumName, Count) ->
-%% emit(["'",EnumName,"' -> ",Count]).
+emit_enc_integer(Erule, C, Value) ->
+ call(Erule, encode_integer, [{asis,C},Value]).
get_constraint([{Key,V}],Key) ->
@@ -624,23 +515,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,69 +531,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 before generating SingleValue or ValueRange
- %% In case of ValueRange, also check for 'MIN and 'MAX'
- case lists:usort(SVList) of
- [N] ->
- [{'SingleValue',N}];
- L when is_list(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';
- _ -> 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,[]) ->
[];
@@ -796,9 +610,9 @@ gen_encode_objectfields(Erules,ClassName,[{typefield,Name,OptOrMand}|Rest],
emit([" if",nl,
" is_list(Val) ->",nl,
" NewVal = list_to_binary(Val),",nl,
- " [20,size(NewVal),NewVal];",nl,
+ " [20,byte_size(NewVal),NewVal];",nl,
" is_binary(Val) ->",nl,
- " [20,size(Val),Val]",nl,
+ " [20,byte_size(Val),Val]",nl,
" end"]),
[];
{false,{'DEFAULT',DefaultType}} ->
@@ -1175,7 +989,7 @@ gen_objset_enc(_Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
emit({indent(9),"is_list(Val) -> list_to_binary(Val);",nl}),
emit({indent(9),"true -> Val",nl}),
emit({indent(6),"end,",nl}),
- emit({indent(6),"Size = size(BinVal),",nl}),
+ emit({indent(6),"Size = byte_size(BinVal),",nl}),
emit({indent(6),"if",nl}),
emit({indent(9),"Size < 256 ->",nl}),
emit({indent(12),"[20,Size,BinVal];",nl}),
@@ -1380,7 +1194,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;
@@ -1396,77 +1209,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) ->
@@ -1492,7 +1270,7 @@ emit_inner_of_decfun(Type,_) when is_record(Type,type) ->
case Type#type.def of
Def when is_atom(Def) ->
emit({indent(9),Def," ->",nl,indent(12)}),
- gen_dec_prim(erules,Type,"Val");
+ gen_dec_prim(per, Type, "Val");
TRef when is_record(TRef,typereference) ->
T = TRef#typereference.val,
emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
@@ -1581,222 +1359,8 @@ gen_decode_user(Erules,D) when is_record(D,typedef) ->
-gen_dec_prim(Erules,Att,BytesVar) ->
- Typename = Att#type.def,
- Constraint = Att#type.constraint,
- case Typename of
- 'INTEGER' ->
- EffectiveConstr = effective_constraint(integer,Constraint),
- emit_dec_integer(EffectiveConstr,BytesVar);
-% emit({"?RT_PER:decode_integer(",BytesVar,",",
-% {asis,EffectiveConstr},")"});
- {'INTEGER',NamedNumberList} ->
- EffectiveConstr = effective_constraint(integer,Constraint),
- emit_dec_integer(EffectiveConstr,BytesVar,NamedNumberList);
-% emit({"?RT_PER:decode_integer(",BytesVar,",",
-% {asis,EffectiveConstr},",",
-% {asis,NamedNumberList},")"});
-
- 'REAL' ->
- emit(["?RT_PER:decode_real(",BytesVar,")"]);
-
- {'BIT STRING',NamedNumberList} ->
- case get(compact_bit_string) of
- true ->
- emit({"?RT_PER:decode_compact_bit_string(",
- BytesVar,",",{asis,Constraint},",",
- {asis,NamedNumberList},")"});
- _ ->
- emit({"?RT_PER:decode_bit_string(",BytesVar,",",
- {asis,Constraint},",",
- {asis,NamedNumberList},")"})
- end;
- 'NULL' ->
- emit({"?RT_PER:decode_null(",
- BytesVar,")"});
- 'OBJECT IDENTIFIER' ->
- emit({"?RT_PER:decode_object_identifier(",
- BytesVar,")"});
- 'RELATIVE-OID' ->
- emit({"?RT_PER:decode_relative_oid(",
- 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);
- 'BOOLEAN'->
- emit({"?RT_PER:decode_boolean(",BytesVar,")"});
-
- 'OCTET STRING' ->
- emit_dec_octet_string(Constraint,BytesVar);
-
- 'NumericString' ->
- emit_dec_known_multiplier_string('NumericString',
- Constraint,BytesVar);
- TString when TString == 'TeletexString';
- TString == 'T61String' ->
- emit({"?RT_PER:decode_TeletexString(",BytesVar,",",
- {asis,Constraint},")"});
-
- 'VideotexString' ->
- emit({"?RT_PER:decode_VideotexString(",BytesVar,",",
- {asis,Constraint},")"});
-
- 'UTCTime' ->
- emit_dec_known_multiplier_string('VisibleString',
- Constraint,BytesVar);
- 'GeneralizedTime' ->
- emit_dec_known_multiplier_string('VisibleString',
- Constraint,BytesVar);
- 'GraphicString' ->
- emit({"?RT_PER:decode_GraphicString(",BytesVar,",",
- {asis,Constraint},")"});
-
- 'VisibleString' ->
- emit_dec_known_multiplier_string('VisibleString',
- Constraint,BytesVar);
- 'GeneralString' ->
- emit({"?RT_PER:decode_GeneralString(",BytesVar,",",
- {asis,Constraint},")"});
-
- 'PrintableString' ->
- emit_dec_known_multiplier_string('PrintableString',
- Constraint,BytesVar);
- 'IA5String' ->
- emit_dec_known_multiplier_string('IA5String',Constraint,BytesVar);
-
- 'BMPString' ->
- emit_dec_known_multiplier_string('BMPString',Constraint,BytesVar);
-
- 'UniversalString' ->
- emit_dec_known_multiplier_string('UniversalString',
- Constraint,BytesVar);
-
- 'UTF8String' ->
- emit({"?RT_PER:decode_UTF8String(",BytesVar,")"});
- 'ANY' ->
- emit(["?RT_PER:decode_open_type(",BytesVar,",",
- {asis,Constraint}, ")"]);
- 'ASN1_OPEN_TYPE' ->
- case Constraint of
- [#'Externaltypereference'{type=Tname}] ->
- emit(["fun(FBytes) ->",nl,
- " {XTerm,XBytes} = "]),
- emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
- emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
- emit([" {YTerm,XBytes} end(",BytesVar,")"]);
- [#type{def=#'Externaltypereference'{type=Tname}}] ->
- emit(["fun(FBytes) ->",nl,
- " {XTerm,XBytes} = "]),
- emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
- emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
- emit([" {YTerm,XBytes} end(",BytesVar,")"]);
- _ ->
- emit(["?RT_PER:decode_open_type(",BytesVar,",[])"])
- end;
- #'ObjectClassFieldType'{} ->
- case asn1ct_gen:get_inner(Att#type.def) of
- {fixedtypevaluefield,_,InnerType} ->
- gen_dec_prim(Erules,InnerType,BytesVar);
- T ->
- gen_dec_prim(Erules,Att#type{def=T},BytesVar)
- end;
- Other ->
- exit({'cant decode' ,Other})
- end.
-
-
-emit_dec_integer(C,BytesVar,NNL) ->
- asn1ct_name:new(tmpterm),
- asn1ct_name:new(buffer),
- Tmpterm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
- Buffer = asn1ct_gen:mk_var(asn1ct_name:curr(buffer)),
- emit({" begin {",{curr,tmpterm},",",{curr,buffer},"} = ",nl}),
- emit_dec_integer(C,BytesVar),
- emit({",",nl," case ",Tmpterm," of",nl}),
- lists:map(fun({Name,Int})->emit({" ",Int," -> {",{asis,Name},",",
- Buffer,"};",nl});
- (_)-> exit({error,{asn1,{"error in named number list",NNL}}})
- end,
- NNL),
- emit({" _ -> {",Tmpterm,",",Buffer,"}",nl}),
- emit({" end",nl}), % end of case
- emit(" end"). % end of begin
-
-emit_dec_integer([{'SingleValue',Int}],BytesVar) when 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([],_,_) ->
- "".
+gen_dec_prim(Erules, Att, BytesVar) ->
+ asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar).
%% 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
diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl
new file mode 100644
index 0000000000..4b2c3b1b65
--- /dev/null
+++ b/lib/asn1/src/asn1ct_imm.erl
@@ -0,0 +1,766 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT 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_raw_bitstring/2,
+ per_dec_boolean/0,per_dec_enumerated/2,per_dec_enumerated/3,
+ per_dec_extension_map/1,
+ per_dec_integer/2,per_dec_k_m_string/3,
+ per_dec_length/3,per_dec_named_integer/3,
+ per_dec_octet_string/2,per_dec_open_type/1,per_dec_real/1,
+ per_dec_restricted_string/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([{V,_}], _Aligned) ->
+ {value,V};
+per_dec_enumerated(NamedList0, Aligned) ->
+ Ub = length(NamedList0) - 1,
+ Constraint = [{'ValueRange',{0,Ub}}],
+ Int = per_dec_integer(Constraint, Aligned),
+ EnumTail = case matched_range(Int) of
+ {0,Ub} ->
+ %% The error case can never happen.
+ [];
+ _ ->
+ [enum_error]
+ end,
+ NamedList = per_dec_enumerated_fix_list(NamedList0, EnumTail, 0),
+ {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_k_m_string(StringType, Constraint, Aligned) ->
+ SzConstr = get_constraint(Constraint, 'SizeConstraint'),
+ N = string_num_bits(StringType, Constraint, Aligned),
+ Imm = dec_string(SzConstr, N, Aligned),
+ Chars = char_tab(Constraint, StringType, N),
+ convert_string(N, Chars, Imm).
+
+per_dec_octet_string(Constraint, Aligned) ->
+ dec_string(Constraint, 8, Aligned).
+
+per_dec_raw_bitstring(Constraint, Aligned) ->
+ dec_string(Constraint, 1, Aligned).
+
+per_dec_open_type(Aligned) ->
+ {get_bits,decode_unconstrained_length(true, Aligned),
+ [8,binary,{align,Aligned}]}.
+
+per_dec_real(Aligned) ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,real_common,decode_real,[V]},
+ com,Buf,"}"])
+ end,
+ {call,Dec,
+ {get_bits,decode_unconstrained_length(true, Aligned),
+ [8,binary,{align,Aligned}]}}.
+
+per_dec_restricted_string(Aligned) ->
+ DecLen = decode_unconstrained_length(true, Aligned),
+ {get_bits,DecLen,[8,binary]}.
+
+
+%%%
+%%% 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) ->
+ asn1ct_func:call(per_common,
+ 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.
+
+matched_range({get_bits,Bits0,[U|Flags]}) when is_integer(U) ->
+ case lists:member(signed, Flags) of
+ false ->
+ Bits = U*Bits0,
+ {0,(1 bsl Bits) - 1};
+ true ->
+ unknown
+ end;
+matched_range(_Op) -> unknown.
+
+string_num_bits(StringType, Constraint, Aligned) ->
+ case get_constraint(Constraint, 'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv), Aligned);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128, Aligned);
+ 'VisibleString' ->
+ charbits(95, Aligned);
+ 'PrintableString' ->
+ charbits(74, Aligned);
+ 'NumericString' ->
+ charbits(11, Aligned);
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+charbits(NumChars, false) ->
+ uper_num_bits(NumChars);
+charbits(NumChars, true) ->
+ 1 bsl uper_num_bits(uper_num_bits(NumChars)).
+
+convert_string(8, notab, Imm) ->
+ {convert,binary_to_list,Imm};
+convert_string(NumBits, notab, Imm) when NumBits < 8 ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,per_common,decode_chars,
+ [V,NumBits]},com,Buf,"}"])
+ end,
+ {call,Dec,Imm};
+convert_string(NumBits, notab, Imm) when NumBits =:= 16 ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,per_common,decode_chars_16bit,
+ [V]},com,Buf,"}"])
+ end,
+ {call,Dec,Imm};
+convert_string(NumBits, notab, Imm) ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,per_common,decode_big_chars,
+ [V,NumBits]},com,Buf,"}"])
+ end,
+ {call,Dec,Imm};
+convert_string(NumBits, Chars, Imm) ->
+ Dec = fun(V, Buf) ->
+ emit(["{",{call,per_common,decode_chars,
+ [V,NumBits,{asis,Chars}]},com,Buf,"}"])
+ end,
+ {call,Dec,Imm}.
+
+char_tab(C, StringType, NumBits) ->
+ case get_constraint(C, 'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ char_tab_1(Sv, NumBits);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ notab;
+ 'VisibleString' ->
+ notab;
+ 'PrintableString' ->
+ Chars = " '()+,-./0123456789:=?"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz",
+ char_tab_1(Chars, NumBits);
+ 'NumericString' ->
+ char_tab_1(" 0123456789", NumBits);
+ 'UniversalString' ->
+ notab;
+ 'BMPString' ->
+ notab
+ end
+ end.
+
+char_tab_1(Chars, NumBits) ->
+ Max = lists:max(Chars),
+ BitValMax = (1 bsl NumBits) - 1,
+ if
+ Max =< BitValMax ->
+ notab;
+ true ->
+ list_to_tuple(lists:sort(Chars))
+ end.
+
+%%%
+%%% 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,V}=Term, A) when is_integer(V); is_atom(V) ->
+ {Term,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,V}, Buf0, St0) when is_atom(V) ->
+ {{"'"++atom_to_list(V)++"'",Buf0},[],St0};
+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([{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) ->
+ case "/" ++ bit_flags_1(Acc, "") of
+ "/unit:1" -> [];
+ Opts -> Opts
+ end.
+
+
+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..764555c4d2 100644
--- a/lib/asn1/src/asn1ct_value.erl
+++ b/lib/asn1/src/asn1ct_value.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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(M, Type);
'ASN1_OPEN_TYPE' ->
case Type#type.constraint of
[#'Externaltypereference'{type=TrefConstraint}] ->
@@ -65,7 +65,7 @@ from_type(M,Typename,Type) when is_record(Type,type) ->
end;
{constructed,bif} when Typename == ['EXTERNAL'] ->
Val=from_type_constructed(M,Typename,InnerType,Type),
- asn1rt_check:transform_to_EXTERNAL1994(Val);
+ asn1ct_eval_ext:transform_to_EXTERNAL1994(Val);
{constructed,bif} ->
from_type_constructed(M,Typename,InnerType,Type)
end;
@@ -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(M, D) ->
C = D#type.constraint,
case D#type.def of
'INTEGER' ->
@@ -212,18 +212,7 @@ from_type_prim(D,Erule) ->
NN = [X||{X,_} <- NamedNumberList],
case NN of
[] ->
- Bl1 =lists:reverse(adjust_list(size_random(C),[1,0,1,1])),
- Bl2 = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,Bl1)),
- case {length(Bl2),get_constraint(C,'SizeConstraint')} of
- {Len,Len} ->
- Bl2;
- {_Len,Int} when is_integer(Int) ->
- Bl1;
- {Len,{Min,_}} when Min > Len ->
- Bl1;
- _ ->
- Bl2
- end;
+ random_unnamed_bit_string(M, C);
_ ->
[lists:nth(random(length(NN)),NN)]
end;
@@ -303,12 +292,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 ->
@@ -325,6 +309,32 @@ c_string(C,Default) ->
Default
end.
+random_unnamed_bit_string(M, C) ->
+ Bl1 = lists:reverse(adjust_list(size_random(C), [1,0,1,1])),
+ Bl2 = lists:reverse(lists:dropwhile(fun(0)-> true;
+ (1) -> false
+ end,Bl1)),
+ Val = case {length(Bl2),get_constraint(C, 'SizeConstraint')} of
+ {Len,Len} ->
+ Bl2;
+ {_Len,Int} when is_integer(Int) ->
+ Bl1;
+ {Len,{Min,_}} when Min > Len ->
+ Bl1;
+ _ ->
+ Bl2
+ end,
+ case M:bit_string_format() of
+ legacy ->
+ Val;
+ bitstring ->
+ << <<B:1>> || B <- Val >>;
+ compact ->
+ BitString = << <<B:1>> || B <- Val >>,
+ PadLen = (8 - (bit_size(BitString) band 7)) band 7,
+ {PadLen,<<BitString/bitstring,0:PadLen>>}
+ end.
+
%% FIXME:
%% random_sign(integer) ->
%% case random(2) of
@@ -440,20 +450,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
deleted file mode 100644
index 22f9f2ecfd..0000000000
--- a/lib/asn1/src/asn1rt_ber_bin.erl
+++ /dev/null
@@ -1,2471 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
--module(asn1rt_ber_bin).
-
-%% encoding / decoding of BER
-
--export([decode/1]).
--export([fixoptionals/2,split_list/2,cindex/3,restbytes2/3,
- list_to_record/2,
- encode_tag_val/1,decode_tag/1,peek_tag/1,
- check_tags/3, encode_tags/3]).
--export([encode_boolean/2,decode_boolean/3,
- encode_integer/3,encode_integer/4,
- decode_integer/4,decode_integer/5,encode_enumerated/2,
- encode_enumerated/4,decode_enumerated/5,
- encode_real/2, 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]).
-
--include("asn1_records.hrl").
-
-% the encoding of class of tag bits 8 and 7
--define(UNIVERSAL, 0).
--define(APPLICATION, 16#40).
--define(CONTEXT, 16#80).
--define(PRIVATE, 16#C0).
-
-%%% primitive or constructed encoding % bit 6
--define(PRIMITIVE, 0).
--define(CONSTRUCTED, 2#00100000).
-
-%%% The tag-number for universal types
--define(N_BOOLEAN, 1).
--define(N_INTEGER, 2).
--define(N_BIT_STRING, 3).
--define(N_OCTET_STRING, 4).
--define(N_NULL, 5).
--define(N_OBJECT_IDENTIFIER, 6).
--define(N_OBJECT_DESCRIPTOR, 7).
--define(N_EXTERNAL, 8).
--define(N_REAL, 9).
--define(N_ENUMERATED, 10).
--define(N_EMBEDDED_PDV, 11).
--define(N_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)}}}).
-
-
-%%===============================================================================
-%% Decode a tag
-%%
-%% decode_tag(OctetListBuffer) -> {{Class, Form, TagNo}, RestOfBuffer, RemovedBytes}
-%%===============================================================================
-
-%% multiple octet tag
-decode_tag(<<Class:2, Form:1, 31:5, Buffer/binary>>) ->
- {TagNo, Buffer1, RemovedBytes} = decode_tag(Buffer, 0, 1),
- {{(Class bsl 6), (Form bsl 5), TagNo}, Buffer1, RemovedBytes};
-
-%% single tag (< 31 tags)
-decode_tag(<<Class:2,Form:1,TagNo:5, Buffer/binary>>) ->
- {{(Class bsl 6), (Form bsl 5), TagNo}, Buffer, 1}.
-
-%% last partial tag
-decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck, RemovedBytes) ->
- TagNo = (TagAck bsl 7) bor PartialTag,
- %%<<TagNo>> = <<TagAck:1, PartialTag:7>>,
- {TagNo, Buffer, RemovedBytes+1};
-% more tags
-decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck, RemovedBytes) ->
- TagAck1 = (TagAck bsl 7) bor PartialTag,
- %%<<TagAck1:16>> = <<TagAck:1, PartialTag:7,0:8>>,
- decode_tag(Buffer, TagAck1, RemovedBytes+1).
-
-%%------------------------------------------------------------------
-%% check_tags_i is the same as check_tags except that it stops and
-%% returns the remaining tags not checked when it encounters an
-%% indefinite length field
-%% only called internally within this module
-
-check_tags_i([Tag], Buffer, OptOrMand) -> % optimized very usual case
- {[],check_one_tag(Tag, Buffer, OptOrMand)};
-check_tags_i(Tags, Buffer, OptOrMand) ->
- check_tags_i(Tags, Buffer, 0, OptOrMand).
-
-check_tags_i([Tag1,Tag2|TagRest], Buffer, Rb, OptOrMand)
- when Tag1#tag.type == 'IMPLICIT' ->
- check_tags_i([Tag1#tag{type=Tag2#tag.type}|TagRest], Buffer, Rb, OptOrMand);
-
-check_tags_i([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
- {Form_Length,Buffer2,Rb1} = check_one_tag(Tag1, Buffer, OptOrMand),
- case TagRest of
- [] -> {TagRest, {Form_Length, Buffer2, Rb + Rb1}};
- _ ->
- case Form_Length of
- {?CONSTRUCTED,_} ->
- {TagRest, {Form_Length, Buffer2, Rb + Rb1}};
- _ ->
- check_tags_i(TagRest, Buffer2, Rb + Rb1, mandatory)
- end
- end;
-
-check_tags_i([], Buffer, Rb, _) ->
- {[],{{0,0},Buffer,Rb}}.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% This function is called from generated code
-
-check_tags([Tag], Buffer, OptOrMand) -> % optimized very usual case
- check_one_tag(Tag, Buffer, OptOrMand);
-check_tags(Tags, Buffer, OptOrMand) ->
- check_tags(Tags, Buffer, 0, OptOrMand).
-
-check_tags([Tag1,Tag2|TagRest], Buffer, Rb, OptOrMand)
- when Tag1#tag.type == 'IMPLICIT' ->
- check_tags([Tag1#tag{type=Tag2#tag.type}|TagRest], Buffer, Rb, OptOrMand);
-
-check_tags([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
- {Form_Length,Buffer2,Rb1} = check_one_tag(Tag1, Buffer, OptOrMand),
- case TagRest of
- [] -> {Form_Length, Buffer2, Rb + Rb1};
- _ -> check_tags(TagRest, Buffer2, Rb + Rb1, mandatory)
- end;
-
-check_tags([], Buffer, Rb, _) ->
- {{0,0},Buffer,Rb}.
-
-check_one_tag(Tag=#tag{class=ExpectedClass,number=ExpectedNumber}, Buffer, OptOrMand) ->
- case catch decode_tag(Buffer) of
- {'EXIT',_Reason} ->
- tag_error(no_data,Tag,Buffer,OptOrMand);
- {{ExpectedClass,Form,ExpectedNumber},Buffer2,Rb} ->
- {{L,Buffer3},RemBytes2} = decode_length(Buffer2),
- {{Form,L}, Buffer3, RemBytes2+Rb};
- {ErrorTag,_,_} ->
- tag_error(ErrorTag, Tag, Buffer, OptOrMand)
- end.
-
-tag_error(ErrorTag, Tag, Buffer, OptOrMand) ->
- case OptOrMand of
- mandatory ->
- exit({error,{asn1, {invalid_tag,
- {ErrorTag, Tag, Buffer}}}});
- _ ->
- exit({error,{asn1, {no_optional_tag,
- {ErrorTag, Tag, Buffer}}}})
- end.
-%%=======================================================================
-%%
-%% Encode all tags in the list Tags and return a possibly deep list of
-%% bytes with tag and length encoded
-%%
-%% prepend_tags(Tags, BytesSoFar, LenSoFar) -> {Bytes, Len}
-encode_tags(Tags, BytesSoFar, LenSoFar) ->
- NewTags = encode_tags1(Tags, []),
- %% NewTags contains the resulting tags in reverse order
- encode_tags2(NewTags, BytesSoFar, LenSoFar).
-
-%encode_tags2([#tag{class=?UNIVERSAL,number=No}|Trest], BytesSoFar, LenSoFar) ->
-% {Bytes2,L2} = encode_length(LenSoFar),
-% encode_tags2(Trest,[[No|Bytes2],BytesSoFar], LenSoFar + 1 + L2);
-encode_tags2([Tag|Trest], BytesSoFar, LenSoFar) ->
- {Bytes1,L1} = encode_one_tag(Tag),
- {Bytes2,L2} = encode_length(LenSoFar),
- encode_tags2(Trest, [Bytes1,Bytes2|BytesSoFar],
- LenSoFar + L1 + L2);
-encode_tags2([], BytesSoFar, LenSoFar) ->
- {BytesSoFar,LenSoFar}.
-
-encode_tags1([Tag1, Tag2| Trest], Acc) when Tag1#tag.type =:= 'IMPLICIT' ->
- encode_tags1([Tag1#tag{type=Tag2#tag.type,form=Tag2#tag.form}|Trest],Acc);
-encode_tags1([Tag1 | Trest], Acc) ->
- encode_tags1(Trest, [Tag1|Acc]);
-encode_tags1([], Acc) ->
- Acc. % the resulting tags are returned in reverse order
-
-encode_one_tag(Bin) when is_binary(Bin) ->
- {Bin,byte_size(Bin)};
-encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
- NewForm = case Type of
- 'EXPLICIT' ->
- ?CONSTRUCTED;
- _ ->
- Form
- end,
- Bytes = encode_tag_val({Class,NewForm,No}),
- {Bytes,size(Bytes)}.
-
-%%===============================================================================
-%% Change the tag (used when an implicit tagged type has a reference to something else)
-%% The constructed bit in the tag is taken from the tag to be replaced.
-%%
-%% change_tag(NewTag,[Tag,Buffer]) -> [NewTag,Buffer]
-%%===============================================================================
-
-%change_tag({NewClass,NewTagNr}, Buffer) ->
-% {{OldClass, OldForm, OldTagNo}, Buffer1, RemovedBytes} = decode_tag(lists:flatten(Buffer)),
-% [encode_tag_val({NewClass, OldForm, NewTagNr}) | Buffer1].
-
-
-%%===============================================================================
-%%
-%% This comment is valid for all the encode/decode functions
-%%
-%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
-%% used for PER-coding but not for BER-coding.
-%%
-%% Val = Value. If Val is an atom then it is a symbolic integer value
-%% (i.e the atom must be one of the names in the NamedNumberList).
-%% The NamedNumberList is used to translate the atom to an integer value
-%% before encoding.
-%%
-%%===============================================================================
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% encode_open_type(Value) -> CompleteList
-%% Value = list of bytes of an already encoded value (the list must be flat)
-%% | binary
-
-%% This version does not consider Explicit tagging of the open type. It
-%% is only left because of backward compatibility.
-encode_open_type(Val) when 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
-%%============================================================================
-%%
-%% encode real value
-%%============================================================================
-
-%% only base 2 internally so far!!
-encode_real(_C,0, DoTag) ->
- dotag(DoTag, ?N_REAL, {[],0});
-encode_real(_C,'PLUS-INFINITY', DoTag) ->
- dotag(DoTag, ?N_REAL, {[64],1});
-encode_real(_C,'MINUS-INFINITY', DoTag) ->
- dotag(DoTag, ?N_REAL, {[65],1});
-encode_real(C,Val, DoTag) when is_tuple(Val); is_list(Val) ->
- dotag(DoTag, ?N_REAL, encode_real(C,Val)).
-
-%%%%%%%%%%%%%%
-%% only base 2 encoding!
-%% binary encoding:
-%% +------------+ +------------+ +-+-+-+-+---+---+
-%% | (tag)9 | | n + p + 1 | |1|S|BB |FF |EE |
-%% +------------+ +------------+ +-+-+-+-+---+---+
-%%
-%% +------------+ +------------+
-%% | | | |
-%% +------------+ ...+------------+
-%% n octets for exponent
-%%
-%% +------------+ +------------+
-%% | | | |
-%% +------------+ ...+------------+
-%% p octets for pos mantissa
-%%
-%% S is 0 for positive sign
-%% 1 for negative sign
-%% BB: encoding base, 00 = 2, (01 = 8, 10 = 16)
-%% 01 and 10 not used
-%% FF: scale factor 00 = 0 (used in base 2 encoding)
-%% EE: encoding of the exponent:
-%% 00 - on the following octet
-%% 01 - on the 2 following octets
-%% 10 - on the 3 following octets
-%% 11 - encoding of the length of the two's-complement encoding of
-%% exponent on the following octet, and two's-complement
-%% encoding of exponent on the other octets.
-%%
-%% In DER and base 2 encoding the mantissa is encoded as value 0 or
-%% bit shifted until it is an odd number. Thus, do this for BER as
-%% well.
-%% This interface also used by RT_COMMON
-encode_real(_C,{Mantissa, Base, Exponent}) when Base =:= 2 ->
-%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
- {Man,ExpAdd} = truncate_zeros(Mantissa), %% DER adjustment
- Exp = Exponent + ExpAdd,
- OctExp = if Exp >= 0 -> list_to_binary(encode_integer_pos(Exp, []));
- true -> list_to_binary(encode_integer_neg(Exp, []))
- end,
-%% ok = io:format("OctExp: ~w~n",[OctExp]),
- SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval
- true -> 1
- end,
-%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
- SFactor = 0,
- OctExpLen = size(OctExp),
- if OctExpLen > 255 ->
- exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
- true -> true %% make real assert later..
- end,
- {LenCode, EOctets} = case OctExpLen of % bit 2,1
- 1 -> {0, OctExp};
- 2 -> {1, OctExp};
- 3 -> {2, OctExp};
- _ -> {3, <<OctExpLen, OctExp/binary>>}
- end,
- BB = 0, %% 00 for base 2
- FirstOctet = <<1:1,SignBit:1,BB:2,SFactor:2,LenCode:2>>,
- OctMantissa = if Man > 0 -> list_to_binary(minimum_octets(Man));
- true -> list_to_binary(minimum_octets(-(Man))) % signbit keeps track of sign
- end,
- %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
- Bin = <<FirstOctet/binary, EOctets/binary, OctMantissa/binary>>,
- {Bin, size(Bin)};
-encode_real(C,{Mantissa,Base,Exponent})
- when Base =:= 10, is_integer(Mantissa), is_integer(Exponent) ->
- %% always encode as NR3 due to DER on the format
- %% mmmm.Eseeee where
- %% m := digit
- %% s := '-' | '+' | []
- %% '+' only allowed in +0
- %% e := digit
- %% ex: 1234.E-5679
-%% {Man,AddExp} = truncate_zeros(Mantissa,0),
-%% ManNum = trunc(Mantissa),
-%% {TruncatedMan,NumZeros} = truncate_zeros10(Mantissa),
- ManStr = integer_to_list(Mantissa),
-
- encode_real_as_string(C,ManStr,Exponent);
-encode_real(_C,{_,Base,_}) ->
- exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}});
-%% base 10
-encode_real(C,Real) when is_list(Real) ->
- %% The Real string may come in as a NR1, NR2 or NR3 string.
- {Mantissa, Exponent} =
- case string:tokens(Real,"Ee") of
- [NR2] ->
- {NR2,0};
- [NR3MB,NR3E] ->
- %% remove beginning zeros
- {NR3MB,list_to_integer(NR3E)}
- end,
-
- %% .Decimal | Number | Number.Decimal
- ZeroDecimal =
- fun("0") -> "";
- (L) -> L
- end,
- {NewMantissa,LenDecimal} =
- case Mantissa of
- [$.|Dec] ->
- NewMan = remove_trailing_zeros(Dec),
- {NewMan,length(ZeroDecimal(NewMan))};
- _ ->
- case string:tokens(Mantissa,",.") of
- [Num] -> %% No decimal-mark
- {integer_to_list(list_to_integer(Num)),0};
- [Num,Dec] ->
- NewDec = ZeroDecimal(remove_trailing_zeros(Dec)),
- NewMan = integer_to_list(list_to_integer(Num)) ++ NewDec,
- {integer_to_list(list_to_integer(NewMan)),
- length(NewDec)}
- end
- end,
-
-% DER_Exponent = integer_to_list(Exponent - ExpReduce),
- encode_real_as_string(C,NewMantissa,Exponent - LenDecimal).
-
-encode_real_as_string(_C,Mantissa,Exponent)
- when is_list(Mantissa), is_integer(Exponent) ->
- %% Remove trailing zeros in Mantissa and add this to Exponent
- TruncMant = remove_trailing_zeros(Mantissa),
-
- ExpIncr = length(Mantissa) - length(TruncMant),
-
- ExpStr = integer_to_list(Exponent + ExpIncr),
-
- ExpBin =
- case ExpStr of
- "0" ->
- <<"E+0">>;
- _ ->
- ExpB = list_to_binary(ExpStr),
- <<$E,ExpB/binary>>
- end,
- ManBin = list_to_binary(TruncMant),
- NR3 = 3,
- {<<NR3,ManBin/binary,$.,ExpBin/binary>>,2 + size(ManBin) + size(ExpBin)}.
-
-remove_trailing_zeros(IntStr) ->
- case lists:dropwhile(fun($0)-> true;
- (_) -> false
- end, lists:reverse(IntStr)) of
- [] ->
- "0";
- ReversedIntStr ->
- lists:reverse(ReversedIntStr)
- end.
-
-truncate_zeros(Num) ->
- truncate_zeros(Num,0).
-truncate_zeros(0,Sum) ->
- {0,Sum};
-truncate_zeros(M,Sum) ->
- case M band 16#f =:= M band 16#e of
- true -> truncate_zeros(M bsr 1,Sum+1);
- _ -> {M,Sum}
- end.
-
-
-%%============================================================================
-%% decode real value
-%%
-%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
-%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
-%% RestBuff}
-%%
-%% only for base 2 decoding sofar!!
-%%============================================================================
-
-decode_real(Buffer, C, Tags, OptOrMand) ->
- NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_REAL}),
- decode_real_notag(Buffer, C, NewTags, OptOrMand).
-
-%% This interface used by RT_COMMON
-decode_real(Buffer,Len) ->
- decode_real2(Buffer,[],Len,0).
-
-decode_real_notag(Buffer, C, Tags, OptOrMand) ->
- {_RestTags, {{_,Len}, Buffer0, Rb0}} =
- check_tags_i(Tags, Buffer, OptOrMand),
- decode_real2(Buffer0, C, Len, Rb0).
-
-decode_real2(Buffer, _C, 0, _RemBytes) ->
- {0,Buffer};
-decode_real2(Buffer0, _C, Len, RemBytes1) ->
- <<First, Buffer2/binary>> = Buffer0,
- if
- First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
- First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
-%% First =:= 2#00000000 -> {0, Buffer2};
- First =:= 1 orelse First =:= 2 orelse First =:= 3 ->
- %% charcter string encoding of base 10
- {NRx,Rest} = split_binary(Buffer2,Len-1),
- {binary_to_list(NRx),Rest,Len};
- true ->
- %% have some check here to verify only supported bases (2)
- %% not base 8 or 16
- <<_B7:1,Sign:1,BB:2,_FF:2,EE:2>> = <<First>>,
- Base =
- case BB of
- 0 -> 2; % base 2, only one so far
- _ -> exit({error,{asn1, {non_supported_base, BB}}})
- end,
- {FirstLen, {Exp, Buffer3,_Rb2}, RemBytes2} =
- case EE of
- 0 -> {2, decode_integer2(1, Buffer2, RemBytes1), RemBytes1+1};
- 1 -> {3, decode_integer2(2, Buffer2, RemBytes1), RemBytes1+2};
- 2 -> {4, decode_integer2(3, Buffer2, RemBytes1), RemBytes1+3};
- 3 ->
- <<ExpLen1,RestBuffer/binary>> = Buffer2,
- { ExpLen1 + 2,
- decode_integer2(ExpLen1, RestBuffer, RemBytes1),
- RemBytes1+ExpLen1}
- end,
- %% io:format("FirstLen: ~w, Exp: ~w, Buffer3: ~w ~n",
-
- Length = Len - FirstLen,
- <<LongInt:Length/unit:8,RestBuff/binary>> = Buffer3,
- {{Mantissa, Buffer4}, RemBytes3} =
- if Sign =:= 0 ->
- %% io:format("sign plus~n"),
- {{LongInt, RestBuff}, 1 + Length};
- true ->
- %% io:format("sign minus~n"),
- {{-LongInt, RestBuff}, 1 + Length}
- end,
- {{Mantissa, Base, Exp}, Buffer4, RemBytes2+RemBytes3}
- end.
-
-
-%%============================================================================
-%% Bitstring value, ITU_T X.690 Chapter 8.6
-%%
-%% encode bitstring value
-%%
-%% bitstring NamedBitList
-%% Val can be of:
-%% - [identifiers] where only named identifers are set to one,
-%% the Constraint must then have some information of the
-%% bitlength.
-%% - [list of ones and zeroes] all bits
-%% - integer value representing the bitlist
-%% C is constrint Len, only valid when identifiers
-%%============================================================================
-
-encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,DoTag) when 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).
-
-
-%%%%%%%%%%%
-%% mk_object_val(Value) -> {OctetList, Len}
-%% returns a Val as a list of octets, the 8 bit is allways set to one except
-%% for the last octet, where its 0
-%%
-
-
-mk_object_val(Val) when Val =< 127 ->
- {[255 band Val], 1};
-mk_object_val(Val) ->
- mk_object_val(Val bsr 7, [Val band 127], 1).
-mk_object_val(0, Ack, Len) ->
- {Ack, Len};
-mk_object_val(Val, Ack, Len) ->
- mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
-
-
-
-%%============================================================================
-%% decode Object Identifier value
-%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
-%%============================================================================
-
-decode_object_identifier(Buffer, Tags, OptOrMand) ->
- NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,
- number=?N_OBJECT_IDENTIFIER}),
- decode_object_identifier_notag(Buffer, NewTags, OptOrMand).
-
-decode_object_identifier_notag(Buffer, Tags, OptOrMand) ->
- {RestTags, {FormLen, Buffer0, Rb0}} =
- check_tags_i(Tags, Buffer, OptOrMand),
-
- case FormLen of
- {?CONSTRUCTED,Len} ->
- {Buffer00,RestBytes} = split_list(Buffer0,Len),
- {Val01, Buffer01, Rb01} =
- decode_object_identifier_notag(Buffer00,
- RestTags, OptOrMand),
- {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
- {Val01, Buffer02, Rb0+Rb01+Rb02};
- {_,Len} ->
- {[AddedObjVal|ObjVals],Buffer01} =
- dec_subidentifiers(Buffer0,0,[],Len),
- {Val1, Val2} = if
- AddedObjVal < 40 ->
- {0, AddedObjVal};
- AddedObjVal < 80 ->
- {1, AddedObjVal - 40};
- true ->
- {2, AddedObjVal - 80}
- end,
- {list_to_tuple([Val1, Val2 | ObjVals]), Buffer01,
- Rb0+Len}
- end.
-
-dec_subidentifiers(Buffer,_Av,Al,0) ->
- {lists:reverse(Al),Buffer};
-dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al,Len) ->
- dec_subidentifiers(T,(Av bsl 7) + H,Al,Len-1);
-dec_subidentifiers(<<H,T/binary>>,Av,Al,Len) ->
- dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al],Len-1).
-
-%%============================================================================
-%% 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
-%%
-%% Encode length
-%%
-%% encode_length(Int | indefinite) ->
-%% [<127]| [128 + Int (<127),OctetList] | [16#80]
-%%============================================================================
-
-encode_length(indefinite) ->
- {[16#80],1}; % 128
-encode_length(L) when L =< 16#7F ->
- {[L],1};
-encode_length(L) ->
- Oct = minimum_octets(L),
- Len = length(Oct),
- if
- Len =< 126 ->
- {[ (16#80+Len) | Oct ],Len+1};
- true ->
- exit({error,{asn1, to_long_length_oct, Len}})
- end.
-
-
-%% Val must be >= 0
-minimum_octets(Val) ->
- minimum_octets(Val,[]).
-
-minimum_octets(0,Acc) ->
- Acc;
-minimum_octets(Val, Acc) ->
- minimum_octets((Val bsr 8),[Val band 16#FF | Acc]).
-
-
-%%===========================================================================
-%% Decode length
-%%
-%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
-%% {{Length, RestOctetsL}, NoRemovedBytes}
-%%===========================================================================
-
-decode_length(<<1:1,0:7,T/binary>>) ->
- {{indefinite, T}, 1};
-decode_length(<<0:1,Length:7,T/binary>>) ->
- {{Length,T},1};
-decode_length(<<1:1,LL:7,T/binary>>) ->
- <<Length:LL/unit:8,Rest/binary>> = T,
- {{Length,Rest}, LL+1}.
-
-%decode_length([128 | T]) ->
-% {{indefinite, T},1};
-%decode_length([H | T]) when H =< 127 ->
-% {{H, T},1};
-%decode_length([H | T]) ->
-% dec_long_length(H band 16#7F, T, 0, 1).
-
-
-%%dec_long_length(0, Buffer, Acc, Len) ->
-%% {{Acc, Buffer},Len};
-%%dec_long_length(Bytes, [H | T], Acc, Len) ->
-%% dec_long_length(Bytes - 1, T, (Acc bsl 8) + H, Len+1).
-
-%%===========================================================================
-%% Decode tag and length
-%%
-%% decode_tag_and_length(Buffer) -> {Tag, Len, RemainingBuffer, RemovedBytes}
-%%
-%%===========================================================================
-
-decode_tag_and_length(Buffer) ->
- {Tag, Buffer2, RemBytesTag} = decode_tag(Buffer),
- {{Len, Buffer3}, RemBytesLen} = decode_length(Buffer2),
- {Tag, Len, Buffer3, RemBytesTag+RemBytesLen}.
-
-
-%%============================================================================
-%% Check if valid tag
-%%
-%% check_if_valid_tag(Tag, List_of_valid_tags, OptOrMand) -> name of the tag
-%%============================================================================
-
-check_if_valid_tag(<<0,0,_/binary>>,_,_) ->
- asn1_EOC;
-check_if_valid_tag(<<>>, _, OptOrMand) ->
- check_if_valid_tag2_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)).
-
-dotag_universal(UniversalTag,Bytes,Len) when Len =< 16#7F->
- {[UniversalTag,Len,Bytes],2+Len};
-dotag_universal(UniversalTag,Bytes,Len) ->
- {EncLen,LenLen}=encode_length(Len),
- {[UniversalTag,EncLen,Bytes],1+LenLen+Len}.
-
-%% decoding postitive integer values.
-decode_integer2(Len,Bin = <<0:1,_:7,_Bs/binary>>,RemovedBytes) ->
- <<Int:Len/unit:8,Buffer2/binary>> = Bin,
- {Int,Buffer2,RemovedBytes};
-%% decoding negative integer values.
-decode_integer2(Len,<<1:1,B2:7,Bs/binary>>,RemovedBytes) ->
- <<N:Len/unit:8,Buffer2/binary>> = <<B2,Bs/binary>>,
- Int = N - (1 bsl (8 * Len - 1)),
- {Int,Buffer2,RemovedBytes}.
-
-%%decode_integer2(Len,Buffer,Acc,RemovedBytes) when (hd(Buffer) band 16#FF) =< 16#7F ->
-%% {decode_integer_pos(Buffer, 8 * (Len - 1)),skip(Buffer,Len),RemovedBytes};
-%%decode_integer2(Len,Buffer,Acc,RemovedBytes) ->
-%% {decode_integer_neg(Buffer, 8 * (Len - 1)),skip(Buffer,Len),RemovedBytes}.
-
-%%decode_integer_pos([Byte|Tail], Shift) ->
-%% (Byte bsl Shift) bor decode_integer_pos(Tail, Shift-8);
-%%decode_integer_pos([], _) -> 0.
-
-
-%%decode_integer_neg([Byte|Tail], Shift) ->
-%% (-128 + (Byte band 127) bsl Shift) bor decode_integer_pos(Tail, Shift-8).
-
-
-concat_bit_binaries([],Bin={_,_}) ->
- Bin;
-concat_bit_binaries({0,B1},{U2,B2}) ->
- {U2,<<B1/binary,B2/binary>>};
-concat_bit_binaries({U1,B1},{U2,B2}) ->
- S1 = (size(B1) * 8) - U1,
- S2 = (size(B2) * 8) - U2,
- PadBits = 8 - ((S1+S2) rem 8),
- {PadBits, <<B1:S1/binary-unit:1,B2:S2/binary-unit:1,0:PadBits>>};
-concat_bit_binaries(L1,L2) when 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) ->
- Tags;
-new_tags([T1 = #tag{type='IMPLICIT'},#tag{type=T2Type}|Rest],LastTag) ->
- new_tags([T1#tag{type=T2Type}|Rest],LastTag);
-new_tags(Tags,LastTag) ->
- case lists:last(Tags) of
- #tag{type='IMPLICIT'} ->
- Tags;
- _ ->
- Tags ++ [LastTag]
- end.
diff --git a/lib/asn1/src/asn1rt_ber_bin_v2.erl b/lib/asn1/src/asn1rt_ber_bin_v2.erl
deleted file mode 100644
index 9ff5017c68..0000000000
--- a/lib/asn1/src/asn1rt_ber_bin_v2.erl
+++ /dev/null
@@ -1,2035 +0,0 @@
-%%
-%% %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%
-%%
-%%
--module(asn1rt_ber_bin_v2).
-
-%% 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,
- encode_tag_val/1,
- encode_tags/3,
- skip_ExtensionAdditions/2]).
--export([encode_boolean/2,decode_boolean/2,
- encode_integer/3,encode_integer/4,
- decode_integer/3, decode_integer/4,
- encode_enumerated/2,
- encode_enumerated/4,decode_enumerated/4,
- encode_real/3,decode_real/2,
- encode_bit_string/4,decode_bit_string/4,
- decode_compact_bit_string/4,
- encode_octet_string/3,decode_octet_string/3,
- encode_null/2,decode_null/2,
- encode_relative_oid/2,decode_relative_oid/2,
- encode_object_identifier/2,decode_object_identifier/2,
- encode_restricted_string/4,decode_restricted_string/4,
- encode_universal_string/3,decode_universal_string/3,
- encode_UTF8_string/3,decode_UTF8_string/2,
- encode_BMP_string/3,decode_BMP_string/3,
- encode_generalized_time/3,decode_generalized_time/3,
- encode_utc_time/3,decode_utc_time/3,
- encode_length/1,decode_length/1,
- decode_tag_and_length/1]).
-
--export([encode_open_type/1,encode_open_type/2,
- decode_open_type/2,decode_open_type/3,
- decode_open_type_as_binary/2,
- 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).
--define(APPLICATION, 16#40).
--define(CONTEXT, 16#80).
--define(PRIVATE, 16#C0).
-
-%%% primitive or constructed encoding % bit 6
--define(PRIMITIVE, 0).
--define(CONSTRUCTED, 2#00100000).
-
-%%% The tag-number for universal types
--define(N_BOOLEAN, 1).
--define(N_INTEGER, 2).
--define(N_BIT_STRING, 3).
--define(N_OCTET_STRING, 4).
--define(N_NULL, 5).
--define(N_OBJECT_IDENTIFIER, 6).
--define(N_OBJECT_DESCRIPTOR, 7).
--define(N_EXTERNAL, 8).
--define(N_REAL, 9).
--define(N_ENUMERATED, 10).
--define(N_EMBEDDED_PDV, 11).
--define(N_SEQUENCE, 16).
--define(N_SET, 17).
--define(N_NumericString, 18).
--define(N_PrintableString, 19).
--define(N_TeletexString, 20).
--define(N_VideotexString, 21).
--define(N_IA5String, 22).
--define(N_UTCTime, 23).
--define(N_GeneralizedTime, 24).
--define(N_GraphicString, 25).
--define(N_VisibleString, 26).
--define(N_GeneralString, 27).
--define(N_UniversalString, 28).
--define(N_BMPString, 30).
-
-
-% the complete tag-word of built-in types
--define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
--define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
--define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
--define(T_OCTET_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 4). % can be CONSTRUCTED
--define(T_NULL, ?UNIVERSAL bor ?PRIMITIVE bor 5).
--define(T_OBJECT_IDENTIFIER,?UNIVERSAL bor ?PRIMITIVE bor 6).
--define(T_OBJECT_DESCRIPTOR,?UNIVERSAL bor ?PRIMITIVE bor 7).
--define(T_EXTERNAL, ?UNIVERSAL bor ?PRIMITIVE bor 8).
--define(T_REAL, ?UNIVERSAL bor ?PRIMITIVE bor 9).
--define(T_ENUMERATED, ?UNIVERSAL bor ?PRIMITIVE bor 10).
--define(T_EMBEDDED_PDV, ?UNIVERSAL bor ?PRIMITIVE bor 11).
--define(T_SEQUENCE, ?UNIVERSAL bor ?CONSTRUCTED bor 16).
--define(T_SET, ?UNIVERSAL bor ?CONSTRUCTED bor 17).
--define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
--define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
--define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
--define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
--define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
--define(T_UTCTime, ?UNIVERSAL bor ?PRIMITIVE bor 23).
--define(T_GeneralizedTime, ?UNIVERSAL bor ?PRIMITIVE bor 24).
--define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
--define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
--define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
--define(T_UniversalString, ?UNIVERSAL bor ?PRIMITIVE bor 28). %can be constructed
--define(T_BMPString, ?UNIVERSAL bor ?PRIMITIVE bor 30). %can be constructed
-
-% encode(Tlv={_Tag={?PRIMITIVE,_},_VList}) ->
-% encode_primitive(Tlv);
-% encode(Tlv) ->
-% encode_constructed(Tlv).
-
-encode(Tlv) ->
- encode(Tlv,erlang).
-
-encode(Tlv,_) when is_binary(Tlv) ->
- 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;
-encode(Tlv, _) ->
- encode_erl(Tlv).
-
-encode_erl({TlvTag,TlvVal}) when is_list(TlvVal) ->
- %% constructed form of value
- encode_tlv(TlvTag,TlvVal,?CONSTRUCTED);
-encode_erl({TlvTag,TlvVal}) ->
- encode_tlv(TlvTag,TlvVal,?PRIMITIVE).
-
-encode_tlv(TlvTag,TlvVal,Form) ->
- Tag = encode_tlv_tag(TlvTag,Form),
- {Val,VLen} = encode_tlv_val(TlvVal),
- {Len,_LLen} = encode_length(VLen),
- BinLen = list_to_binary(Len),
- <<Tag/binary,BinLen/binary,Val/binary>>.
-
-encode_tlv_tag(ClassTagNo,Form) ->
- Class = ClassTagNo bsr 16,
- encode_tag_val({Class bsl 6,Form,(ClassTagNo - (Class bsl 16))}).
-
-encode_tlv_val(TlvL) when is_list(TlvL) ->
- encode_tlv_list(TlvL,[]);
-encode_tlv_val(Bin) ->
- {Bin,size(Bin)}.
-
-encode_tlv_list([Tlv|Tlvs],Acc) ->
- EncTlv = encode_erl(Tlv),
- encode_tlv_list(Tlvs,[EncTlv|Acc]);
-encode_tlv_list([],Acc) ->
- Bin=list_to_binary(lists:reverse(Acc)),
- {Bin,size(Bin)}.
-
-decode(B) ->
- decode(B, erlang).
-
-%% 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)
- 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
- exit({error,{asn1,L}});
-handle_error({$2,T},L) -> % error in nif due to wrong tag
- exit({error,{asn1,{"bad tag after byte:",error_pos(T),L}}});
-handle_error({$3,T},L) -> % error in driver due to length error
- exit({error,{asn1,{"bad length field after byte:",
- error_pos(T),L}}});
-handle_error({$4,T},L) -> % error in driver due to indefinite length error
- exit({error,{asn1,
- {"indefinite length without end bytes after byte:",
- error_pos(T),L}}});
-handle_error({$5,T},L) -> % error in driver due to indefinite length error
- exit({error,{asn1,{"bad encoded value after byte:",
- error_pos(T),L}}});
-handle_error(ErrL,L) ->
- exit({error,{asn1,ErrL,L}}).
-
-error_pos([]) ->
- "unknown position";
-error_pos([B])->
- B;
-error_pos([B|Bs]) ->
- BS = 8 * length(Bs),
- B bsl BS + error_pos(Bs).
-
-decode_primitive(Bin) ->
- {Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
- case Form of
- 1 -> % constructed
- {{TagNo,decode_constructed(V)},Rest};
- 0 -> % primitive
- {{TagNo,V},Rest};
- 2 -> % constructed indefinite
- {Vlist,Rest2} = decode_constructed_indefinite(V,[]),
- {{TagNo,Vlist},Rest2}
- end.
-
-decode_constructed(Bin) when byte_size(Bin) =:= 0 ->
- [];
-decode_constructed(Bin) ->
- {Tlv,Rest} = decode_primitive(Bin),
- [Tlv|decode_constructed(Rest)].
-
-decode_constructed_indefinite(<<0,0,Rest/binary>>,Acc) ->
- {lists:reverse(Acc),Rest};
-decode_constructed_indefinite(Bin,Acc) ->
- {Tlv,Rest} = decode_primitive(Bin),
- decode_constructed_indefinite(Rest, [Tlv|Acc]).
-
-%% decode_primitive_incomplete/2 decodes an encoded message incomplete
-%% by help of the pattern attribute (first argument).
-decode_primitive_incomplete([[default,TagNo]],Bin) -> %default
- case decode_tag_and_length(Bin) of
- {Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,[],Rest);
- _ ->
- %{asn1_DEFAULT,Bin}
- asn1_NOVALUE
- end;
-decode_primitive_incomplete([[default,TagNo,Directives]],Bin) -> %default, constructed type, Directives points into this type
- case decode_tag_and_length(Bin) of
- {Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,Directives,Rest);
- _ ->
- %{asn1_DEFAULT,Bin}
- asn1_NOVALUE
- end;
-decode_primitive_incomplete([[opt,TagNo]],Bin) -> %optional
- case decode_tag_and_length(Bin) of
- {Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,[],Rest);
- _ ->
- %{{TagNo,asn1_NOVALUE},Bin}
- asn1_NOVALUE
- end;
-decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) -> %optional
- case decode_tag_and_length(Bin) of
- {Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,Directives,Rest);
- _ ->
- %{{TagNo,asn1_NOVALUE},Bin}
- asn1_NOVALUE
- end;
-%% An optional that shall be undecoded
-decode_primitive_incomplete([[opt_undec,Tag]],Bin) ->
- case decode_tag_and_length(Bin) of
- {_,Tag,_,_} ->
- decode_incomplete_bin(Bin);
- _ ->
- asn1_NOVALUE
- end;
-%% A choice alternative that shall be undecoded
-decode_primitive_incomplete([[alt_undec,TagNo]|RestAlts],Bin) ->
-% decode_incomplete_bin(Bin);
-% case decode_tlv(Bin) of
- case decode_tag_and_length(Bin) of
-% {{_Form,TagNo,_Len,_V},_R} ->
- {_,TagNo,_,_} ->
- decode_incomplete_bin(Bin);
- _ ->
- decode_primitive_incomplete(RestAlts,Bin)
- end;
-decode_primitive_incomplete([[alt,TagNo]|RestAlts],Bin) ->
- case decode_tag_and_length(Bin) of
- {_Form,TagNo,V,Rest} ->
- {{TagNo,V},Rest};
- _ ->
- decode_primitive_incomplete(RestAlts,Bin)
- end;
-decode_primitive_incomplete([[alt,TagNo,Directives]|RestAlts],Bin) ->
- case decode_tag_and_length(Bin) of
- {Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,Directives,Rest);
- _ ->
- decode_primitive_incomplete(RestAlts,Bin)
- end;
-decode_primitive_incomplete([[alt_parts,TagNo]],Bin) ->
- case decode_tag_and_length(Bin) of
- {_Form,TagNo,V,Rest} ->
- {{TagNo,V},Rest};
- _ ->
- asn1_NOVALUE
- end;
-decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) ->
- case decode_tag_and_length(Bin) of
- {_Form,TagNo,V,Rest} ->
- {{TagNo,decode_parts_incomplete(V)},Rest};
- _ ->
- decode_primitive_incomplete(RestAlts,Bin)
- end;
-decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) -> %incomlete decode
- decode_incomplete_bin(Bin);
-decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) ->
- case decode_tag_and_length(Bin) of
- {_Form,TagNo,V,Rest} ->
- {{TagNo,decode_parts_incomplete(V)},Rest};
- Err ->
- {error,{asn1,"tag failure",TagNo,Err}}
- end;
-decode_primitive_incomplete([mandatory|RestTag],Bin) ->
- {Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
- decode_incomplete2(Form,TagNo,V,RestTag,Rest);
-%% A choice that is a toptype or a mandatory component of a
-%% SEQUENCE or SET.
-decode_primitive_incomplete([[mandatory|Directives]],Bin) ->
- {Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
- decode_incomplete2(Form,TagNo,V,Directives,Rest);
-decode_primitive_incomplete([],Bin) ->
- decode_primitive(Bin).
-
-%% decode_parts_incomplete/1 receives a number of values encoded in
-%% sequence and returns the parts as unencoded binaries
-decode_parts_incomplete(<<>>) ->
- [];
-decode_parts_incomplete(Bin) ->
- {ok,Rest} = skip_tag(Bin),
- {ok,Rest2} = skip_length_and_value(Rest),
- LenPart = size(Bin) - size(Rest2),
- <<Part:LenPart/binary,RestBin/binary>> = Bin,
- [Part|decode_parts_incomplete(RestBin)].
-
-
-%% decode_incomplete2 checks if V is a value of a constructed or
-%% primitive type, and continues the decode propeerly.
-decode_incomplete2(_Form=2,TagNo,V,TagMatch,_) ->
- %% constructed indefinite length
- {Vlist,Rest2} = decode_constr_indef_incomplete(TagMatch,V,[]),
- {{TagNo,Vlist},Rest2};
-decode_incomplete2(1,TagNo,V,[TagMatch],Rest) when is_list(TagMatch) ->
- {{TagNo,decode_constructed_incomplete(TagMatch,V)},Rest};
-decode_incomplete2(1,TagNo,V,TagMatch,Rest) ->
- {{TagNo,decode_constructed_incomplete(TagMatch,V)},Rest};
-decode_incomplete2(0,TagNo,V,_TagMatch,Rest) ->
- {{TagNo,V},Rest}.
-
-decode_constructed_incomplete([Tags=[Ts]],Bin) when is_list(Ts) ->
- decode_constructed_incomplete(Tags,Bin);
-decode_constructed_incomplete(_TagMatch,<<>>) ->
- [];
-decode_constructed_incomplete([mandatory|RestTag],Bin) ->
- {Tlv,Rest} = decode_primitive(Bin),
- [Tlv|decode_constructed_incomplete(RestTag,Rest)];
-decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
- when Alt == alt_undec; Alt == alt; Alt == alt_parts ->
- {_Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
- case incomplete_choice_alt(TagNo,Directives) of
- {alt_undec,_} ->
- LenA = size(Bin)-size(Rest),
- <<A:LenA/binary,Rest/binary>> = Bin,
- A;
- {alt,InnerDirectives} ->
- {Tlv,Rest} = decode_primitive_incomplete(InnerDirectives,V),
- {TagNo,Tlv};
- {alt_parts,_} ->
- [{TagNo,decode_parts_incomplete(V)}];
- no_match -> %% if a choice alternative was encoded that
- %% was not specified in the config file,
- %% thus decode component anonomous.
- {Tlv,_}=decode_primitive(Bin),
- Tlv
- end;
-decode_constructed_incomplete([TagNo|RestTag],Bin) ->
-%% {Tlv,Rest} = decode_primitive_incomplete([TagNo],Bin),
- case decode_primitive_incomplete([TagNo],Bin) of
- {Tlv,Rest} ->
- [Tlv|decode_constructed_incomplete(RestTag,Rest)];
- asn1_NOVALUE ->
- decode_constructed_incomplete(RestTag,Bin)
- end;
-decode_constructed_incomplete([],Bin) ->
- {Tlv,Rest}=decode_primitive(Bin),
- [Tlv|decode_constructed_incomplete([],Rest)].
-
-decode_constr_indef_incomplete(_TagMatch,<<0,0,Rest/binary>>,Acc) ->
- {lists:reverse(Acc),Rest};
-decode_constr_indef_incomplete([Tag|RestTags],Bin,Acc) ->
-% {Tlv,Rest} = decode_primitive_incomplete([Tag],Bin),
- case decode_primitive_incomplete([Tag],Bin) of
- {Tlv,Rest} ->
- decode_constr_indef_incomplete(RestTags,Rest,[Tlv|Acc]);
- asn1_NOVALUE ->
- decode_constr_indef_incomplete(RestTags,Bin,Acc)
- end.
-
-
-decode_incomplete_bin(Bin) ->
- {ok,Rest} = skip_tag(Bin),
- {ok,Rest2} = skip_length_and_value(Rest),
- IncLen = size(Bin) - size(Rest2),
- <<IncBin:IncLen/binary,Ret/binary>> = Bin,
- {IncBin,Ret}.
-
-incomplete_choice_alt(TagNo,[[Alt,TagNo]|Directives]) ->
- {Alt,Directives};
-incomplete_choice_alt(TagNo,[D]) when is_list(D) ->
- incomplete_choice_alt(TagNo,D);
-incomplete_choice_alt(TagNo,[_H|Directives]) ->
- incomplete_choice_alt(TagNo,Directives);
-incomplete_choice_alt(_,[]) ->
- no_match.
-
-
-
-
-%% decode_selective(Pattern, Binary) the first argument is a pattern that tells
-%% what to do with the next element the second is the BER encoded
-%% message as a binary
-%% Returns {ok,Value} or {error,Reason}
-%% Value is a binary that in turn must be decoded to get the decoded
-%% value.
-decode_selective([],Binary) ->
- {ok,Binary};
-decode_selective([skip|RestPattern],Binary)->
- {ok,RestBinary}=skip_tag(Binary),
- {ok,RestBinary2}=skip_length_and_value(RestBinary),
- decode_selective(RestPattern,RestBinary2);
-decode_selective([[skip_optional,Tag]|RestPattern],Binary) ->
- case skip_optional_tag(Tag,Binary) of
- {ok,RestBinary} ->
- {ok,RestBinary2}=skip_length_and_value(RestBinary),
- decode_selective(RestPattern,RestBinary2);
- missing ->
- decode_selective(RestPattern,Binary)
- end;
-decode_selective([[choosen,Tag]],Binary) ->
- return_value(Tag,Binary);
-% case skip_optional_tag(Tag,Binary) of %may be optional/default
-% {ok,RestBinary} ->
-% {ok,Value} = get_value(RestBinary);
-% missing ->
-% {ok,<<>>}
-% end;
-decode_selective([[choosen,Tag]|RestPattern],Binary) ->
- case skip_optional_tag(Tag,Binary) of
- {ok,RestBinary} ->
- {ok,Value} = get_value(RestBinary),
- decode_selective(RestPattern,Value);
- missing ->
- {ok,<<>>}
- end;
-decode_selective(P,_) ->
- {error,{asn1,{partial_decode,"bad pattern",P}}}.
-
-return_value(Tag,Binary) ->
- {ok,{Tag,RestBinary}}=get_tag(Binary),
- {ok,{LenVal,_RestBinary2}} = get_length_and_value(RestBinary),
- {ok,<<Tag/binary,LenVal/binary>>}.
-
-
-%% skip_tag and skip_length_and_value are rutines used both by
-%% decode_partial_incomplete and decode_selective (decode/2).
-
-skip_tag(<<_:3,31:5,Rest/binary>>)->
- skip_long_tag(Rest);
-skip_tag(<<_:3,_Tag:5,Rest/binary>>) ->
- {ok,Rest}.
-
-skip_long_tag(<<1:1,_:7,Rest/binary>>) ->
- skip_long_tag(Rest);
-skip_long_tag(<<0:1,_:7,Rest/binary>>) ->
- {ok,Rest}.
-
-skip_optional_tag(<<>>,Binary) ->
- {ok,Binary};
-skip_optional_tag(<<Tag,RestTag/binary>>,<<Tag,Rest/binary>>) ->
- skip_optional_tag(RestTag,Rest);
-skip_optional_tag(_,_) ->
- missing.
-
-
-
-
-skip_length_and_value(Binary) ->
- case decode_length(Binary) of
- {indefinite,RestBinary} ->
- skip_indefinite_value(RestBinary);
- {Length,RestBinary} ->
- <<_:Length/unit:8,Rest/binary>> = RestBinary,
- {ok,Rest}
- end.
-
-skip_indefinite_value(<<0,0,Rest/binary>>) ->
- {ok,Rest};
-skip_indefinite_value(Binary) ->
- {ok,RestBinary}=skip_tag(Binary),
- {ok,RestBinary2} = skip_length_and_value(RestBinary),
- skip_indefinite_value(RestBinary2).
-
-get_value(Binary) ->
- case decode_length(Binary) of
- {indefinite,RestBinary} ->
- get_indefinite_value(RestBinary,[]);
- {Length,RestBinary} ->
- <<Value:Length/binary,_Rest/binary>> = RestBinary,
- {ok,Value}
- end.
-
-get_indefinite_value(<<0,0,_Rest/binary>>,Acc) ->
- {ok,list_to_binary(lists:reverse(Acc))};
-get_indefinite_value(Binary,Acc) ->
- {ok,{Tag,RestBinary}}=get_tag(Binary),
- {ok,{LenVal,RestBinary2}} = get_length_and_value(RestBinary),
- get_indefinite_value(RestBinary2,[LenVal,Tag|Acc]).
-
-get_tag(<<H:1/binary,Rest/binary>>) ->
- case H of
- <<_:3,31:5>> ->
- get_long_tag(Rest,[H]);
- _ -> {ok,{H,Rest}}
- end.
-get_long_tag(<<H:1/binary,Rest/binary>>,Acc) ->
- case H of
- <<0:1,_:7>> ->
- {ok,{list_to_binary(lists:reverse([H|Acc])),Rest}};
- _ ->
- get_long_tag(Rest,[H|Acc])
- end.
-
-get_length_and_value(Bin = <<0:1,Length:7,_T/binary>>) ->
- <<Len,Val:Length/binary,Rest/binary>> = Bin,
- {ok,{<<Len,Val/binary>>, Rest}};
-get_length_and_value(Bin = <<1:1,0:7,_T/binary>>) ->
- get_indefinite_length_and_value(Bin);
-get_length_and_value(<<1:1,LL:7,T/binary>>) ->
- <<Length:LL/unit:8,Rest/binary>> = T,
- <<Value:Length/binary,Rest2/binary>> = Rest,
- {ok,{<<1:1,LL:7,Length:LL/unit:8,Value/binary>>,Rest2}}.
-
-get_indefinite_length_and_value(<<H,T/binary>>) ->
- get_indefinite_length_and_value(T,[H]).
-
-get_indefinite_length_and_value(<<0,0,Rest/binary>>,Acc) ->
- {ok,{list_to_binary(lists:reverse(Acc)),Rest}};
-get_indefinite_length_and_value(Binary,Acc) ->
- {ok,{Tag,RestBinary}}=get_tag(Binary),
- {ok,{LenVal,RestBinary2}}=get_length_and_value(RestBinary),
- get_indefinite_length_and_value(RestBinary2,[LenVal,Tag|Acc]).
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% match_tags takes a Tlv (Tag, Length, Value) structure and matches
-%% it with the tags in TagList. If the tags does not match the function
-%% crashes otherwise it returns the remaining Tlv after that the tags have
-%% been removed.
-%%
-%% match_tags(Tlv, TagList)
-%%
-
-match_tags({T,V},[T]) ->
- V;
-match_tags({T,V}, [T|Tt]) ->
- match_tags(V,Tt);
-match_tags([{T,V}],[T|Tt]) ->
- match_tags(V, Tt);
-match_tags(Vlist = [{T,_V}|_], [T]) ->
- Vlist;
-match_tags(Tlv, []) ->
- Tlv;
-match_tags(Tlv = {Tag,_V},[T|_Tt]) ->
- exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}).
-
-
-cindex(Ix,Val,Cname) ->
- case element(Ix,Val) of
- {Cname,Val2} -> Val2;
- X -> X
- end.
-
-%%%
-%% skips components that do not match a tag in Tags
-skip_ExtensionAdditions([],_Tags) ->
- [];
-skip_ExtensionAdditions(TLV=[{Tag,_}|Rest],Tags) ->
- case [X||X=T<-Tags,T==Tag] of
- [] ->
- %% skip this TLV and continue with next
- skip_ExtensionAdditions(Rest,Tags);
- _ ->
- TLV
- 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>>.
-
-
-%%===============================================================================
-%% Decode a tag
-%%
-%% decode_tag(OctetListBuffer) -> {{Form, (Class bsl 16)+ TagNo}, RestOfBuffer, RemovedBytes}
-%%===============================================================================
-
-decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 0:1, Length:7, V:Length/binary, RestBuffer/binary>>) when TagNo < 31 ->
- {Form, (Class bsl 16) + TagNo, V, RestBuffer};
-decode_tag_and_length(<<Class:2, 1:1, TagNo:5, 1:1, 0:7, T/binary>>) when TagNo < 31 ->
- {2, (Class bsl 16) + TagNo, T, <<>>};
-decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 1:1, LL:7, Length:LL/unit:8,V:Length/binary, T/binary>>) when TagNo < 31 ->
- {Form, (Class bsl 16) + TagNo, V, T};
-decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 0:1, Length:7, V:Length/binary, RestBuffer/binary>>) ->
- {Form, (Class bsl 16) + TagNo, V, RestBuffer};
-decode_tag_and_length(<<Class:2, 1:1, 31:5, 0:1, TagNo:7, 1:1, 0:7, T/binary>>) ->
- {2, (Class bsl 16) + TagNo, T, <<>>};
-decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 1:1, LL:7, Length:LL/unit:8, V:Length/binary, T/binary>>) ->
- {Form, (Class bsl 16) + TagNo, V, T};
-decode_tag_and_length(<<Class:2, Form:1, 31:5, 1:1, TagPart1:7, 0:1, TagPartLast, Buffer/binary>>) ->
- TagNo = (TagPart1 bsl 7) bor TagPartLast,
- {Length, RestBuffer} = decode_length(Buffer),
- << V:Length/binary, RestBuffer2/binary>> = RestBuffer,
- {Form, (Class bsl 16) + TagNo, V, RestBuffer2};
-decode_tag_and_length(<<Class:2, Form:1, 31:5, Buffer/binary>>) ->
- {TagNo, Buffer1} = decode_tag(Buffer, 0),
- {Length, RestBuffer} = decode_length(Buffer1),
- << V:Length/binary, RestBuffer2/binary>> = RestBuffer,
- {Form, (Class bsl 16) + TagNo, V, RestBuffer2}.
-
-
-
-%% last partial tag
-decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck) ->
- TagNo = (TagAck bsl 7) bor PartialTag,
- %%<<TagNo>> = <<TagAck:1, PartialTag:7>>,
- {TagNo, Buffer};
-% more tags
-decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck) ->
- TagAck1 = (TagAck bsl 7) bor PartialTag,
- %%<<TagAck1:16>> = <<TagAck:1, PartialTag:7,0:8>>,
- decode_tag(Buffer, TagAck1).
-
-
-%%=======================================================================
-%%
-%% Encode all tags in the list Tags and return a possibly deep list of
-%% bytes with tag and length encoded
-%% The taglist must be in reverse order (fixed by the asn1 compiler)
-%% e.g [T1,T2] will result in
-%% {[EncodedT2,EncodedT1|BytesSoFar],LenSoFar+LenT2+LenT1}
-%%
-
-encode_tags([Tag|Trest], BytesSoFar, LenSoFar) ->
-% remove {Bytes1,L1} = encode_one_tag(Tag),
- {Bytes2,L2} = encode_length(LenSoFar),
- encode_tags(Trest, [Tag,Bytes2|BytesSoFar],
- LenSoFar + size(Tag) + L2);
-encode_tags([], BytesSoFar, LenSoFar) ->
- {BytesSoFar,LenSoFar}.
-
-encode_tags(TagIn, {BytesSoFar,LenSoFar}) ->
- encode_tags(TagIn, BytesSoFar, LenSoFar).
-
-% encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
-% NewForm = case Type of
-% 'EXPLICIT' ->
-% ?CONSTRUCTED;
-% _ ->
-% Form
-% end,
-% Bytes = encode_tag_val({Class,NewForm,No}),
-% {Bytes,size(Bytes)}.
-
-
-%%===============================================================================
-%%
-%% This comment is valid for all the encode/decode functions
-%%
-%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
-%% used for PER-coding but not for BER-coding.
-%%
-%% Val = Value. If Val is an atom then it is a symbolic integer value
-%% (i.e the atom must be one of the names in the NamedNumberList).
-%% The NamedNumberList is used to translate the atom to an integer value
-%% before encoding.
-%%
-%%===============================================================================
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% encode_open_type(Value) -> io_list (i.e nested list with integers, binaries)
-%% Value = list of bytes of an already encoded value (the list must be flat)
-%% | binary
-
-%%
-encode_open_type(Val) when is_list(Val) ->
-% {Val,length(Val)};
- encode_open_type(list_to_binary(Val));
-encode_open_type(Val) ->
- {Val, size(Val)}.
-
-%%
-encode_open_type(Val, T) when is_list(Val) ->
- encode_open_type(list_to_binary(Val),T);
-encode_open_type(Val,[]) ->
- {Val, size(Val)};
-encode_open_type(Val,Tag) ->
- encode_tags(Tag,Val, size(Val)).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_open_type(Tlv, TagIn) -> Value
-%% Tlv = {Tag,V} | V where V -> binary()
-%% TagIn = [TagVal] where TagVal -> int()
-%% Value = binary with decoded data (which must be decoded again as some type)
-%%
-decode_open_type(Tlv, TagIn) ->
- decode_open_type(Tlv, TagIn, erlang).
-decode_open_type(Tlv, TagIn, Method) ->
- case match_tags(Tlv,TagIn) of
- Bin when is_binary(Bin) ->
- {InnerTlv,_} = decode(Bin,Method),
- InnerTlv;
- TlvBytes -> TlvBytes
- end.
-
-
-decode_open_type_as_binary(Tlv, TagIn) ->
- decode_open_type_as_binary(Tlv, TagIn, erlang).
-decode_open_type_as_binary(Tlv,TagIn, Method)->
- case match_tags(Tlv,TagIn) of
- V when is_binary(V) ->
- V;
- [Tlv2] -> encode(Tlv2, Method);
- Tlv2 -> encode(Tlv2, Method)
- end.
-
-%%===============================================================================
-%%===============================================================================
-%%===============================================================================
-%% Boolean, ITU_T X.690 Chapter 8.2
-%%===============================================================================
-%%===============================================================================
-%%===============================================================================
-
-%%===============================================================================
-%% encode_boolean(Integer, ReversedTagList) -> {[Octet],Len}
-%%===============================================================================
-
-encode_boolean({Name, Val}, TagIn) when is_atom(Name) ->
- encode_boolean(Val, TagIn);
-encode_boolean(true, TagIn) ->
- encode_tags(TagIn, [16#FF],1);
-encode_boolean(false, TagIn) ->
- encode_tags(TagIn, [0],1);
-encode_boolean(X,_) ->
- exit({error,{asn1, {encode_boolean, X}}}).
-
-
-%%===============================================================================
-%% decode_boolean(BuffList, HasTag, TotalLen) -> {true, Remain, RemovedBytes} |
-%% {false, Remain, RemovedBytes}
-%%===============================================================================
-decode_boolean(Tlv,TagIn) ->
- Val = match_tags(Tlv, TagIn),
- case Val of
- <<0:8>> ->
- false;
- <<_:8>> ->
- true;
- _ ->
- exit({error,{asn1, {decode_boolean, Val}}})
- end.
-
-
-%%===========================================================================
-%% Integer, ITU_T X.690 Chapter 8.3
-
-%% encode_integer(Constraint, Value, Tag) -> [octet list]
-%% encode_integer(Constraint, Name, NamedNumberList, Tag) -> [octet list]
-%% Value = INTEGER | {Name,INTEGER}
-%% Tag = tag | notag
-%%===========================================================================
-
-encode_integer(C, Val, Tag) when is_integer(Val) ->
- encode_tags(Tag, encode_integer(C, Val));
-encode_integer(C,{Name,Val},Tag) when is_atom(Name) ->
- encode_integer(C,Val,Tag);
-encode_integer(_C, Val, _Tag) ->
- exit({error,{asn1, {encode_integer, Val}}}).
-
-
-
-encode_integer(C, Val, NamedNumberList, Tag) when is_atom(Val) ->
- case lists:keysearch(Val, 1, NamedNumberList) of
- {value,{_, NewVal}} ->
- encode_tags(Tag, encode_integer(C, NewVal));
- _ ->
- exit({error,{asn1, {encode_integer_namednumber, Val}}})
- end;
-encode_integer(C,{_Name,Val},NamedNumberList,Tag) ->
- encode_integer(C,Val,NamedNumberList,Tag);
-encode_integer(C, Val, _NamedNumberList, Tag) ->
- encode_tags(Tag, encode_integer(C, Val)).
-
-
-encode_integer(_, Val) ->
- Bytes =
- if
- Val >= 0 ->
- encode_integer_pos(Val, []);
- true ->
- encode_integer_neg(Val, [])
- end,
- {Bytes,length(Bytes)}.
-
-encode_integer_pos(0, L=[B|_Acc]) when B < 128 ->
- L;
-encode_integer_pos(N, Acc) ->
- encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
-
-encode_integer_neg(-1, L=[B1|_T]) when B1 > 127 ->
- L;
-encode_integer_neg(N, Acc) ->
- encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
-
-%%===============================================================================
-%% decode integer
-%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
-%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
-%%===============================================================================
-
-decode_integer(Tlv,Range,NamedNumberList,TagIn) ->
- V = match_tags(Tlv,TagIn),
- Int = decode_integer(V),
- range_check_integer(Int,Range),
- number2name(Int,NamedNumberList).
-
-decode_integer(Tlv,Range,TagIn) ->
- V = match_tags(Tlv, TagIn),
- Int = decode_integer(V),
- range_check_integer(Int,Range),
- Int.
-
-%% decoding postitive integer values.
-decode_integer(Bin = <<0:1,_:7,_/binary>>) ->
- Len = size(Bin),
-% <<Int:Len/unit:8,Buffer2/binary>> = Bin,
- <<Int:Len/unit:8>> = Bin,
- Int;
-%% decoding negative integer values.
-decode_integer(Bin = <<1:1,B2:7,Bs/binary>>) ->
- Len = size(Bin),
-% <<N:Len/unit:8,Buffer2/binary>> = <<B2,Bs/binary>>,
- <<N:Len/unit:8>> = <<B2,Bs/binary>>,
- Int = N - (1 bsl (8 * Len - 1)),
- Int.
-
-range_check_integer(Int,Range) ->
- case Range of
- [] -> % No length constraint
- Int;
- {Lb,Ub} when Int >= Lb, Ub >= Int -> % variable length constraint
- Int;
- Int -> % fixed value constraint
- Int;
- {_,_} ->
- exit({error,{asn1,{integer_range,Range,Int}}});
- SingleValue when is_integer(SingleValue) ->
- exit({error,{asn1,{integer_range,Range,Int}}});
- _ -> % some strange constraint that we don't support yet
- Int
- end.
-
-number2name(Int,[]) ->
- Int;
-number2name(Int,NamedNumberList) ->
- case lists:keysearch(Int, 2, NamedNumberList) of
- {value,{NamedVal, _}} ->
- NamedVal;
- _ ->
- Int
- end.
-
-
-%%============================================================================
-%% Enumerated value, ITU_T X.690 Chapter 8.4
-
-%% encode enumerated value
-%%============================================================================
-encode_enumerated(Val, TagIn) when is_integer(Val)->
- encode_tags(TagIn, encode_integer(false,Val));
-encode_enumerated({Name,Val}, TagIn) when is_atom(Name) ->
- encode_enumerated(Val, TagIn).
-
-%% The encode_enumerated functions below this line can be removed when the
-%% new code generation is stable. (the functions might have to be kept here
-%% a while longer for compatibility reasons)
-
-encode_enumerated(C, Val, {NamedNumberList,ExtList}, TagIn) when is_atom(Val) ->
- case catch encode_enumerated(C, Val, NamedNumberList, TagIn) of
- {'EXIT',_} -> encode_enumerated(C, Val, ExtList, TagIn);
- Result -> Result
- end;
-
-encode_enumerated(C, Val, NamedNumberList, TagIn) when is_atom(Val) ->
- case lists:keysearch(Val, 1, NamedNumberList) of
- {value, {_, NewVal}} ->
- encode_tags(TagIn, encode_integer(C, NewVal));
- _ ->
- exit({error,{asn1, {enumerated_not_in_range, Val}}})
- end;
-
-encode_enumerated(C, {asn1_enum, Val}, {_,_}, TagIn) when is_integer(Val) ->
- encode_tags(TagIn, encode_integer(C,Val));
-
-encode_enumerated(C, {Name,Val}, NamedNumberList, TagIn) when is_atom(Name) ->
- encode_enumerated(C, Val, NamedNumberList, TagIn);
-
-encode_enumerated(_C, Val, _NamedNumberList, _TagIn) ->
- exit({error,{asn1, {enumerated_not_namednumber, Val}}}).
-
-
-
-%%============================================================================
-%% decode enumerated value
-%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> Value
-%%===========================================================================
-decode_enumerated(Tlv, Range, NamedNumberList, Tags) ->
- Buffer = match_tags(Tlv,Tags),
- decode_enumerated_notag(Buffer, Range, NamedNumberList, Tags).
-
-decode_enumerated_notag(Buffer, _Range, {NamedNumberList,ExtList}, _Tags) ->
-
- IVal = decode_integer2(size(Buffer), Buffer),
- case decode_enumerated1(IVal, NamedNumberList) of
- {asn1_enum,IVal} ->
- decode_enumerated1(IVal,ExtList);
- EVal ->
- EVal
- end;
-decode_enumerated_notag(Buffer, _Range, NNList, _Tags) ->
- IVal = decode_integer2(size(Buffer), Buffer),
- case decode_enumerated1(IVal, NNList) of
- {asn1_enum,_} ->
- exit({error,{asn1, {illegal_enumerated, IVal}}});
- EVal ->
- EVal
- end.
-
-decode_enumerated1(Val, NamedNumberList) ->
- %% it must be a named integer
- case lists:keysearch(Val, 2, NamedNumberList) of
- {value,{NamedVal, _}} ->
- NamedVal;
- _ ->
- {asn1_enum,Val}
- end.
-
-
-%%============================================================================
-%%
-%% Real value, ITU_T X.690 Chapter 8.5
-%%============================================================================
-%%
-%% encode real value
-%%============================================================================
-
-%% only base 2 internally so far!!
-encode_real(_C,0, TagIn) ->
- encode_tags(TagIn, {[],0});
-encode_real(_C,'PLUS-INFINITY', TagIn) ->
- encode_tags(TagIn, {[64],1});
-encode_real(_C,'MINUS-INFINITY', TagIn) ->
- encode_tags(TagIn, {[65],1});
-encode_real(C,Val, TagIn) when is_tuple(Val); is_list(Val) ->
- encode_tags(TagIn, encode_real(C,Val)).
-
-
-
-encode_real(C,Val) ->
- asn1rt_ber_bin:encode_real(C,Val).
-
-
-%%============================================================================
-%% decode real value
-%%
-%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
-%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
-%% RestBuff}
-%%
-%% only for base 2 and 10 decoding sofar!!
-%%============================================================================
-
-decode_real(Tlv, Tags) ->
- Buffer = match_tags(Tlv,Tags),
- decode_real_notag(Buffer).
-
-decode_real_notag(Buffer) ->
- Len =
- case Buffer of
- Bin when is_binary(Bin) ->
- size(Bin);
- {_T,_V} ->
- exit({error,{asn1,{real_not_in_primitive_form,Buffer}}})
- end,
- {Val,_Rest,Len} = asn1rt_ber_bin:decode_real(Buffer,Len),
- Val.
-%% exit({error,{asn1, {unimplemented,real}}}).
-%% decode_real2(Buffer, Form, size(Buffer)).
-
-% decode_real2(Buffer, Form, Len) ->
-% <<First, Buffer2/binary>> = Buffer,
-% if
-% First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
-% First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
-% First =:= 2#00000000 -> {0, Buffer2};
-% true ->
-% %% have some check here to verify only supported bases (2)
-% <<B7:1,B6:1,B5_4:2,B3_2:2,B1_0:2>> = <<First>>,
-% Sign = B6,
-% Base =
-% case B5_4 of
-% 0 -> 2; % base 2, only one so far
-% _ -> exit({error,{asn1, {non_supported_base, First}}})
-% end,
-% ScalingFactor =
-% case B3_2 of
-% 0 -> 0; % no scaling so far
-% _ -> exit({error,{asn1, {non_supported_scaling, First}}})
-% end,
-
-% {FirstLen,Exp,Buffer3} =
-% case B1_0 of
-% 0 ->
-% <<_:1/unit:8,Buffer21/binary>> = Buffer2,
-% {2, decode_integer2(1, Buffer2),Buffer21};
-% 1 ->
-% <<_:2/unit:8,Buffer21/binary>> = Buffer2,
-% {3, decode_integer2(2, Buffer2)};
-% 2 ->
-% <<_:3/unit:8,Buffer21/binary>> = Buffer2,
-% {4, decode_integer2(3, Buffer2)};
-% 3 ->
-% <<ExpLen1,RestBuffer/binary>> = Buffer2,
-% <<_:ExpLen1/unit:8,RestBuffer2/binary>> = RestBuffer,
-% { ExpLen1 + 2,
-% decode_integer2(ExpLen1, RestBuffer, RemBytes1),
-% RestBuffer2}
-% end,
-% Length = Len - FirstLen,
-% <<LongInt:Length/unit:8,RestBuff/binary>> = Buffer3,
-% {Mantissa, Buffer4} =
-% if Sign =:= 0 ->
-
-% {LongInt, RestBuff};% sign plus,
-% true ->
-
-% {-LongInt, RestBuff}% sign minus
-% end,
-% case Form of
-% tuple ->
-% {Val,Buf,RemB} = Exp,
-% {{Mantissa, Base, {Val,Buf}}, Buffer4, RemBytes2+RemBytes3};
-% _value ->
-% comming
-% end
-% end.
-
-
-%%============================================================================
-%% Bitstring value, ITU_T X.690 Chapter 8.6
-%%
-%% encode bitstring value
-%%
-%% bitstring NamedBitList
-%% Val can be of:
-%% - [identifiers] where only named identifers are set to one,
-%% the Constraint must then have some information of the
-%% bitlength.
-%% - [list of ones and zeroes] all bits
-%% - integer value representing the bitlist
-%% C is constrint Len, only valid when identifiers
-%%============================================================================
-
-encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,TagIn) when is_integer(Unused), is_binary(BinBits) ->
- encode_bin_bit_string(C,Bin,NamedBitList,TagIn);
-encode_bit_string(C, [FirstVal | RestVal], NamedBitList, TagIn) when is_atom(FirstVal) ->
- encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn);
-
-encode_bit_string(C, [{bit,X} | RestVal], NamedBitList, TagIn) ->
- encode_bit_string_named(C, [{bit,X} | RestVal], NamedBitList, TagIn);
-
-encode_bit_string(C, [FirstVal| RestVal], NamedBitList, TagIn) when is_integer(FirstVal) ->
- encode_bit_string_bits(C, [FirstVal | RestVal], NamedBitList, TagIn);
-
-encode_bit_string(_C, 0, _NamedBitList, TagIn) ->
- encode_tags(TagIn, <<0>>,1);
-
-encode_bit_string(_C, [], _NamedBitList, TagIn) ->
- encode_tags(TagIn, <<0>>,1);
-
-encode_bit_string(C, IntegerVal, NamedBitList, TagIn) when is_integer(IntegerVal) ->
- BitListVal = int_to_bitlist(IntegerVal),
- encode_bit_string_bits(C, BitListVal, NamedBitList, TagIn);
-
-encode_bit_string(C, {Name,BitList}, NamedBitList, TagIn) when is_atom(Name) ->
- encode_bit_string(C, BitList, NamedBitList, TagIn).
-
-
-
-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,TagIn)->
- case get_constraint(C,'SizeConstraint') of
- no ->
- remove_unused_then_dotag(TagIn, Unused, BinBits);
- {_Min,Max} ->
- BBLen = (size(BinBits)*8)-Unused,
- if
- BBLen > Max ->
- exit({error,{asn1,
- {bitstring_length,
- {{was,BBLen},{maximum,Max}}}}});
- true ->
- remove_unused_then_dotag(TagIn, Unused, BinBits)
- end;
- Size ->
- case ((size(BinBits)*8)-Unused) of
- BBSize when BBSize =< Size ->
- remove_unused_then_dotag(TagIn, Unused, BinBits);
- BBSize ->
- exit({error,{asn1,
- {bitstring_length,
- {{was,BBSize},{should_be,Size}}}}})
- end
- end.
-
-remove_unused_then_dotag(TagIn,Unused,BinBits) ->
- case Unused of
- 0 when (size(BinBits) == 0) ->
- encode_tags(TagIn,<<0>>,1);
- 0 ->
- Bin = <<Unused,BinBits/binary>>,
- encode_tags(TagIn,Bin,size(Bin));
- Num ->
- N = (size(BinBits)-1),
- <<BBits:N/binary,LastByte>> = BinBits,
- encode_tags(TagIn,
- [Unused,binary_to_list(BBits) ++[(LastByte bsr Num) bsl Num]],
- 1+size(BinBits))
- end.
-
-
-%%=================================================================
-%% Encode named bits
-%%=================================================================
-
-encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn) ->
- ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
- Size =
- case get_constraint(C,'SizeConstraint') of
- no ->
- lists:max(ToSetPos)+1;
- {_Min,Max} ->
- Max;
- TSize ->
- TSize
- end,
- BitList = make_and_set_list(Size, ToSetPos, 0),
- {Len, Unused, OctetList} = encode_bitstring(BitList),
- encode_tags(TagIn, [Unused|OctetList],Len+1).
-
-
-%%----------------------------------------
-%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
-%% [sorted_list_of_bitpositions_to_set]
-%%----------------------------------------
-
-get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
- get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
-get_all_bitposes([Val | Rest], NamedBitList, Ack) when is_atom(Val) ->
- case lists:keysearch(Val, 1, NamedBitList) of
- {value, {_ValName, ValPos}} ->
- get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
- _ ->
- exit({error,{asn1, {bitstring_namedbit, Val}}})
- end;
-get_all_bitposes([], _NamedBitList, Ack) ->
- lists:sort(Ack).
-
-
-%%----------------------------------------
-%% make_and_set_list(Len of list to return, [list of positions to set to 1])->
-%% returns list of Len length, with all in SetPos set.
-%% in positioning in list the first element is 0, the second 1 etc.., but
-%% Len will make a list of length Len, not Len + 1.
-%% BitList = make_and_set_list(C, ToSetPos, 0),
-%%----------------------------------------
-
-make_and_set_list(0, [], _) -> [];
-make_and_set_list(0, _, _) ->
- exit({error,{asn1,bitstring_sizeconstraint}});
-make_and_set_list(Len, [XPos|SetPos], XPos) ->
- [1 | make_and_set_list(Len - 1, SetPos, XPos + 1)];
-make_and_set_list(Len, [Pos|SetPos], XPos) ->
- [0 | make_and_set_list(Len - 1, [Pos | SetPos], XPos + 1)];
-make_and_set_list(Len, [], XPos) ->
- [0 | make_and_set_list(Len - 1, [], XPos + 1)].
-
-
-
-
-
-
-%%=================================================================
-%% Encode bit string for lists of ones and zeroes
-%%=================================================================
-encode_bit_string_bits(C, BitListVal, _NamedBitList, TagIn) when is_list(BitListVal) ->
- case get_constraint(C,'SizeConstraint') of
- no ->
- {Len, Unused, OctetList} = encode_bitstring(BitListVal),
- %%add unused byte to the Len
- encode_tags(TagIn, [Unused | OctetList], Len+1);
- Constr={Min,_Max} when is_integer(Min) ->
- %% Max may be an integer or 'MAX'
- encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
- {Constr={_,_},[]} ->%Constr={Min,Max}
- %% constraint with extension mark
- encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
- Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}}
- %% constraint with extension mark
- encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
- Size ->
- case length(BitListVal) of
- BitSize when BitSize == Size ->
- {Len, Unused, OctetList} = encode_bitstring(BitListVal),
- %%add unused byte to the Len
- encode_tags(TagIn, [Unused | OctetList], Len+1);
- BitSize when BitSize < Size ->
- PaddedList = pad_bit_list(Size-BitSize,BitListVal),
- {Len, Unused, OctetList} = encode_bitstring(PaddedList),
- %%add unused byte to the Len
- encode_tags(TagIn, [Unused | OctetList], Len+1);
- BitSize ->
- exit({error,{asn1,
- {bitstring_length, {{was,BitSize},{should_be,Size}}}}})
- end
-
- end.
-
-encode_constr_bit_str_bits({{_Min1,Max1},{Min2,Max2}},BitListVal,TagIn) ->
- BitLen = length(BitListVal),
- case BitLen of
- Len when Len > Max2 ->
- exit({error,{asn1,{bitstring_length,{{was,BitLen},
- {maximum,Max2}}}}});
- Len when Len > Max1, Len < Min2 ->
- exit({error,{asn1,{bitstring_length,{{was,BitLen},
- {not_allowed_interval,
- Max1,Min2}}}}});
- _ ->
- {Len, Unused, OctetList} = encode_bitstring(BitListVal),
- %%add unused byte to the Len
- encode_tags(TagIn, [Unused, OctetList], Len+1)
- end;
-encode_constr_bit_str_bits({Min,Max},BitListVal,TagIn) ->
- 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,Max}}}}});
- true ->
- {Len, Unused, OctetList} = encode_bitstring(BitListVal),
- %%add unused byte to the Len
- encode_tags(TagIn, [Unused, OctetList], Len+1)
- end.
-
-
-%% returns a list of length Size + length(BitListVal), with BitListVal
-%% as the most significant elements followed by padded zero elements
-pad_bit_list(Size,BitListVal) ->
- Tail = lists:duplicate(Size,0),
- lists:append(BitListVal,Tail).
-
-%%=================================================================
-%% Do the actual encoding
-%% ([bitlist]) -> {ListLen, UnusedBits, OctetList}
-%%=================================================================
-
-encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest]) ->
- Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
- (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
- encode_bitstring(Rest, [Val], 1);
-encode_bitstring(Val) ->
- {Unused, Octet} = unused_bitlist(Val, 7, 0),
- {1, Unused, [Octet]}.
-
-encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest], Ack, Len) ->
- Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
- (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
- encode_bitstring(Rest, [Ack | [Val]], Len + 1);
-%%even multiple of 8 bits..
-encode_bitstring([], Ack, Len) ->
- {Len, 0, Ack};
-%% unused bits in last octet
-encode_bitstring(Rest, Ack, Len) ->
-% io:format("uneven ~w ~w ~w~n",[Rest, Ack, Len]),
- {Unused, Val} = unused_bitlist(Rest, 7, 0),
- {Len + 1, Unused, [Ack | [Val]]}.
-
-%%%%%%%%%%%%%%%%%%
-%% unused_bitlist([list of ones and zeros <= 7], 7, []) ->
-%% {Unused bits, Last octet with bits moved to right}
-unused_bitlist([], Trail, Ack) ->
- {Trail + 1, Ack};
-unused_bitlist([Bit | Rest], Trail, Ack) ->
-%% io:format("trail Bit: ~w Rest: ~w Trail: ~w Ack:~w~n",[Bit, Rest, Trail, Ack]),
- unused_bitlist(Rest, Trail - 1, (Bit bsl Trail) bor Ack).
-
-
-%%============================================================================
-%% decode bitstring value
-%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
-%%============================================================================
-
-decode_compact_bit_string(Buffer, Range, NamedNumberList, Tags) ->
-% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
- decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags,
- NamedNumberList,bin).
-
-decode_bit_string(Buffer, Range, NamedNumberList, Tags) ->
-% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
- decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags,
- NamedNumberList,old).
-
-
-decode_bit_string2(<<0>>,_NamedNumberList,BinOrOld) ->
- case BinOrOld of
- bin ->
- {0,<<>>};
- _ ->
- []
- end;
-decode_bit_string2(<<Unused,Bits/binary>>,NamedNumberList,BinOrOld) ->
- case NamedNumberList of
- [] ->
- case BinOrOld of
- bin ->
- {Unused,Bits};
- _ ->
- decode_bitstring2(size(Bits), Unused, Bits)
- end;
- _ ->
- BitString = decode_bitstring2(size(Bits), Unused, Bits),
- decode_bitstring_NNL(BitString,NamedNumberList)
- end.
-
-%%----------------------------------------
-%% Decode the in buffer to bits
-%%----------------------------------------
-decode_bitstring2(1,Unused,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,_/binary>>) ->
- lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0],8-Unused);
-decode_bitstring2(Len, Unused,
- <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Buffer/binary>>) ->
- [B7, B6, B5, B4, B3, B2, B1, B0 |
- decode_bitstring2(Len - 1, Unused, Buffer)].
-
-%%decode_bitstring2(1, Unused, Buffer) ->
-%% make_bits_of_int(hd(Buffer), 128, 8-Unused);
-%%decode_bitstring2(Len, Unused, [BitVal | Buffer]) ->
-%% [B7, B6, B5, B4, B3, B2, B1, B0] = make_bits_of_int(BitVal, 128, 8),
-%% [B7, B6, B5, B4, B3, B2, B1, B0 |
-%% decode_bitstring2(Len - 1, Unused, Buffer)].
-
-
-%%make_bits_of_int(_, _, 0) ->
-%% [];
-%%make_bits_of_int(BitVal, MaskVal, Unused) when Unused > 0 ->
-%% X = case MaskVal band BitVal of
-%% 0 -> 0 ;
-%% _ -> 1
-%% end,
-%% [X | make_bits_of_int(BitVal, MaskVal bsr 1, Unused - 1)].
-
-
-
-%%----------------------------------------
-%% Decode the bitlist to names
-%%----------------------------------------
-
-
-decode_bitstring_NNL(BitList,NamedNumberList) ->
- decode_bitstring_NNL(BitList,NamedNumberList,0,[]).
-
-
-decode_bitstring_NNL([],_,_No,Result) ->
- lists:reverse(Result);
-
-decode_bitstring_NNL([B|BitList],[{Name,No}|NamedNumberList],No,Result) ->
- if
- B == 0 ->
- decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result);
- true ->
- decode_bitstring_NNL(BitList,NamedNumberList,No+1,[Name|Result])
- end;
-decode_bitstring_NNL([1|BitList],NamedNumberList,No,Result) ->
- decode_bitstring_NNL(BitList,NamedNumberList,No+1,[{bit,No}|Result]);
-decode_bitstring_NNL([0|BitList],NamedNumberList,No,Result) ->
- decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result).
-
-
-%%============================================================================
-%% Octet string, ITU_T X.690 Chapter 8.7
-%%
-%% encode octet string
-%% The OctetList must be a flat list of integers in the range 0..255
-%% the function does not check this because it takes to much time
-%%============================================================================
-encode_octet_string(_C, OctetList, TagIn) when is_binary(OctetList) ->
- encode_tags(TagIn, OctetList, size(OctetList));
-encode_octet_string(_C, OctetList, TagIn) when is_list(OctetList) ->
- encode_tags(TagIn, OctetList, length(OctetList));
-encode_octet_string(C, {Name,OctetList}, TagIn) when is_atom(Name) ->
- encode_octet_string(C, OctetList, TagIn).
-
-
-%%============================================================================
-%% decode octet string
-%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
-%%
-%% Octet string is decoded as a restricted string
-%%============================================================================
-decode_octet_string(Buffer, Range, Tags) ->
-% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}),
- decode_restricted_string(Buffer, Range, ?N_OCTET_STRING,
- Tags, [], old).
-
-%%============================================================================
-%% Null value, ITU_T X.690 Chapter 8.8
-%%
-%% encode NULL value
-%%============================================================================
-
-encode_null({Name, _Val}, TagIn) when is_atom(Name) ->
- encode_tags(TagIn, [], 0);
-encode_null(_Val, TagIn) ->
- encode_tags(TagIn, [], 0).
-
-%%============================================================================
-%% decode NULL value
-%% (Buffer, HasTag, TotalLen) -> {NULL, Remain, RemovedBytes}
-%%============================================================================
-
-decode_null(Tlv, Tags) ->
- Val = match_tags(Tlv, Tags),
- case Val of
- <<>> ->
- 'NULL';
- _ ->
- exit({error,{asn1,{decode_null,Val}}})
- end.
-
-%%============================================================================
-%% Object identifier, ITU_T X.690 Chapter 8.19
-%%
-%% encode Object Identifier value
-%%============================================================================
-
-encode_object_identifier({Name,Val}, TagIn) when is_atom(Name) ->
- encode_object_identifier(Val, TagIn);
-encode_object_identifier(Val, TagIn) ->
- encode_tags(TagIn, e_object_identifier(Val)).
-
-e_object_identifier({'OBJECT IDENTIFIER', V}) ->
- e_object_identifier(V);
-e_object_identifier({Cname, V}) when 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} = lists:mapfoldl(fun enc_obj_id_tail/2,0,Tail),
- {[H|R], Lh+Lr}.
-
-enc_obj_id_tail(H, Len) ->
- {B, L} = mk_object_val(H),
- {B,Len+L}.
-
-
-%%%%%%%%%%%
-%% mk_object_val(Value) -> {OctetList, Len}
-%% returns a Val as a list of octets, the 8 bit is allways set to one except
-%% for the last octet, where its 0
-%%
-
-
-mk_object_val(Val) when Val =< 127 ->
- {[255 band Val], 1};
-mk_object_val(Val) ->
- mk_object_val(Val bsr 7, [Val band 127], 1).
-mk_object_val(0, Ack, Len) ->
- {Ack, Len};
-mk_object_val(Val, Ack, Len) ->
- mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
-
-
-
-%%============================================================================
-%% decode Object Identifier value
-%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
-%%============================================================================
-
-decode_object_identifier(Tlv, Tags) ->
- Val = match_tags(Tlv, Tags),
- [AddedObjVal|ObjVals] = dec_subidentifiers(Val,0,[]),
- {Val1, Val2} = if
- AddedObjVal < 40 ->
- {0, AddedObjVal};
- AddedObjVal < 80 ->
- {1, AddedObjVal - 40};
- true ->
- {2, AddedObjVal - 80}
- end,
- list_to_tuple([Val1, Val2 | ObjVals]).
-
-dec_subidentifiers(<<>>,_Av,Al) ->
- lists:reverse(Al);
-dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al) ->
- dec_subidentifiers(T,(Av bsl 7) + H,Al);
-dec_subidentifiers(<<H,T/binary>>,Av,Al) ->
- dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al]).
-
-%%============================================================================
-%% 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,TagIn) ->
- encode_tags(TagIn, enc_relative_oid(Val)).
-
-enc_relative_oid(Tuple) when is_tuple(Tuple) ->
- enc_relative_oid(tuple_to_list(Tuple));
-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(Tlv, Tags) ->
- Val = match_tags(Tlv, Tags),
- ObjVals = dec_subidentifiers(Val,0,[]),
- list_to_tuple(ObjVals).
-
-%%============================================================================
-%% Restricted character string types, ITU_T X.690 Chapter 8.20
-%%
-%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
-%%============================================================================
-%% The StringType arg is kept for future use but might be removed
-encode_restricted_string(_C, OctetList, _StringType, TagIn)
- when is_binary(OctetList) ->
- encode_tags(TagIn, OctetList, size(OctetList));
-encode_restricted_string(_C, OctetList, _StringType, TagIn)
- when is_list(OctetList) ->
- encode_tags(TagIn, OctetList, length(OctetList));
-encode_restricted_string(C,{Name,OctetL}, StringType, TagIn) when is_atom(Name)->
- encode_restricted_string(C, OctetL, StringType, TagIn).
-
-%%============================================================================
-%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
-%% (Buffer, Range, StringType, HasTag, TotalLen) ->
-%% {String, Remain, RemovedBytes}
-%%============================================================================
-
-decode_restricted_string(Buffer, Range, StringType, Tags) ->
- decode_restricted_string(Buffer, Range, StringType, Tags, [], old).
-
-
-decode_restricted_string(Tlv, Range, StringType, TagsIn,
- NamedNumberList, BinOrOld) ->
- Val = match_tags(Tlv, TagsIn),
- Val2 =
- case Val of
- PartList = [_H|_T] -> % constructed val
- Bin = collect_parts(PartList),
- decode_restricted(Bin, StringType,
- NamedNumberList, BinOrOld);
- Bin ->
- decode_restricted(Bin, StringType,
- NamedNumberList, BinOrOld)
- end,
- check_and_convert_restricted_string(Val2,StringType,Range,NamedNumberList,BinOrOld).
-
-
-
-% case StringType of
-% ?N_BIT_STRING when BinOrOld == bin ->
-% {concat_bit_binaries(AccVal, Val), AccRb+Rb};
-% _ when 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,
-
-
-
-decode_restricted(Bin, StringType, NamedNumberList,BinOrOld) ->
- case StringType of
- ?N_BIT_STRING ->
- decode_bit_string2(Bin, NamedNumberList, BinOrOld);
- ?N_UniversalString ->
- mk_universal_string(binary_to_list(Bin));
- ?N_BMPString ->
- mk_BMP_string(binary_to_list(Bin));
- _ ->
- Bin
- end.
-
-
-check_and_convert_restricted_string(Val,StringType,Range,NamedNumberList,_BinOrOld) ->
- {StrLen,NewVal} = case StringType of
- ?N_BIT_STRING when NamedNumberList /= [] ->
- {no_check,Val};
- ?N_BIT_STRING when 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) ->
- {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=[Min|_]} when StrLen >= Lb; StrLen >= Min ->
- 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.
-
-
-%%============================================================================
-%% encode Universal string
-%%============================================================================
-
-encode_universal_string(C, {Name, Universal}, TagIn) when is_atom(Name) ->
- encode_universal_string(C, Universal, TagIn);
-encode_universal_string(_C, Universal, TagIn) ->
- OctetList = mk_uni_list(Universal),
- encode_tags(TagIn, OctetList, length(OctetList)).
-
-mk_uni_list(In) ->
- mk_uni_list(In,[]).
-
-mk_uni_list([],List) ->
- lists:reverse(List);
-mk_uni_list([{A,B,C,D}|T],List) ->
- mk_uni_list(T,[D,C,B,A|List]);
-mk_uni_list([H|T],List) ->
- mk_uni_list(T,[H,0,0,0|List]).
-
-%%===========================================================================
-%% decode Universal strings
-%% (Buffer, Range, StringType, HasTag, LenIn) ->
-%% {String, Remain, RemovedBytes}
-%%===========================================================================
-
-decode_universal_string(Buffer, Range, Tags) ->
- decode_restricted_string(Buffer, Range, ?N_UniversalString,
- Tags, [], old).
-
-
-mk_universal_string(In) ->
- mk_universal_string(In,[]).
-
-mk_universal_string([],Acc) ->
- lists:reverse(Acc);
-mk_universal_string([0,0,0,D|T],Acc) ->
- mk_universal_string(T,[D|Acc]);
-mk_universal_string([A,B,C,D|T],Acc) ->
- mk_universal_string(T,[{A,B,C,D}|Acc]).
-
-
-%%============================================================================
-%% encode UTF8 string
-%%============================================================================
-
-encode_UTF8_string(_C,UTF8String,TagIn) when is_binary(UTF8String) ->
- encode_tags(TagIn, UTF8String, size(UTF8String));
-encode_UTF8_string(_C,UTF8String,TagIn) ->
- encode_tags(TagIn, UTF8String, length(UTF8String)).
-
-
-%%============================================================================
-%% decode UTF8 string
-%%============================================================================
-
-decode_UTF8_string(Tlv,TagsIn) ->
- Val = match_tags(Tlv, TagsIn),
- case Val of
- PartList = [_H|_T] -> % constructed val
- collect_parts(PartList);
- Bin ->
- Bin
- end.
-
-
-%%============================================================================
-%% encode BMP string
-%%============================================================================
-
-encode_BMP_string(C, {Name,BMPString}, TagIn) when is_atom(Name)->
- encode_BMP_string(C, BMPString, TagIn);
-encode_BMP_string(_C, BMPString, TagIn) ->
- OctetList = mk_BMP_list(BMPString),
- encode_tags(TagIn, OctetList, length(OctetList)).
-
-mk_BMP_list(In) ->
- mk_BMP_list(In,[]).
-
-mk_BMP_list([],List) ->
- lists:reverse(List);
-mk_BMP_list([{0,0,C,D}|T],List) ->
- mk_BMP_list(T,[D,C|List]);
-mk_BMP_list([H|T],List) ->
- mk_BMP_list(T,[H,0|List]).
-
-%%============================================================================
-%% decode (OctetList, Range(ignored), tag|notag) -> {ValList, RestList}
-%% (Buffer, Range, StringType, HasTag, TotalLen) ->
-%% {String, Remain, RemovedBytes}
-%%============================================================================
-decode_BMP_string(Buffer, Range, Tags) ->
- decode_restricted_string(Buffer, Range, ?N_BMPString,
- Tags, [], old).
-
-mk_BMP_string(In) ->
- mk_BMP_string(In,[]).
-
-mk_BMP_string([],US) ->
- lists:reverse(US);
-mk_BMP_string([0,B|T],US) ->
- mk_BMP_string(T,[B|US]);
-mk_BMP_string([C,D|T],US) ->
- mk_BMP_string(T,[{0,0,C,D}|US]).
-
-
-%%============================================================================
-%% Generalized time, ITU_T X.680 Chapter 39
-%%
-%% encode Generalized time
-%%============================================================================
-
-encode_generalized_time(C, {Name,OctetList}, TagIn) when is_atom(Name) ->
- encode_generalized_time(C, OctetList, TagIn);
-encode_generalized_time(_C, OctetList, TagIn) ->
- encode_tags(TagIn, OctetList, length(OctetList)).
-
-%%============================================================================
-%% decode Generalized time
-%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
-%%============================================================================
-
-decode_generalized_time(Tlv, _Range, Tags) ->
- Val = match_tags(Tlv, Tags),
- NewVal = case Val of
- PartList = [_H|_T] -> % constructed
- collect_parts(PartList);
- Bin ->
- Bin
- end,
- binary_to_list(NewVal).
-
-%%============================================================================
-%% Universal time, ITU_T X.680 Chapter 40
-%%
-%% encode UTC time
-%%============================================================================
-
-encode_utc_time(C, {Name,OctetList}, TagIn) when is_atom(Name) ->
- encode_utc_time(C, OctetList, TagIn);
-encode_utc_time(_C, OctetList, TagIn) ->
- encode_tags(TagIn, OctetList, length(OctetList)).
-
-%%============================================================================
-%% decode UTC time
-%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
-%%============================================================================
-
-decode_utc_time(Tlv, _Range, Tags) ->
- Val = match_tags(Tlv, Tags),
- NewVal = case Val of
- PartList = [_H|_T] -> % constructed
- collect_parts(PartList);
- Bin ->
- Bin
- end,
- binary_to_list(NewVal).
-
-
-%%============================================================================
-%% Length handling
-%%
-%% Encode length
-%%
-%% encode_length(Int | indefinite) ->
-%% [<127]| [128 + Int (<127),OctetList] | [16#80]
-%%============================================================================
-
-encode_length(indefinite) ->
- {[16#80],1}; % 128
-encode_length(L) when L =< 16#7F ->
- {[L],1};
-encode_length(L) ->
- Oct = minimum_octets(L),
- Len = length(Oct),
- if
- Len =< 126 ->
- {[ (16#80+Len) | Oct ],Len+1};
- true ->
- exit({error,{asn1, to_long_length_oct, Len}})
- end.
-
-
-%% Val must be >= 0
-minimum_octets(Val) ->
- minimum_octets(Val,[]).
-
-minimum_octets(0,Acc) ->
- Acc;
-minimum_octets(Val, Acc) ->
- minimum_octets((Val bsr 8),[Val band 16#FF | Acc]).
-
-
-%%===========================================================================
-%% Decode length
-%%
-%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
-%% {{Length, RestOctetsL}, NoRemovedBytes}
-%%===========================================================================
-
-decode_length(<<1:1,0:7,T/binary>>) ->
- {indefinite, T};
-decode_length(<<0:1,Length:7,T/binary>>) ->
- {Length,T};
-decode_length(<<1:1,LL:7,T/binary>>) ->
- <<Length:LL/unit:8,Rest/binary>> = T,
- {Length,Rest}.
-
-
-
-%%-------------------------------------------------------------------------
-%% INTERNAL HELPER FUNCTIONS (not exported)
-%%-------------------------------------------------------------------------
-
-
-%% decoding postitive integer values.
-decode_integer2(Len,Bin = <<0:1,_:7,_Bs/binary>>) ->
- <<Int:Len/unit:8>> = Bin,
- Int;
-%% decoding negative integer values.
-decode_integer2(Len,<<1:1,B2:7,Bs/binary>>) ->
- <<N:Len/unit:8>> = <<B2,Bs/binary>>,
- Int = N - (1 bsl (8 * Len - 1)),
- Int.
-
-get_constraint(C,Key) ->
- case lists:keysearch(Key,1,C) of
- false ->
- no;
- {value,{_,V}} ->
- V
- end.
-
-collect_parts(TlvList) ->
- collect_parts(TlvList,[]).
-
-collect_parts([{_,L}|Rest],Acc) when is_list(L) ->
- collect_parts(Rest,[collect_parts(L)|Acc]);
-collect_parts([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],_Acc) ->
- collect_parts_bit(Rest,[Bits],Unused);
-collect_parts([{_T,V}|Rest],Acc) ->
- collect_parts(Rest,[V|Acc]);
-collect_parts([],Acc) ->
- list_to_binary(lists:reverse(Acc)).
-
-collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
- collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
-collect_parts_bit([],Acc,Uacc) ->
- list_to_binary([Uacc|lists:reverse(Acc)]).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/asn1/src/asn1rt_check.erl b/lib/asn1/src/asn1rt_check.erl
deleted file mode 100644
index 35b993fc71..0000000000
--- a/lib/asn1/src/asn1rt_check.erl
+++ /dev/null
@@ -1,360 +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_check).
-
--export([check_bool/2,
- check_int/3,
- check_bitstring/3,
- check_octetstring/2,
- check_null/2,
- check_objectidentifier/2,
- check_objectdescriptor/2,
- check_real/2,
- check_enum/3,
- check_restrictedstring/2]).
-
--export([transform_to_EXTERNAL1990/1,
- transform_to_EXTERNAL1994/1]).
-
--export([dynamicsort_SET_components/1,
- dynamicsort_SETOF/1]).
-
-check_bool(_Bool,asn1_DEFAULT) ->
- true;
-check_bool(Bool,Bool) when Bool == true; Bool == false ->
- true;
-check_bool(_Bool1,Bool2) ->
- throw({error,Bool2}).
-
-check_int(_,asn1_DEFAULT,_) ->
- true;
-check_int(Value,Value,_) when is_integer(Value) ->
- true;
-check_int(DefValue,Value,NNL) when is_atom(Value) ->
- case lists:keysearch(Value,1,NNL) of
- {value,{_,DefValue}} ->
- true;
- _ ->
- throw({error,DefValue})
- end;
-check_int(DefaultValue,_Value,_) ->
- throw({error,DefaultValue}).
-
-% check_bitstring([H|T],[H|T],_) when is_integer(H) ->
-% true;
-% check_bitstring(V,V,_) when is_integer(V) ->
-% true;
-%% Two equal lists or integers
-check_bitstring(_,asn1_DEFAULT,_) ->
- true;
-check_bitstring(V,V,_) ->
- true;
-%% Default value as a list of 1 and 0 and user value as an integer
-check_bitstring(L=[H|T],Int,_) when is_integer(Int),is_integer(H) ->
- case bit_list_to_int(L,length(T)) of
- Int -> true;
- _ -> throw({error,L,Int})
- end;
-%% Default value as an integer, val as list
-check_bitstring(Int,Val,NBL) when is_integer(Int),is_list(Val) ->
- BL = int_to_bit_list(Int,[],length(Val)),
- check_bitstring(BL,Val,NBL);
-%% Default value and user value as lists of ones and zeros
-check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL=[_H|_T]) when is_integer(H1),is_integer(H2) ->
- L2new = remove_trailing_zeros(L2),
- check_bitstring(L1,L2new,NBL);
-%% Default value as a list of 1 and 0 and user value as a list of atoms
-check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL) when is_integer(H1),is_atom(H2) ->
- L3 = bit_list_to_nbl(L1,NBL,0,[]),
- check_bitstring(L3,L2,NBL);
-%% Both default value and user value as a list of atoms
-check_bitstring(L1=[H1|T1],L2=[H2|_T2],_)
- when is_atom(H1),is_atom(H2),length(L1) == length(L2) ->
- case lists:member(H1,L2) of
- true ->
- check_bitstring1(T1,L2);
- false -> throw({error,L2})
- end;
-%% Default value as a list of atoms and user value as a list of 1 and 0
-check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL) when is_atom(H1),is_integer(H2) ->
- L3 = bit_list_to_nbl(L2,NBL,0,[]),
- check_bitstring(L1,L3,NBL);
-%% User value in compact format
-check_bitstring(DefVal,CBS={_,_},NBL) ->
- NewVal = cbs_to_bit_list(CBS),
- check_bitstring(DefVal,NewVal,NBL);
-check_bitstring(DV,V,_) ->
- throw({error,DV,V}).
-
-
-bit_list_to_int([0|Bs],ShL)->
- bit_list_to_int(Bs,ShL-1) + 0;
-bit_list_to_int([1|Bs],ShL) ->
- bit_list_to_int(Bs,ShL-1) + (1 bsl ShL);
-bit_list_to_int([],_) ->
- 0.
-
-int_to_bit_list(0,Acc,0) ->
- Acc;
-int_to_bit_list(Int,Acc,Len) ->
- int_to_bit_list(Int bsr 1,[Int band 1|Acc],Len - 1).
-
-bit_list_to_nbl([0|T],NBL,Pos,Acc) ->
- bit_list_to_nbl(T,NBL,Pos+1,Acc);
-bit_list_to_nbl([1|T],NBL,Pos,Acc) ->
- case lists:keysearch(Pos,2,NBL) of
- {value,{N,_}} ->
- bit_list_to_nbl(T,NBL,Pos+1,[N|Acc]);
- _ ->
- throw({error,{no,named,element,at,pos,Pos}})
- end;
-bit_list_to_nbl([],_,_,Acc) ->
- Acc.
-
-remove_trailing_zeros(L2) ->
- remove_trailing_zeros1(lists:reverse(L2)).
-remove_trailing_zeros1(L) ->
- lists:reverse(lists:dropwhile(fun(0)->true;
- (_) ->false
- end,
- L)).
-
-check_bitstring1([H|T],NBL) ->
- case lists:member(H,NBL) of
- true ->
- check_bitstring1(T,NBL);
- V -> throw({error,V})
- end;
-check_bitstring1([],_) ->
- true.
-
-cbs_to_bit_list({Unused,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Rest/binary>>}) when size(Rest) >= 1 ->
- [B7,B6,B5,B4,B3,B2,B1,B0|cbs_to_bit_list({Unused,Rest})];
-cbs_to_bit_list({0,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1>>}) ->
- [B7,B6,B5,B4,B3,B2,B1,B0];
-cbs_to_bit_list({Unused,Bin}) when size(Bin) == 1 ->
- Used = 8-Unused,
- <<Int:Used,_:Unused>> = Bin,
- int_to_bit_list(Int,[],Used).
-
-
-check_octetstring(_,asn1_DEFAULT) ->
- true;
-check_octetstring(L,L) ->
- true;
-check_octetstring(L,Int) when is_list(L),is_integer(Int) ->
- case integer_to_octetlist(Int) of
- L -> true;
- V -> throw({error,V})
- end;
-check_octetstring(_,V) ->
- throw({error,V}).
-
-integer_to_octetlist(Int) ->
- integer_to_octetlist(Int,[]).
-integer_to_octetlist(0,Acc) ->
- Acc;
-integer_to_octetlist(Int,Acc) ->
- integer_to_octetlist(Int bsr 8,[(Int band 255)|Acc]).
-
-check_null(_,asn1_DEFAULT) ->
- true;
-check_null('NULL','NULL') ->
- true;
-check_null(_,V) ->
- throw({error,V}).
-
-check_objectidentifier(_,asn1_DEFAULT) ->
- true;
-check_objectidentifier(OI,OI) ->
- true;
-check_objectidentifier(DOI,OI) when is_tuple(DOI),is_tuple(OI) ->
- check_objectidentifier1(tuple_to_list(DOI),tuple_to_list(OI));
-check_objectidentifier(_,OI) ->
- throw({error,OI}).
-
-check_objectidentifier1([V|Rest1],[V|Rest2]) ->
- check_objectidentifier1(Rest1,Rest2,V);
-check_objectidentifier1([V1|Rest1],[V2|Rest2]) ->
- case reserved_objectid(V2,[]) of
- V1 ->
- check_objectidentifier1(Rest1,Rest2,[V1]);
- V ->
- throw({error,V})
- end.
-check_objectidentifier1([V|Rest1],[V|Rest2],Above) ->
- check_objectidentifier1(Rest1,Rest2,[V|Above]);
-check_objectidentifier1([V1|Rest1],[V2|Rest2],Above) ->
- case reserved_objectid(V2,Above) of
- V1 ->
- check_objectidentifier1(Rest1,Rest2,[V1|Above]);
- V ->
- throw({error,V})
- end;
-check_objectidentifier1([],[],_) ->
- true;
-check_objectidentifier1(_,V,_) ->
- throw({error,object,identifier,V}).
-
-%% ITU-T Rec. X.680 Annex B - D
-reserved_objectid('itu-t',[]) -> 0;
-reserved_objectid('ccitt',[]) -> 0;
-%% arcs below "itu-t"
-reserved_objectid('recommendation',[0]) -> 0;
-reserved_objectid('question',[0]) -> 1;
-reserved_objectid('administration',[0]) -> 2;
-reserved_objectid('network-operator',[0]) -> 3;
-reserved_objectid('identified-organization',[0]) -> 4;
-
-reserved_objectid(iso,[]) -> 1;
-%% arcs below "iso", note that number 1 is not used
-reserved_objectid('standard',[1]) -> 0;
-reserved_objectid('member-body',[1]) -> 2;
-reserved_objectid('identified-organization',[1]) -> 3;
-
-reserved_objectid('joint-iso-itu-t',[]) -> 2;
-reserved_objectid('joint-iso-ccitt',[]) -> 2;
-
-reserved_objectid(_,_) -> false.
-
-
-check_objectdescriptor(_,asn1_DEFAULT) ->
- true;
-check_objectdescriptor(OD,OD) ->
- true;
-check_objectdescriptor(OD,OD) ->
- throw({error,{not_implemented_yet,check_objectdescriptor}}).
-
-check_real(_,asn1_DEFAULT) ->
- true;
-check_real(R,R) ->
- true;
-check_real(_,_) ->
- throw({error,{not_implemented_yet,check_real}}).
-
-check_enum(_,asn1_DEFAULT,_) ->
- true;
-check_enum(Val,Val,_) ->
- true;
-check_enum(Int,Atom,Enumerations) when is_integer(Int),is_atom(Atom) ->
- case lists:keysearch(Atom,1,Enumerations) of
- {value,{_,Int}} -> true;
- _ -> throw({error,{enumerated,Int,Atom}})
- end;
-check_enum(DefVal,Val,_) ->
- throw({error,{enumerated,DefVal,Val}}).
-
-
-check_restrictedstring(_,asn1_DEFAULT) ->
- true;
-check_restrictedstring(Val,Val) ->
- true;
-check_restrictedstring([V|Rest1],[V|Rest2]) ->
- check_restrictedstring(Rest1,Rest2);
-check_restrictedstring([V1|Rest1],[V2|Rest2]) ->
- check_restrictedstring(V1,V2),
- check_restrictedstring(Rest1,Rest2);
-%% tuple format of value
-check_restrictedstring({V1,V2},[V1,V2]) ->
- true;
-check_restrictedstring([V1,V2],{V1,V2}) ->
- true;
-%% quadruple format of value
-check_restrictedstring({V1,V2,V3,V4},[V1,V2,V3,V4]) ->
- true;
-check_restrictedstring([V1,V2,V3,V4],{V1,V2,V3,V4}) ->
- true;
-%% character string list
-check_restrictedstring(V1,V2) when is_list(V1),is_tuple(V2) ->
- check_restrictedstring(V1,tuple_to_list(V2));
-check_restrictedstring(V1,V2) ->
- throw({error,{restricted,string,V1,V2}}).
-
-transform_to_EXTERNAL1990(Val) when is_tuple(Val),size(Val) == 4 ->
- transform_to_EXTERNAL1990(tuple_to_list(Val),[]);
-transform_to_EXTERNAL1990(Val) when is_tuple(Val) ->
- %% Data already in ASN1 1990 format
- Val.
-
-transform_to_EXTERNAL1990(['EXTERNAL'|Rest],Acc) ->
- transform_to_EXTERNAL1990(Rest,['EXTERNAL'|Acc]);
-transform_to_EXTERNAL1990([{syntax,Syntax}|Rest],Acc) ->
- transform_to_EXTERNAL1990(Rest,[asn1_NOVALUE,Syntax|Acc]);
-transform_to_EXTERNAL1990([{'presentation-context-id',PCid}|Rest],Acc) ->
- transform_to_EXTERNAL1990(Rest,[PCid,asn1_NOVALUE|Acc]);
-transform_to_EXTERNAL1990([{'context-negotiation',Context_negot}|Rest],Acc) ->
- {_,Presentation_Cid,Transfer_syntax} = Context_negot,
- transform_to_EXTERNAL1990(Rest,[Presentation_Cid,Transfer_syntax|Acc]);
-transform_to_EXTERNAL1990([asn1_NOVALUE|Rest],Acc) ->
- transform_to_EXTERNAL1990(Rest,[asn1_NOVALUE|Acc]);
-transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc) when is_list(Data_value)->
- list_to_tuple(lists:reverse([{'octet-aligned',Data_value},
- Data_val_desc|Acc]));
-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); is_binary(Data_value) ->
- list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
-
-
-transform_to_EXTERNAL1994(V={'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}) ->
- Identification =
- case {DRef,IndRef} of
- {DRef,asn1_NOVALUE} ->
- {syntax,DRef};
- {asn1_NOVALUE,IndRef} ->
- {'presentation-context-id',IndRef};
- _ ->
- {'context-negotiation',
- {'EXTERNAL_identification_context-negotiation',IndRef,DRef}}
- end,
- case Encoding of
- {_,Val} when is_list(Val);is_binary(Val) ->
- {'EXTERNAL',Identification,Data_v_desc,Val};
-
- _ ->
- V
- end.
-
-
-%% dynamicsort_SET_components(Arg) ->
-%% Res Arg -> list()
-%% Res -> list()
-%% Sorts the elements in Arg according to the encoded tag in
-%% increasing order.
-dynamicsort_SET_components(ListOfEncCs) ->
- BinL = lists:map(fun(X) -> list_to_binary(X) end,ListOfEncCs),
- TagBinL = lists:map(fun(X) ->
- {{T,_,TN},_,_} = asn1rt_ber_bin:decode_tag(X),
- {{T,TN},X}
- end,BinL),
- ClassTagNoSorted = lists:keysort(1,TagBinL),
- lists:map(fun({_,El}) -> El end,ClassTagNoSorted).
-
-%% dynamicsort_SETOF(Arg) -> Res
-%% Arg -> list()
-%% Res -> list()
-%% Sorts the elements in Arg in increasing size
-dynamicsort_SETOF(ListOfEncVal) ->
- BinL = lists:map(fun(L) when is_list(L) -> list_to_binary(L);
- (B) -> B end,ListOfEncVal),
- lists:sort(BinL).
diff --git a/lib/asn1/src/asn1rt_nif.erl b/lib/asn1/src/asn1rt_nif.erl
index de1fb94816..c1879e3dcf 100644
--- a/lib/asn1/src/asn1rt_nif.erl
+++ b/lib/asn1/src/asn1rt_nif.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -77,10 +77,31 @@ load_nif() ->
Status
end.
-encode_per_complete(_TagValueList) ->
+decode_ber_tlv(Binary) ->
+ case decode_ber_tlv_raw(Binary) of
+ {error,Reason} ->
+ exit({error,{asn1,Reason}});
+ Other ->
+ Other
+ end.
+
+encode_per_complete(TagValueList) ->
+ case encode_per_complete_raw(TagValueList) of
+ {error,Reason} -> handle_error(Reason, TagValueList);
+ Other when is_binary(Other) -> Other
+ end.
+
+handle_error([], _)->
+ exit({error,{asn1,enomem}});
+handle_error($1, L) -> % error in complete in driver
+ exit({error,{asn1,L}});
+handle_error(ErrL, L) ->
+ exit({error,{asn1,ErrL,L}}).
+
+encode_per_complete_raw(_TagValueList) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).
-decode_ber_tlv(_Binary) ->
+decode_ber_tlv_raw(_Binary) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).
encode_ber_tlv(_TagValueList) ->
diff --git a/lib/asn1/src/asn1rt_per_bin.erl b/lib/asn1/src/asn1rt_per_bin.erl
deleted file mode 100644
index 5772f09bf4..0000000000
--- a/lib/asn1/src/asn1rt_per_bin.erl
+++ /dev/null
@@ -1,2285 +0,0 @@
-%%
-%% %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 online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT 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, minimum_bits/1]).
-
--define('16K',16384).
--define('32K',32768).
--define('64K',65536).
-
-dec_fixup(Terms,Cnames,RemBytes) ->
- dec_fixup(Terms,Cnames,RemBytes,[]).
-
-dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
- dec_fixup(T,Tc,RemBytes,Acc);
-dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
- dec_fixup(T,Tc,RemBytes,Acc);
-dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
- dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
-dec_fixup([],_Cnames,RemBytes,Acc) ->
- {lists:reverse(Acc),RemBytes}.
-
-cindex(Ix,Val,Cname) ->
- case element(Ix,Val) of
- {Cname,Val2} -> Val2;
- X -> X
- end.
-
-%% converts a list to a record if necessary
-list_to_record(_Name,Tuple) when 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 =< (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),
- [{bits, LengthBitsNeeded, OctsLen - 1}, {octets, Octs}];
- true ->
- exit({not_supported,{integer_range,Range}})
- end;
-encode_constrained_number(Range,Val) ->
- exit({error,{asn1,{integer_range,Range,value,Val}}}).
-
-%% For some reason the minimum bits needed in the length field in encoding of
-%% constrained whole numbers must always be atleast 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.
-
-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 =< (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_list(Bytes, Len),
- {binary:decode_unsigned(binary:list_to_bin(Octs)), RestBytes};
- 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_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
deleted file mode 100644
index 1df757a47f..0000000000
--- a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl
+++ /dev/null
@@ -1,1748 +0,0 @@
-%%
-%% %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%
-%%
-%%
--module(asn1rt_per_bin_rt2ct).
-%% encoding / decoding of PER aligned
-
--include("asn1_records.hrl").
-
--export([dec_fixup/3, cindex/3, list_to_record/2]).
--export([setchoiceext/1, setext/1, fixoptionals/3, fixextensions/2,
- getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
--export([getoptionals/2, getoptionals2/2,
- set_choice/3, encode_integer/2, encode_integer/3 ]).
--export([decode_integer/2, decode_integer/3, encode_small_number/1,
- decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
- encode_small_length/1, decode_small_length/1,
- decode_compact_bit_string/3]).
--export([decode_enumerated/3,
- encode_bit_string/3, decode_bit_string/3 ]).
--export([encode_octet_string/2, decode_octet_string/2,
- encode_null/1, decode_null/1,
- encode_object_identifier/1, decode_object_identifier/1,
- 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,
- encode_GraphicString/2, decode_GraphicString/2,
- encode_TeletexString/2, decode_TeletexString/2,
- encode_VideotexString/2, decode_VideotexString/2,
- encode_ObjectDescriptor/2, decode_ObjectDescriptor/1,
- 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,
- encode_octet_string/3,
- decode_octet_string/3,
- encode_known_multiplier_string/5,
- decode_known_multiplier_string/5,
- getoctets/2, getbits/2
-% start_drv/1,start_drv2/1,init_drv/1
- ]).
-
-
--export([eint_positive/1]).
--export([pre_complete_bits/2]).
-
--define('16K',16384).
--define('32K',32768).
--define('64K',65536).
-
-%%-define(nodriver,true).
-
-dec_fixup(Terms,Cnames,RemBytes) ->
- dec_fixup(Terms,Cnames,RemBytes,[]).
-
-dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
- dec_fixup(T,Tc,RemBytes,Acc);
-dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
- dec_fixup(T,Tc,RemBytes,Acc);
-dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
- dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
-dec_fixup([],_Cnames,RemBytes,Acc) ->
- {lists:reverse(Acc),RemBytes}.
-
-cindex(Ix,Val,Cname) ->
- case element(Ix,Val) of
- {Cname,Val2} -> Val2;
- X -> X
- end.
-
-%% converts a list to a record if necessary
-list_to_record(_,Tuple) when 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) ->
- [0];
-setchoiceext(false) ->
- [1].
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% setext(true|false) -> CompleteList
-%%
-
-setext(false) ->
-% [{debug,ext},{bits,1,0}];
- [0];
-setext(true) ->
-% [{debug,ext},{bits,1,1}];
- [1].
-
-fixoptionals(OptList,_OptLength,Val) when is_tuple(Val) ->
-% Bits = fixoptionals(OptList,Val,0),
-% {Val,{bits,OptLength,Bits}};
-% {Val,[10,OptLength,Bits]};
- {Val,fixoptionals(OptList,Val,[])};
-
-fixoptionals([],_,Acc) ->
- %% Optbits
- lists:reverse(Acc);
-fixoptionals([{Pos,DefVal}|Ot],Val,Acc) ->
- case element(Pos,Val) of
- asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
- DefVal -> fixoptionals(Ot,Val,[0|Acc]);
- _ -> fixoptionals(Ot,Val,[1|Acc])
- end;
-fixoptionals([Pos|Ot],Val,Acc) ->
- case element(Pos,Val) of
- asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]);
- asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
- _ -> fixoptionals(Ot,Val,[1|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 -> [];
- ExtBits ->
- [encode_small_length(ExtNum),pre_complete_bits(ExtNum,ExtBits)]
- end.
-
-fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
- Acc;
-fixextensions(Pos,ExtPos,Val,Acc) ->
- Bit = case catch(element(Pos+1,Val)) of
- asn1_NOVALUE ->
- 0;
- asn1_NOEXTVALUE ->
- 0;
- {'EXIT',_} ->
- 0;
- _ ->
- 1
- end,
- fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
-
-skipextensions(Bytes,Nr,ExtensionBitstr) when is_bitstring(ExtensionBitstr) ->
- Prev = Nr - 1,
- case ExtensionBitstr of
- <<_:Prev,1:1,_/bitstring>> ->
- {_,Bytes2} = decode_open_type(Bytes,[]),
- skipextensions(Bytes2, Nr+1, ExtensionBitstr);
- <<_:Prev,0:1,_/bitstring>> ->
- skipextensions(Bytes, Nr+1, ExtensionBitstr);
- _ ->
- Bytes
- end.
-
-
-getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
- {0,Bytes};
-getchoice(Bytes,_,1) ->
- decode_small_number(Bytes);
-getchoice(Bytes,NumChoices,0) ->
- decode_constrained_number(Bytes,{0,NumChoices-1}).
-
-%% old version kept for backward compatibility with generates from R7B01
-getoptionals(Bytes,NumOpt) ->
- 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(),
-%% Bytes = bitstring(),
-%% Bin = bitstring(),
-%% Rest = bitstring()
-getbits_as_binary(Num,Bytes) when is_bitstring(Bytes) ->
- <<BS:Num/bitstring,Rest/bitstring>> = Bytes,
- {BS,Rest}.
-
-getbits_as_list(Num,Bytes) when is_bitstring(Bytes) ->
- <<BitStr:Num/bitstring,Rest/bitstring>> = Bytes,
- {[ B || <<B:1>> <= BitStr],Rest}.
-
-
-getbit(Buffer) ->
- <<B:1,Rest/bitstring>> = Buffer,
- {B,Rest}.
-
-
-getbits(Buffer,Num) when is_bitstring(Buffer) ->
- <<Bs:Num,Rest/bitstring>> = Buffer,
- {Bs,Rest}.
-
-align(Bin) when is_binary(Bin) ->
- Bin;
-align(BitStr) when is_bitstring(BitStr) ->
- AlignBits = bit_size(BitStr) rem 8,
- <<_:AlignBits,Rest/binary>> = BitStr,
- Rest.
-
-
-%% First align buffer, then pick the first Num octets.
-%% Returns octets as an integer with bit significance as in buffer.
-getoctets(Buffer,Num) when is_binary(Buffer) ->
- <<Val:Num/integer-unit:8,RestBin/binary>> = Buffer,
- {Val,RestBin};
-getoctets(Buffer,Num) when is_bitstring(Buffer) ->
- AlignBits = bit_size(Buffer) rem 8,
- <<_:AlignBits,Val:Num/integer-unit:8,RestBin/binary>> = Buffer,
- {Val,RestBin}.
-
-
-%% First align buffer, then pick the first Num octets.
-%% Returns octets as a binary
-getoctets_as_bin(Bin,Num) when is_binary(Bin) ->
- <<Octets:Num/binary,RestBin/binary>> = Bin,
- {Octets,RestBin};
-getoctets_as_bin(Bin,Num) when is_bitstring(Bin) ->
- AlignBits = bit_size(Bin) rem 8,
- <<_:AlignBits,Val:Num/binary,RestBin/binary>> = Bin,
- {Val,RestBin}.
-
-
-%% 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_constrained_number({0,Len1-1},N)];
- [0, % the value is in the root set
- encode_constrained_number({0,Len1-1},N)];
- N when is_integer(N) ->
-% [{bits,1,0}]; % no encoding if only 0 or 1 alternative
- [0]; % no encoding if only 0 or 1 alternative
- false ->
-% [{bits,1,1}, % extension value
- [1, % extension value
- case set_choice_tag(Alt,L2) of
- N2 when 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_constrained_number({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 binary().
-%% C is the constrained length.
-%% If the buffer is not aligned, this function does that.
-decode_fragmented_bits(Buffer,C) when is_binary(Buffer) ->
- decode_fragmented_bits(Buffer,C,[]);
-decode_fragmented_bits(Buffer,C) when is_bitstring(Buffer) ->
- AlignBits = bit_size(Buffer) rem 8,
- <<_:AlignBits,Rest/binary>> = Buffer,
- decode_fragmented_bits(Rest,C,[]).
-
-decode_fragmented_bits(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
- {Value,Bin2} = split_binary(Bin, Len * ?'16K'), % Len = 1 | 2 | 3 | 4
- decode_fragmented_bits(Bin2,C,[Value|Acc]);
-decode_fragmented_bits(<<0:1,0:7,Bin/binary>>,C,Acc) ->
- BinBits = erlang:list_to_bitstring(lists:reverse(Acc)),
- case C of
- Int when is_integer(Int),C == bit_size(BinBits) ->
- {BinBits,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) ->
- <<Value:Len/bitstring,Rest/bitstring>> = Bin,
- BinBits = erlang:list_to_bitstring([Value|Acc]),
- case C of
- Int when is_integer(Int),C == bit_size(BinBits) ->
- {BinBits,Rest};
- Int when is_integer(Int) ->
- 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
-%% Value = list of bytes of an already encoded value (the list must be flat)
-%% | binary
-%% Contraint = not used in this version
-%%
-encode_open_type(_Constraint, Val) when is_list(Val) ->
- Bin = list_to_binary(Val),
- case size(Bin) of
- Size when Size>255 ->
- [encode_length(undefined,Size),[21,<<Size:16>>,Bin]];
- Size ->
- [encode_length(undefined,Size),[20,Size,Bin]]
- end;
-encode_open_type(_Constraint, Val) when is_binary(Val) ->
- case size(Val) of
- Size when Size>255 ->
- [encode_length(undefined,size(Val)),[21,<<Size:16>>,Val]]; % octets implies align
- Size ->
- [encode_length(undefined,Size),[20,Size,Val]]
- end.
-%% the binary_to_list is not optimal but compatible with the current solution
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_open_type(Buffer,Constraint) -> Value
-%% Constraint is not used in this version
-%% Buffer = [byte] with PER encoded data
-%% Value = [byte] with decoded data (which must be decoded again as some type)
-%%
-decode_open_type(Bytes, _Constraint) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- getoctets_as_bin(Bytes2,Len).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
-%% encode_integer(Constraint,Value) -> CompleteList
-%% encode_integer(Constraint,{Name,Value}) -> CompleteList
-%%
-%%
-encode_integer(C,V,NamedNumberList) when 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)];
- [1,encode_unconstrained_number(Val)];
- Encoded ->
-% [{bits,1,0},Encoded]
- [0,Encoded]
- end;
-
-encode_integer([],Val) ->
- encode_unconstrained_number(Val);
-%% The constraint is the effective constraint, and in this case is a number
-encode_integer([{'SingleValue',V}],V) ->
- [];
-encode_integer([{'ValueRange',VR={Lb,Ub},Range,PreEnc}],Val) when Val >= Lb,
- Ub >= Val ->
- %% this case when NamedNumberList
- encode_constrained_number(VR,Range,PreEnc,Val);
-encode_integer([{'ValueRange',{Lb,'MAX'}}],Val) ->
- encode_semi_constrained_number(Lb,Val);
-encode_integer([{'ValueRange',{'MIN',_}}],Val) ->
- encode_unconstrained_number(Val);
-encode_integer([{'ValueRange',VR={_Lb,_Ub}}],Val) ->
- encode_constrained_number(VR,Val);
-encode_integer(_,Val) ->
- exit({error,{asn1,{illegal_value,Val}}}).
-
-
-
-decode_integer(Buffer,Range,NamedNumberList) ->
- {Val,Buffer2} = decode_integer(Buffer,Range),
- case lists:keysearch(Val,2,NamedNumberList) of
- {value,{NewVal,_}} -> {NewVal,Buffer2};
- _ -> {Val,Buffer2}
- end.
-
-decode_integer(Buffer,[{Rc,_Ec}]) when 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
-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
- [10,7,Val]; % same as above but more efficient
-encode_small_number(Val) ->
-% [{bits,1,1},encode_semi_constrained_number(0,Val)].
- [1,encode_semi_constrained_number(0,Val)].
-
-decode_small_number(Bytes) ->
- {Bit,Bytes2} = getbit(Bytes),
- case Bit of
- 0 ->
- getbits(Bytes2,6);
- 1 ->
- decode_semi_constrained_number(Bytes2,0)
- end.
-
-%% X.691:10.7 Encoding of a semi-constrained whole number
-%% might be an optimization encode_semi_constrained_number(0,Val) ->
-encode_semi_constrained_number(C,{Name,Val}) when 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
- [20,Len+1,[Len|Oct]];
- Len < 256 ->
- [encode_length(undefined,Len),[20,Len,Oct]];
- true ->
- [encode_length(undefined,Len),[21,<<Len:16>>,Oct]]
- end.
-
-decode_semi_constrained_number(Bytes,{Lb,_}) ->
- decode_semi_constrained_number(Bytes,Lb);
-decode_semi_constrained_number(Bytes,Lb) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- {V,Bytes3} = getoctets(Bytes2,Len),
- {V+Lb,Bytes3}.
-
-encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) ->
- Val2 = Val-Lb,
-% {bits,N,Val2};
- [10,N,Val2];
-encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) when N < 256->
- %% N is 8 or 16 (1 or 2 octets)
- Val2 = Val-Lb,
-% {octets,<<Val2:N/unit:8>>};
- [20,N,Val2];
-encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) -> % N>255
- %% N is 8 or 16 (1 or 2 octets)
- Val2 = Val-Lb,
-% {octets,<<Val2:N/unit:8>>};
- [21,<<N:16>>,Val2];
-encode_constrained_number({Lb,_Ub},Range,_,Val) ->
- Val2 = Val-Lb,
- if
- Range =< 16#1000000 -> % max 3 octets
- Octs = eint_positive(Val2),
-% [encode_length({1,3},size(Octs)),{octets,Octs}];
- L = length(Octs),
- [encode_length({1,3},L),[20,L,Octs]];
- Range =< 16#100000000 -> % max 4 octets
- Octs = eint_positive(Val2),
-% [encode_length({1,4},size(Octs)),{octets,Octs}];
- L = length(Octs),
- [encode_length({1,4},L),[20,L,Octs]];
- Range =< 16#10000000000 -> % max 5 octets
- Octs = eint_positive(Val2),
-% [encode_length({1,5},size(Octs)),{octets,Octs}];
- L = length(Octs),
- [encode_length({1,5},L),[20,L,Octs]];
- true ->
- exit({not_supported,{integer_range,Range}})
- end.
-
-encode_constrained_number(Range,{Name,Val}) when 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 ->
-% Size = {bits,1,Val2};
- [Val2];
- Range =< 4 ->
-% Size = {bits,2,Val2};
- [10,2,Val2];
- Range =< 8 ->
- [10,3,Val2];
- Range =< 16 ->
- [10,4,Val2];
- Range =< 32 ->
- [10,5,Val2];
- Range =< 64 ->
- [10,6,Val2];
- Range =< 128 ->
- [10,7,Val2];
- Range =< 255 ->
- [10,8,Val2];
- Range =< 256 ->
-% Size = {octets,[Val2]};
- [20,1,Val2];
- Range =< 65536 ->
-% Size = {octets,<<Val2:16>>};
- [20,2,<<Val2:16>>];
- Range =< (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 = asn1rt_per_bin:minimum_bits(RangeOctsLen - 1),
- [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs];
- true ->
- exit({not_supported,{integer_range,Range}})
- end;
-encode_constrained_number({_,_},Val) ->
- exit({error,{asn1,{illegal_value,Val}}}).
-
-decode_constrained_number(Buffer,VR={Lb,Ub}) ->
- Range = Ub - Lb + 1,
- decode_constrained_number(Buffer,VR,Range).
-
-decode_constrained_number(Buffer,{Lb,_Ub},_Range,{bits,N}) ->
- {Val,Remain} = getbits(Buffer,N),
- {Val+Lb,Remain};
-decode_constrained_number(Buffer,{Lb,_Ub},_Range,{octets,N}) ->
- {Val,Remain} = getoctets(Buffer,N),
- {Val+Lb,Remain}.
-
-decode_constrained_number(Buffer,{Lb,_Ub},Range) ->
- % Val2 = Val - Lb,
- {Val,Remain} =
- if
- Range == 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 =< (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}.
-
-%% X.691:10.8 Encoding of an unconstrained whole number
-
-encode_unconstrained_number(Val) when Val >= 0 ->
- Oct = eint(Val,[]),
- Len = length(Oct),
- if
- Len < 128 ->
- %{octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
- [20,Len+1,[Len|Oct]];
- Len < 256 ->
-% [encode_length(undefined,Len),20,Len,Oct];
- [20,Len+2,<<2:2,Len:14>>,Oct];% equiv with encode_length(undefined,Len) but faster
- true ->
-% [encode_length(undefined,Len),{octets,Oct}]
- [encode_length(undefined,Len),[21,<<Len:16>>,Oct]]
- end;
-encode_unconstrained_number(Val) -> % negative
- Oct = enint(Val,[]),
- Len = length(Oct),
- if
- Len < 128 ->
-% {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
- [20,Len+1,[Len|Oct]];% equiv with encode_length(undefined,Len) but faster
- Len < 256 ->
-% [encode_length(undefined,Len),20,Len,Oct];
- [20,Len+2,<<2:2,Len:14>>,Oct];% equiv with encode_length(undefined,Len) but faster
- true ->
- %[encode_length(undefined,Len),{octets,Oct}]
- [encode_length(undefined,Len),[21,<<Len:16>>,Oct]]
- end.
-
-
-%% used for positive Values which don't need a sign bit
-%% returns a list
-eint_positive(Val) ->
- case eint(Val,[]) of
- [0,B1|T] ->
- [B1|T];
- T ->
- T
- end.
-
-
-eint(0, [B|Acc]) when B < 128 ->
- [B|Acc];
-eint(N, Acc) ->
- eint(N bsr 8, [N band 16#ff| Acc]).
-
-enint(-1, [B1|T]) when B1 > 127 ->
- [B1|T];
-enint(N, Acc) ->
- enint(N bsr 8, [N band 16#ff|Acc]).
-
-decode_unconstrained_number(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- {Ints,Bytes3} = getoctets_as_bin(Bytes2,Len),
- {dec_integer(Ints),Bytes3}.
-
-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).
-
-%% X.691:10.9.3.5
-%% X.691:10.9.3.7
-encode_length(undefined,Len) -> % un-constrained
- if
- Len < 128 ->
-% {octets,[Len]};
- [20,1,Len];
- Len < 16384 ->
- %{octets,<<2:2,Len:14>>};
- [20,2,<<2:2,Len:14>>];
- true -> % should be able to endode length >= 16384 i.e. fragmented length
- exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
- end;
-
-encode_length({0,'MAX'},Len) ->
- encode_length(undefined,Len);
-encode_length(Vr={Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
- encode_constrained_number(Vr,Len);
-encode_length({Lb,_Ub},Len) when 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
- [0,encode_constrained_number(Vr,Len)];
-encode_length({{Lb,_},Ext},Len) when is_list(Ext) ->
- [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
- [10,7,Len-1];
-encode_small_length(Len) ->
-% [{bits,1,1},encode_length(undefined,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>> ->
- {Oct,Rest};
- <<2:2,Val:14,Rest/binary>> ->
- {Val,Rest};
- <<3:2,_Val:14,_Rest/binary>> ->
- %% this case should be fixed
- exit({error,{asn1,{decode_length,{nyi,above_16k}}}})
- end;
-
-decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
- decode_constrained_number(Buffer,{Lb,Ub});
-decode_length(Buffer,{Lb,_Ub}) when is_integer(Lb), Lb >= 0 -> % Ub > 65535
- decode_length(Buffer,undefined);
-decode_length(Buffer,{{Lb,Ub},Ext}) when is_list(Ext) ->
- case getbit(Buffer) of
- {0,Buffer2} ->
- decode_length(Buffer2, {Lb,Ub});
- {1,Buffer2} ->
- decode_length(Buffer2, undefined)
- end;
-
-
-%When does this case occur with {_,_Lb,Ub} ??
-% X.691:10.9.3.5
-decode_length(Bin,{_,_Lb,_Ub}) -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535
- case Bin of
- <<0:1,Val:7,Rest/bitstring>> ->
- {Val,Rest};
- _ ->
- case align(Bin) of
- <<2:2,Val:14,Rest/binary>> ->
- {Val,Rest};
- <<3:2,_:14,_Rest/binary>> ->
- exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}})
- end
- end;
-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.
-
-%%===============================================================================
-%%===============================================================================
-%%===============================================================================
-%% 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);% consider the constraint
-
-encode_bit_string(C, BL=[{bit,_} | _RestVal], NamedBitList) ->
- ToSetPos = get_all_bitposes(BL, NamedBitList, []),
- BitList = make_and_set_list(ToSetPos,0),
- encode_bit_string(C,BitList,NamedBitList);
-
-%% when the value is a list of ones and zeroes
-encode_bit_string(Int, BitListValue, _)
- when is_list(BitListValue),is_integer(Int),Int =< 16 ->
- %% The type is constrained by a single value size constraint
- %% range_check(Int,length(BitListValue)),
- [40,Int,length(BitListValue),BitListValue];
-encode_bit_string(Int, BitListValue, _)
- when is_list(BitListValue),is_integer(Int), Int =< 255 ->
- %% The type is constrained by a single value size constraint
- %% range_check(Int,length(BitListValue)),
- [2,40,Int,length(BitListValue),BitListValue];
-encode_bit_string(Int, BitListValue, _)
- when is_list(BitListValue),is_integer(Int), Int < ?'64K' ->
- {Code,DesiredLength,Length} =
- case length(BitListValue) of
- B1 when B1 > Int ->
- exit({error,{'BIT_STRING_length_greater_than_SIZE',
- Int,BitListValue}});
- B1 when B1 =< 255,Int =< 255 ->
- {40,Int,B1};
- B1 when B1 =< 255 ->
- {42,<<Int:16>>,B1};
- B1 ->
- {43,<<Int:16>>,<<B1:16>>}
- end,
- %% The type is constrained by a single value size constraint
- [2,Code,DesiredLength,Length,BitListValue];
-encode_bit_string(no, BitListValue,[])
- when is_list(BitListValue) ->
- [encode_length(undefined,length(BitListValue)),
- 2,BitListValue];
-encode_bit_string({{Fix,Fix},Ext}, BitListValue,[])
- when is_integer(Fix), is_list(Ext) ->
- case length(BitListValue) of
- Len when Len =< Fix ->
- [0,encode_bit_string(Fix,BitListValue,[])];
- _ ->
- [1,encode_bit_string(no,BitListValue,[])]
- end;
-encode_bit_string(C, BitListValue,[])
- when is_list(BitListValue) ->
- [encode_length(C,length(BitListValue)),
- 2,BitListValue];
-encode_bit_string(no, BitListValue,_NamedBitList)
- when is_list(BitListValue) ->
- %% this case with an unconstrained BIT STRING can be made more efficient
- %% if the complete driver can take a special code so the length field
- %% is encoded there.
- NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
- lists:reverse(BitListValue))),
- [encode_length(undefined,length(NewBitLVal)),
- 2,NewBitLVal];
-encode_bit_string({{Fix,Fix},Ext}, BitListValue,_NamedBitList)
- when is_integer(Fix), is_list(Ext) ->
- case length(BitListValue) of
- Len when Len =< Fix ->
- [0,encode_bit_string(Fix,BitListValue,_NamedBitList)];
- _ ->
- [1,encode_bit_string(no,BitListValue,_NamedBitList)]
- end;
-encode_bit_string(C,BitListValue,_NamedBitList)
- when is_list(BitListValue) ->% C = {_,'MAX'}
-% NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
-% lists:reverse(BitListValue))),
- NewBitLVal = bit_string_trailing_zeros(BitListValue,C),
- [encode_length(C,length(NewBitLVal)),
- 2,NewBitLVal];
-
-
-%% 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).
-
-bit_string_trailing_zeros(BitList,C) when is_integer(C) ->
- bit_string_trailing_zeros1(BitList,C,C);
-bit_string_trailing_zeros(BitList,{Lb,Ub}) when is_integer(Lb) ->
- bit_string_trailing_zeros1(BitList,Lb,Ub);
-bit_string_trailing_zeros(BitList,{{Lb,Ub},_}) when is_integer(Lb) ->
- bit_string_trailing_zeros1(BitList,Lb,Ub);
-bit_string_trailing_zeros(BitList,_) ->
- BitList.
-
-bit_string_trailing_zeros1(BitList,Lb,Ub) ->
- case length(BitList) of
- Lb -> BitList;
- B when B<Lb -> BitList++lists:duplicate(Lb-B,0);
- D -> F = fun(L,LB,LB,_,_)->lists:reverse(L);
- ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun);
- (L,L1,_,UB,_)when L1 =< UB -> lists:reverse(L);
- (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING,
- BitList}}) end,
- F(lists:reverse(BitList),D,Lb,Ub,F)
- end.
-
-%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
-%% Unused = integer(),i.e. number unused bits in least sign. byte of
-%% BinBits = binary().
-encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList)
- when is_integer(C),C=<16 ->
- range_check(C,bit_size(BinBits) - Unused),
- [45,C,size(BinBits),BinBits];
-encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList)
- when is_integer(C), C =< 255 ->
- range_check(C,bit_size(BinBits) - Unused),
- [2,45,C,size(BinBits),BinBits];
-encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList)
- when is_integer(C), C =< 65535 ->
- range_check(C,bit_size(BinBits) - Unused),
- case size(BinBits) of
- Size when Size =< 255 ->
- [2,46,<<C:16>>,Size,BinBits];
- Size ->
- [2,47,<<C:16>>,<<Size:16>>,BinBits]
- end;
-%% encode_bin_bit_string(C,{_Unused,BinBits},_NamedBitList)
-%% when is_integer(C) ->
-%% exit({error,{asn1, {bitstring_size, not_supported, C}}});
-encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) ->
-% UnusedAndBin1 = {Unused1,Bin1} =
- {Unused1,Bin1} =
- %% removes all trailing bits if NamedBitList is not empty
- remove_trailing_bin(NamedBitList,UnusedAndBin),
- case C of
- {Lb,Ub} when is_integer(Lb),is_integer(Ub) ->
-% [encode_length({Lb,Ub},size(Bin1)*8 - Unused1),
-% align,UnusedAndBin1];
- Size=size(Bin1),
- [encode_length({Lb,Ub},Size*8 - Unused1),
- 2,octets_unused_to_complete(Unused1,Size,Bin1)];
- no ->
- Size=size(Bin1),
- [encode_length(undefined,Size*8 - Unused1),
- 2,octets_unused_to_complete(Unused1,Size,Bin1)];
- {{Fix,Fix},Ext} when is_integer(Fix),is_list(Ext) ->
- %%[encode_length(Sc,size(Bin1)*8 - Unused1),
- case size(Bin1)*8 - Unused1 of
- Size when Size =< Fix ->
- [0,encode_bin_bit_string(Fix,UnusedAndBin,NamedBitList)];
- _Size ->
- [1,encode_bin_bit_string(no,UnusedAndBin,NamedBitList)]
- end;
- Sc ->
- Size=size(Bin1),
- [encode_length(Sc,Size*8 - Unused1),
- 2,octets_unused_to_complete(Unused1,Size,Bin1)]
- end.
-
-range_check(C,C) when is_integer(C) ->
- ok;
-range_check(C1,C2) when is_integer(C1) ->
- exit({error,{asn1,{bit_string_out_of_range,{C1,C2}}}}).
-
-remove_trailing_bin([], {Unused,Bin}) ->
- {Unused,Bin};
-remove_trailing_bin(_NamedNumberList,{_Unused,<<>>}) ->
- {0,<<>>};
-remove_trailing_bin(NamedNumberList, {_Unused,Bin}) ->
- Size = size(Bin)-1,
- <<Bfront:Size/binary, LastByte:8>> = Bin,
- %% clear the Unused bits to be sure
-% LastByte1 = LastByte band (((1 bsl Unused) -1) bxor 255),% why this???
- Unused1 = trailingZeroesInNibble(LastByte band 15),
- Unused2 =
- case Unused1 of
- 4 ->
- 4 + trailingZeroesInNibble(LastByte bsr 4);
- _ -> Unused1
- end,
- case Unused2 of
- 8 ->
- remove_trailing_bin(NamedNumberList,{0,Bfront});
- _ ->
- {Unused2,Bin}
- end.
-
-
-trailingZeroesInNibble(0) ->
- 4;
-trailingZeroesInNibble(1) ->
- 0;
-trailingZeroesInNibble(2) ->
- 1;
-trailingZeroesInNibble(3) ->
- 0;
-trailingZeroesInNibble(4) ->
- 2;
-trailingZeroesInNibble(5) ->
- 0;
-trailingZeroesInNibble(6) ->
- 1;
-trailingZeroesInNibble(7) ->
- 0;
-trailingZeroesInNibble(8) ->
- 3;
-trailingZeroesInNibble(9) ->
- 0;
-trailingZeroesInNibble(10) ->
- 1;
-trailingZeroesInNibble(11) ->
- 0;
-trailingZeroesInNibble(12) -> %#1100
- 2;
-trailingZeroesInNibble(13) ->
- 0;
-trailingZeroesInNibble(14) ->
- 1;
-trailingZeroesInNibble(15) ->
- 0.
-
-%%%%%%%%%%%%%%%
-%% The result is presented as a list of named bits (if possible)
-%% else as a tuple {Unused,Bits}. Unused is the number of unused
-%% bits, least significant bits in the last byte of Bits. Bits is
-%% the BIT STRING represented as a binary.
-%%
-decode_compact_bit_string(Buffer, C, NamedNumberList) ->
- case get_constraint(C,'SizeConstraint') of
- 0 -> % fixed length
- {{8,0},Buffer};
- V when 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
- {BitStr,Buffer2} = decode_fragmented_bits(Buffer,V),
- case bit_size(BitStr) band 7 of
- 0 -> {{0,BitStr},Buffer2};
- N -> {{8-N,<<BitStr/bitstring,0:(8-N)>>},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},Ext} = Sc when is_integer(Fix), is_list(Ext) ->
- case decode_length(Buffer,Sc) of
- {Len,Bytes2} when Len > Fix ->
- Bytes3 = align(Bytes2),
- compact_bit_string(Bytes3,Len,NamedNumberList);
- {Len,Bytes2} when Len > 16 ->
- 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,_Bytes3} = decode_fragmented_bits(Bytes2,V),
- bit_list_or_named(BinBits,V,NamedNumberList);
- {{Fix,Fix},Ext} =Sc when is_integer(Fix), is_list(Ext) ->
- 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 -> % 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,[]) ->
- {BitStr,Rest} = getbits_as_binary(Len,Buffer), % {{Unused,BinBits},NewBuffer}
- PadLen = (8 - (bit_size(BitStr) rem 8)) rem 8,
- {{PadLen,<<BitStr/bitstring,0:PadLen>>},Rest};
-compact_bit_string(Buffer,Len,NamedNumberList) ->
- bit_list_or_named(Buffer,Len,NamedNumberList).
-
-
-%% if no named bits are declared we will return a
-%% BitList = [0 | 1]
-
-bit_list_or_named(Buffer,Len,[]) ->
- getbits_as_list(Len,Buffer);
-
-%% if there are named bits declared we will return a named
-%% BitList where the names are atoms and unnamed bits represented
-%% as {bit,Pos}
-%% BitList = [atom() | {bit,Pos}]
-%% Pos = integer()
-
-bit_list_or_named(Buffer,Len,NamedNumberList) ->
- {BitList,Rest} = getbits_as_list(Len,Buffer),
- {bit_list_or_named1(0,BitList,NamedNumberList,[]), Rest}.
-
-bit_list_or_named1(Pos,[0|Bt],Names,Acc) ->
- bit_list_or_named1(Pos+1,Bt,Names,Acc);
-bit_list_or_named1(Pos,[1|Bt],Names,Acc) ->
- case lists:keysearch(Pos,2,Names) of
- {value,{Name,_}} ->
- bit_list_or_named1(Pos+1,Bt,Names,[Name|Acc]);
- _ ->
- bit_list_or_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
- end;
-bit_list_or_named1(_Pos,[],_Names,Acc) ->
- lists:reverse(Acc).
-
-
-
-%%%%%%%%%%%%%%%
-%%
-
-int_to_bitlist(Int) when 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([], _) ->
- [].
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% X.691:16
-%% encode_octet_string(Constraint,ExtensionMarker,Val)
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-encode_octet_string(C,Val) ->
- encode_octet_string(C,false,Val).
-
-encode_octet_string(C,Bool,{_Name,Val}) ->
- encode_octet_string(C,Bool,Val);
-encode_octet_string(_C,true,_Val) ->
- exit({error,{asn1,{'not_supported',extensionmarker}}});
-encode_octet_string(SZ={_,_},false,Val) ->
-% [encode_length(SZ,length(Val)),align,
-% {octets,Val}];
- Len = length(Val),
- [encode_length(SZ,Len),2,
- octets_to_complete(Len,Val)];
-encode_octet_string(SZ,false,Val) when is_list(SZ) ->
- Len = length(Val),
- [encode_length({hd(SZ),lists:max(SZ)},Len),2,
- octets_to_complete(Len,Val)];
-encode_octet_string(no,false,Val) ->
- Len = length(Val),
- [encode_length(undefined,Len),2,
- octets_to_complete(Len,Val)];
-encode_octet_string(C,_,_) ->
- exit({error,{not_implemented,C}}).
-
-
-decode_octet_string(Bytes,Range) ->
- decode_octet_string(Bytes,Range,false).
-
-decode_octet_string(<<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).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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)->
- Len = length(Val),
- [encode_length(undefined,Len),octets_to_complete(Len,Val)].
-
-
-encode_known_multiplier_string(StringType,SizeC,NumBits,CharOutTab,{Name,Val}) when is_atom(Name) ->
- encode_known_multiplier_string(StringType,SizeC,NumBits,CharOutTab,Val);
-encode_known_multiplier_string(_StringType,SizeC,NumBits,CharOutTab,Val) ->
- Result = chars_encode2(Val,NumBits,CharOutTab),
- case SizeC of
- Ub when is_integer(Ub), Ub*NumBits =< 16 ->
- Result;
- Ub when is_integer(Ub),Ub =<65535 -> % fixed length
- [2,Result];
- {Ub,Lb} ->
- [encode_length({Ub,Lb},length(Val)),2,Result];
- no ->
- [encode_length(undefined,length(Val)),2,Result]
- end.
-
-decode_restricted_string(Bytes,aligned) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- getoctets_as_list(Bytes2,Len).
-
-decode_known_multiplier_string(StringType,SizeC,NumBits,CharInTab,Bytes) ->
- case SizeC of
- Ub when is_integer(Ub), Ub*NumBits =< 16 ->
- chars_decode(Bytes,NumBits,StringType,CharInTab,Ub);
- Ub when is_integer(Ub),Ub =<65535 -> % fixed length
- Bytes1 = align(Bytes),
- chars_decode(Bytes1,NumBits,StringType,CharInTab,Ub);
- Vl when is_list(Vl) ->
- {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
- Bytes2 = align(Bytes1),
- chars_decode(Bytes2,NumBits,StringType,CharInTab,Len);
- no ->
- {Len,Bytes1} = decode_length(Bytes,undefined),
- Bytes2 = align(Bytes1),
- chars_decode(Bytes2,NumBits,StringType,CharInTab,Len);
- {Lb,Ub}->
- {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
- Bytes2 = align(Bytes1),
- chars_decode(Bytes2,NumBits,StringType,CharInTab,Len)
- end.
-
-encode_GeneralString(_C,Val) ->
- encode_restricted_string(aligned,Val).
-decode_GeneralString(Bytes,_C) ->
- decode_restricted_string(Bytes,aligned).
-
-encode_GraphicString(_C,Val) ->
- encode_restricted_string(aligned,Val).
-decode_GraphicString(Bytes,_C) ->
- decode_restricted_string(Bytes,aligned).
-
-encode_ObjectDescriptor(_C,Val) ->
- encode_restricted_string(aligned,Val).
-decode_ObjectDescriptor(Bytes) ->
- decode_restricted_string(Bytes,aligned).
-
-encode_TeletexString(_C,Val) -> % equivalent with T61String
- encode_restricted_string(aligned,Val).
-decode_TeletexString(Bytes,_C) ->
- decode_restricted_string(Bytes,aligned).
-
-encode_VideotexString(_C,Val) ->
- encode_restricted_string(aligned,Val).
-decode_VideotexString(Bytes,_C) ->
- decode_restricted_string(Bytes,aligned).
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
-%%
-getBMPChars(<<T/binary>>, 0, Acc) ->
- {lists:reverse(Acc),T};
-getBMPChars(<<0,O2,Bytes1/bitstring>>, Len, Acc) ->
- getBMPChars(Bytes1,Len-1,[O2|Acc]);
-getBMPChars(<<O1,O2,Bytes1/bitstring>>, Len, Acc) ->
- getBMPChars(Bytes1,Len-1,[{0,0,O1,O2}|Acc]).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% chars_encode(C,StringType,Value) -> ValueList
-%%
-%% encodes chars according to the per rules taking the constraint PermittedAlphabet
-%% into account.
-%% This function does only encode the value part and NOT the length
-
-% chars_encode(C,StringType,Value) ->
-% case {StringType,get_constraint(C,'PermittedAlphabet')} of
-% {'UniversalString',{_,Sv}} ->
-% exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
-% {'BMPString',{_,Sv}} ->
-% exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
-% _ ->
-% {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
-% chars_encode2(Value,NumBits,CharOutTab)
-% end.
-
-
-chars_encode2([H|T],NumBits,T1={Min,Max,notab}) when H =< Max, H >= Min ->
-% [[10,NumBits,H-Min]|chars_encode2(T,NumBits,T1)];
- [pre_complete_bits(NumBits,H-Min)|chars_encode2(T,NumBits,T1)];
-chars_encode2([H|T],NumBits,T1={Min,Max,Tab}) when H =< Max, H >= Min ->
-% [[10,NumBits,element(H-Min+1,Tab)]|chars_encode2(T,NumBits,T1)];
- [pre_complete_bits(NumBits,exit_if_false(H,element(H-Min+1,Tab)))|
- chars_encode2(T,NumBits,T1)];
-chars_encode2([{A,B,C,D}|T],NumBits,T1={Min,_Max,notab}) ->
- %% no value range check here (ought to be, but very expensive)
-% [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
-% [[10,NumBits,((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min]|chars_encode2(T,NumBits,T1)];
- [pre_complete_bits(NumBits,
- ((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min)|
- chars_encode2(T,NumBits,T1)];
-chars_encode2([H={A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
- %% no value range check here (ought to be, but very expensive)
- [pre_complete_bits(NumBits,exit_if_false(H,element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)))|chars_encode2(T,NumBits,{Min,Max,notab})];
-chars_encode2([H|_T],_NumBits,{_Min,_Max,_Tab}) ->
- exit({error,{asn1,{illegal_char_value,H}}});
-chars_encode2([],_,_) ->
- [].
-
-exit_if_false(V,false)->
- exit({error,{asn1,{"illegal value according to Permitted alphabet constraint",V}}});
-exit_if_false(_,V) ->V.
-
-pre_complete_bits(NumBits,Val) when NumBits =< 8 ->
- [10,NumBits,Val];
-pre_complete_bits(NumBits,Val) when NumBits =< 16 ->
- [10,NumBits-8,Val bsr 8,10,8,(Val band 255)];
-pre_complete_bits(NumBits,Val) when NumBits =< 2040 -> % 255 * 8
-% LBUsed = NumBits rem 8,
-% {Unused,Len} = case (8 - LBUsed) of
-% 8 -> {0,NumBits div 8};
-% U -> {U,(NumBits div 8) + 1}
-% end,
-% NewVal = Val bsr LBUsed,
-% [30,Unused,Len,<<NewVal:Len/unit:8,Val:LBUsed,0:Unused>>].
- Unused = (8 - (NumBits rem 8)) rem 8,
- Len = NumBits + Unused,
- [30,Unused,Len div 8,<<(Val bsl Unused):Len>>].
-
-
-chars_decode(Bytes,_,'BMPString',_,Len) ->
- getBMPChars(Bytes,Len,[]);
-chars_decode(Bytes,NumBits,_StringType,CharInTab,Len) ->
- chars_decode2(Bytes,CharInTab,NumBits,Len).
-
-
-chars_decode2(Bytes,CharInTab,NumBits,Len) ->
- chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
-
-chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
- {lists:reverse(Acc),Bytes};
-chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
- {Char,Bytes2} = getbits(Bytes,NumBits),
- Result =
- if
- Char < 256 -> Char;
- true ->
- list_to_tuple(binary_to_list(<<Char:32>>))
- end,
- chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
-chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
- {Char,Bytes2} = getbits(Bytes,NumBits),
- chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
-
-%% BMPString and UniversalString with PermittedAlphabet is currently not supported
-chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
- {Char,Bytes2} = getbits(Bytes,NumBits),
- chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
-
-
- % X.691:17
-encode_null(_Val) -> []. % encodes to nothing
-%encode_null({Name,Val}) when is_atom(Name) ->
-% encode_null(Val).
-
-decode_null(Bytes) ->
- {'NULL',Bytes}.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% encode_UTF8String(Val) -> CompleteList
-%% Val -> <<utf8encoded binary>>
-%% CompleteList -> [apropriate codes and values for driver complete]
-%%
-encode_UTF8String(Val) when is_binary(Val) ->
- [encode_length(undefined,size(Val)),
- octets_to_complete(size(Val),Val)];
-encode_UTF8String(Val) ->
- encode_UTF8String(list_to_binary(Val)).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_UTF8String(Bytes) -> {Utf8Binary,RemainingBytes}
-%% Utf8Binary -> <<utf8 encoded binary>>
-%% RemainingBytes -> <<buffer>>
-decode_UTF8String(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- {_Bin,_Bytes3} = getoctets_as_bin(Bytes2,Len).
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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),
- [encode_length(undefined,size(Octets)),
- octets_to_complete(size(Octets),Octets)].
-
-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_to_complete(size(Octets),Octets)].
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_relative_oid(Val) -> CompleteList
-%% decode_relative_oid({Name,Val}) -> CompleteList
-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_to_complete(size(EncVal),EncVal)].
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_real(Val) -> {REALvalue,Rest}
-%% decode_real({Name,Val}) -> {REALvalue,Rest}
-decode_real(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- {RealVal,Rest,Len} = ?RT_COMMON:decode_real(Bytes2,Len),
- {RealVal,Rest}.
-
-
-get_constraint([{Key,V}],Key) ->
- V;
-get_constraint([],_) ->
- no;
-get_constraint(C,Key) ->
- case lists:keysearch(Key,1,C) of
- false ->
- no;
- {value,{_,V}} ->
- V
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% complete(InList) -> ByteList
-%% Takes a coded list with bits and bytes and converts it to a list of bytes
-%% Should be applied as the last step at encode of a complete ASN.1 type
-%%
-
--ifdef(nodriver).
-
-complete(L) ->
- erlang_complete(L).
-
--else.
-
-%% asn1-1.7
-complete(L) ->
- case asn1rt_nif:encode_per_complete(L) of
- {error, Reason} -> handle_error(Reason, L);
- Else when is_binary(Else) -> Else
- end.
-
-handle_error([],_)->
- exit({error,{asn1,{"memory allocation problem in driver"}}});
-handle_error($1,L) -> % error in complete in driver
- exit({error,{asn1,L}});
-handle_error(ErrL,L) ->
- exit({error,{asn1,ErrL,L}}).
-
--endif.
-
-
-octets_to_complete(Len,Val) when Len < 256 ->
- [20,Len,Val];
-octets_to_complete(Len,Val) ->
- [21,<<Len:16>>,Val].
-
-octets_unused_to_complete(Unused,Len,Val) when Len < 256 ->
- [30,Unused,Len,Val];
-octets_unused_to_complete(Unused,Len,Val) ->
- [31,Unused,<<Len:16>>,Val].
diff --git a/lib/asn1/src/asn1rt_uper_bin.erl b/lib/asn1/src/asn1rt_uper_bin.erl
deleted file mode 100644
index abe178a69e..0000000000
--- a/lib/asn1/src/asn1rt_uper_bin.erl
+++ /dev/null
@@ -1,1618 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
--module(asn1rt_uper_bin).
-
-%% encoding / decoding of PER unaligned
-
--include("asn1_records.hrl").
-
-%%-compile(export_all).
-
- -export([cindex/3, list_to_record/2]).
- -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,
- 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,
- encode_object_identifier/1, decode_object_identifier/1,
- encode_real/1, decode_real/1,
- complete/1, complete_NFP/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
- ]).
-
--define('16K',16384).
--define('32K',32768).
--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
-%%
-
-setext(false) ->
- <<0:1>>;
-setext(true) ->
- <<1:1>>.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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>>};
-
-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_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 -> [];
- ExtBits ->
- [encode_small_length(ExtNum),<<ExtBits:ExtNum>>]
- 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}).
-
-
-%%%%%%%%%%%%%%%
-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_bitstring(Bytes) ->
- <<BS:Num/bitstring,Rest/bitstring>> = Bytes,
- {BS,Rest}.
-
-getbits_as_list(Num,Bytes) when is_bitstring(Bytes) ->
- <<BitStr:Num/bitstring,Rest/bitstring>> = Bytes,
- {[ B || <<B:1>> <= BitStr],Rest}.
-
-getbit(Buffer) ->
- <<B:1,Rest/bitstring>> = Buffer,
- {B,Rest}.
-
-
-getbits(Buffer,Num) when is_bitstring(Buffer) ->
- <<Bs:Num,Rest/bitstring>> = Buffer,
- {Bs,Rest}.
-
-
-
-%% Pick the first Num octets.
-%% Returns octets as an integer with bit significance as in buffer.
-getoctets(Buffer,Num) when is_bitstring(Buffer) ->
- <<Val:Num/integer-unit:8,RestBitStr/bitstring>> = Buffer,
- {Val,RestBitStr}.
-
-%% Pick the first Num octets.
-%% Returns octets as a binary
-getoctets_as_bin(Bin,Num) when is_bitstring(Bin) ->
- <<Octets:Num/binary,RestBin/bitstring>> = Bin,
- {Octets,RestBin}.
-
-%% 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 ->
- [<<0:1>>, % the value is in the root set
- encode_integer([{'ValueRange',{0,Len1-1}}],N)];
- N when is_integer(N) ->
- <<0:1>>; % no encoding if only 0 or 1 alternative
- false ->
- [<<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(Buffer,C) ->
- decode_fragmented_bits(Buffer,C,[]).
-decode_fragmented_bits(<<3:2,Len:6,BitStr/bitstring>>,C,Acc) ->
-%% {Value,Bin2} = split_binary(Bin, Len * ?'16K'),
- FragLen = (Len*?'16K') div 8,
- <<Value:FragLen/binary,BitStr2/bitstring>> = BitStr,
- decode_fragmented_bits(BitStr2,C,[Value|Acc]);
-decode_fragmented_bits(<<0:1,0:7,BitStr/bitstring>>,C,Acc) ->
- BinBits = list_to_binary(lists:reverse(Acc)),
- case C of
- Int when is_integer(Int),C == size(BinBits) ->
- {BinBits,BitStr};
- Int when is_integer(Int) ->
- exit({error,{asn1,{illegal_value,C,BinBits}}})
- end;
-decode_fragmented_bits(<<0:1,Len:7,BitStr/bitstring>>,C,Acc) ->
- <<Val:Len/bitstring,Rest/bitstring>> = BitStr,
-%% <<Value:Len/binary-unit:1,Bin2/binary>> = Bin,
- ResBitStr = list_to_bitstring(lists:reverse([Val|Acc])),
- case C of
- Int when is_integer(Int),C == bit_size(ResBitStr) ->
- {ResBitStr,Rest};
- Int when is_integer(Int) ->
- 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
-%% 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) ->
- encode_open_type(C, list_to_binary(Val));
-encode_open_type(_C, Val) when is_binary(Val) ->
- [encode_length(undefined,size(Val)),Val].
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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,_}}} ->
- [<<1:1>>,encode_unconstrained_number(Val)];
- Encoded ->
- [<<0:1>>,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]); %% 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
-encode_small_number({Name,Val}) when is_atom(Name) ->
- encode_small_number(Val);
-encode_small_number(Val) when Val =< 63 ->
- <<Val:7>>;
-encode_small_number(Val) ->
- [<<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) ->
- %% encoding in minimum no of octets preceeded by a length
- Val2 = Val - Lb,
-%% NumBits = num_bits(Val2),
- Bin = eint_bin_positive(Val2),
- Size = size(Bin),
- if
- Size < 128 ->
- [<<Size>>,Bin]; % equiv with encode_length(undefined,Len) but faster
- Size < 16384 ->
- [<<2:2,Size:14>>,Bin];
- true ->
- [encode_length(undefined,Size),Bin]
- 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,
- NumBits = num_bits(Range),
- <<Val2:NumBits>>;
-encode_constrained_number(Range,Val) ->
- exit({error,{asn1,{integer_range,Range,value,Val}}}).
-
-
-decode_constrained_number(Buffer,{Lb,Ub}) ->
- Range = Ub - Lb + 1,
- NumBits = num_bits(Range),
- {Val,Remain} = getbits(Buffer,NumBits),
- {Val+Lb,Remain}.
-
-%% X.691:10.8 Encoding of an unconstrained whole number
-
-encode_unconstrained_number(Val) when Val >= 0 ->
- Oct = eint_bin_2Cs(Val),
- Len = size(Oct),
- if
- Len < 128 ->
- [<<Len>>,Oct]; % equiv with encode_length(undefined,Len) but faster
- Len < 16384 ->
- [<<2:2,Len:14>>,Oct];
- true ->
- [encode_length(undefined,Len),<<Len:16>>,Oct]
- end;
-encode_unconstrained_number(Val) -> % negative
- Oct = enint(Val,[]),
- Len = size(Oct),
- if
- Len < 128 ->
- [<<Len>>,Oct]; % equiv with encode_length(undefined,Len) but faster
- Len < 16384 ->
- [<<2:2,Len:14>>,Oct];
- true ->
- [encode_length(undefined,Len),Oct]
- end.
-
-
-eint_bin_2Cs(Int) ->
- case eint_bin_positive(Int) of
- Bin = <<B,_/binary>> when B > 16#7f ->
- <<0,Bin/binary>>;
- Bin -> Bin
- end.
-
-%% returns the integer as a binary
-eint_bin_positive(Val) when Val < 16#100 ->
- <<Val>>;
-eint_bin_positive(Val) when Val < 16#10000 ->
- <<Val:16>>;
-eint_bin_positive(Val) when Val < 16#1000000 ->
- <<Val:24>>;
-eint_bin_positive(Val) when Val < 16#100000000 ->
- <<Val:32>>;
-eint_bin_positive(Val) ->
- list_to_binary([eint_bin_positive2(Val bsr 32)|<<Val:32>>]).
-eint_bin_positive2(Val) when Val < 16#100 ->
- <<Val>>;
-eint_bin_positive2(Val) when Val < 16#10000 ->
- <<Val:16>>;
-eint_bin_positive2(Val) when Val < 16#1000000 ->
- <<Val:24>>;
-eint_bin_positive2(Val) when Val < 16#100000000 ->
- <<Val:32>>;
-eint_bin_positive2(Val) ->
- [eint_bin_positive2(Val bsr 32)|<<Val:32>>].
-
-
-
-
-enint(-1, [B1|T]) when B1 > 127 ->
- list_to_binary([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_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
-%% 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 ->
- <<Len>>;
- Len < 16384 ->
- <<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
- [<<0:1>>,encode_constrained_number(Vr,Len)];
-encode_length({{Lb,_Ub},Ext},Len) when is_list(Ext) ->
- [<<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 ->
- <<(Len-1):7>>;
-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};
-decode_length(<<2:2,Val:14,Rest/bitstring>>,undefined) ->
- {Val,Rest};
-decode_length(<<3:2,_:14,_Rest/bitstring>>,undefined) ->
- exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
-
-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) ->
- {0,Buffer2} = getbit(Buffer),
- decode_length(Buffer2, VR);
-
-
-%When does this case occur with {_,_Lb,Ub} ??
-% X.691:10.9.3.5
-decode_length(Bin,{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535
- case Bin of
- <<0:1,Val:7,Rest/bitstring>> ->
- {Val,Rest};
- <<2:2,Val:14,Rest/bitstring>> ->
- {Val,Rest};
- <<3:2,_:14,_Rest/bitstring>> ->
- exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}})
- end;
-decode_length(Buffer,SingleValue) when is_integer(SingleValue) ->
- {SingleValue,Buffer}.
-
-
- % X.691:11
-encode_boolean(true) ->
- <<1:1>>;
-encode_boolean(false) ->
- <<0:1>>;
-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(get_constraint(C,'SizeConstraint'),Bin,NamedBitList);
-
-encode_bit_string(C, BitListVal, NamedBitList) ->
- encode_bit_string1(get_constraint(C,'SizeConstraint'), BitListVal, NamedBitList).
-%% when the value is a list of named bits
-encode_bit_string1(C, LoNB=[FirstVal | _RestVal], NamedBitList) when is_atom(FirstVal) ->
- ToSetPos = get_all_bitposes(LoNB, NamedBitList, []),
- BitList = make_and_set_list(ToSetPos,0),
- encode_bit_string1(C,BitList,NamedBitList);
-
-encode_bit_string1(C, BL=[{bit,_No} | _RestVal], NamedBitList) ->
- ToSetPos = get_all_bitposes(BL, NamedBitList, []),
- BitList = make_and_set_list(ToSetPos,0),
- encode_bit_string1(C,BitList,NamedBitList);
-%% when the value is a list of ones and zeroes
-encode_bit_string1(Int, BitListValue, _)
- when is_list(BitListValue),is_integer(Int) ->
- %% The type is constrained by a single value size constraint
- bit_list2bitstr(Int,BitListValue);
-encode_bit_string1(no, BitListValue,[])
- when is_list(BitListValue) ->
- Len = length(BitListValue),
- [encode_length(undefined,Len),bit_list2bitstr(Len,BitListValue)];
-encode_bit_string1(C, BitListValue,[])
- when is_list(BitListValue) ->
- Len = length(BitListValue),
- [encode_length(C,Len),bit_list2bitstr(Len,BitListValue)];
-encode_bit_string1(no, BitListValue,_NamedBitList)
- when is_list(BitListValue) ->
- %% this case with an unconstrained BIT STRING can be made more efficient
- %% if the complete driver can take a special code so the length field
- %% is encoded there.
- NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
- lists:reverse(BitListValue))),
- Len = length(NewBitLVal),
- [encode_length(undefined,Len),bit_list2bitstr(Len,NewBitLVal)];
-encode_bit_string1(C,BitListValue,_NamedBitList)
- when is_list(BitListValue) ->% C = {_,'MAX'}
- NewBitStr = bitstr_trailing_zeros(BitListValue,C),
- [encode_length(C,bit_size(NewBitStr)),NewBitStr];
-
-
-%% when the value is an integer
-encode_bit_string1(C, IntegerVal, NamedBitList) when is_integer(IntegerVal)->
- BitList = int_to_bitlist(IntegerVal),
- encode_bit_string1(C,BitList,NamedBitList);
-
-%% when the value is a tuple
-encode_bit_string1(C,{Name,Val}, NamedBitList) when is_atom(Name) ->
- encode_bit_string1(C,Val,NamedBitList).
-
-bit_list2bitstr(Len,BitListValue) ->
- case length(BitListValue) of
- Len ->
- << <<B:1>> ||B <- BitListValue>>;
- L when L > Len -> % truncate
- << << <<B:1>> ||B <- BitListValue>> :Len/bitstring>>;
- L -> % Len > L -> pad
- << << <<B:1>> ||B <- BitListValue>>/bitstring ,0:(Len-L)>>
- end.
-
-adjust_trailing_zeros(Len,Bin) when Len == bit_size(Bin) ->
- Bin;
-adjust_trailing_zeros(Len,Bin) when Len > bit_size(Bin) ->
- <<Bin/bitstring,0:(Len-bit_size(Bin))>>;
-adjust_trailing_zeros(Len,Bin) ->
- <<Bin:Len/bitstring>>.
-
-bitstr_trailing_zeros(BitList,C) when is_integer(C) ->
- bitstr_trailing_zeros1(BitList,C,C);
-bitstr_trailing_zeros(BitList,{Lb,Ub}) when is_integer(Lb) ->
- bitstr_trailing_zeros1(BitList,Lb,Ub);
-bitstr_trailing_zeros(BitList,{{Lb,Ub},_}) when is_integer(Lb) ->
- bitstr_trailing_zeros1(BitList,Lb,Ub);
-bitstr_trailing_zeros(BitList,_) ->
- bit_list2bitstr(length(BitList),BitList).
-
-bitstr_trailing_zeros1(BitList,Lb,Ub) ->
- case length(BitList) of
- Lb -> bit_list2bitstr(Lb,BitList);
- B when B<Lb -> bit_list2bitstr(Lb,BitList);
- D -> F = fun(L,LB,LB,_,_)->bit_list2bitstr(LB,lists:reverse(L));
- ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun);
- (L,L1,_,UB,_)when L1 =< UB ->
- bit_list2bitstr(L1,lists:reverse(L));
- (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING,
- BitList}}) end,
- F(lists:reverse(BitList),D,Lb,Ub,F)
- end.
-
-%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
-%% Unused = integer(),i.e. number unused bits in least sign. byte of
-%% BinBits = binary().
-encode_bin_bit_string(C,{_,BinBits},_NamedBitList)
- when is_integer(C),C=<16 ->
- adjust_trailing_zeros(C,BinBits);
-encode_bin_bit_string(C,{_Unused,BinBits},_NamedBitList)
- when is_integer(C) ->
- adjust_trailing_zeros(C,BinBits);
-encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) ->
- %% removes all trailing bits if NamedBitList is not empty
- BitStr = remove_trailing_bin(NamedBitList,UnusedAndBin),
- case C of
- {Lb,Ub} when is_integer(Lb),is_integer(Ub) ->
- [encode_length({Lb,Ub},bit_size(BitStr)),BitStr];
- no ->
- [encode_length(undefined,bit_size(BitStr)),BitStr];
- Sc ->
- [encode_length(Sc,bit_size(BitStr)),BitStr]
- end.
-
-
-remove_trailing_bin([], {Unused,Bin}) ->
- BS = bit_size(Bin)-Unused,
- <<BitStr:BS/bitstring,_:Unused>> = Bin,
- BitStr;
-remove_trailing_bin(_NamedNumberList,{_Unused,<<>>}) ->
- <<>>;
-remove_trailing_bin(NamedNumberList, {_Unused,Bin}) ->
- 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});
- _ ->
- BS = bit_size(Bin) - Unused2,
- <<BitStr:BS/bitstring,_:Unused2>> = Bin,
- BitStr
- end.
-
-trailingZeroesInNibble(0) ->
- 4;
-trailingZeroesInNibble(1) ->
- 0;
-trailingZeroesInNibble(2) ->
- 1;
-trailingZeroesInNibble(3) ->
- 0;
-trailingZeroesInNibble(4) ->
- 2;
-trailingZeroesInNibble(5) ->
- 0;
-trailingZeroesInNibble(6) ->
- 1;
-trailingZeroesInNibble(7) ->
- 0;
-trailingZeroesInNibble(8) ->
- 3;
-trailingZeroesInNibble(9) ->
- 0;
-trailingZeroesInNibble(10) ->
- 1;
-trailingZeroesInNibble(11) ->
- 0;
-trailingZeroesInNibble(12) -> %#1100
- 2;
-trailingZeroesInNibble(13) ->
- 0;
-trailingZeroesInNibble(14) ->
- 1;
-trailingZeroesInNibble(15) ->
- 0.
-
-%%%%%%%%%%%%%%%
-%% The result is presented as a list of named bits (if possible)
-%% else as a tuple {Unused,Bits}. Unused is the number of unused
-%% bits, least significant bits in the last byte of Bits. Bits is
-%% the BIT STRING represented as a binary.
-%%
-decode_compact_bit_string(Buffer, C, NamedNumberList) ->
- case get_constraint(C,'SizeConstraint') of
- 0 -> % fixed length
- {{8,0},Buffer};
- V when 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
- compact_bit_string(Buffer,V,NamedNumberList);
- V when is_integer(V) -> % V > 65536 => fragmented value
- {Bin,Buffer2} = decode_fragmented_bits(Buffer,V),
- PadLen = (8 - (bit_size(Bin) rem 8)) rem 8,
- {{PadLen,<<Bin/bitstring,0:PadLen>>},Buffer2};
-%% {0,_} -> {{0,Bin},Buffer2};
-%% {U,_} -> {{8-U,Bin},Buffer2}
- {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}),
- compact_bit_string(Bytes2,Len,NamedNumberList);
- no ->
- %% This case may demand decoding of fragmented length/value
- {Len,Bytes2} = decode_length(Buffer,undefined),
- compact_bit_string(Bytes2,Len,NamedNumberList);
- Sc ->
- {Len,Bytes2} = decode_length(Buffer,Sc),
- compact_bit_string(Bytes2,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}),
- bit_list_or_named(Bytes2,Len,NamedNumberList);
- no ->
- {Len,Bytes2} = decode_length(Buffer,undefined),
- bit_list_or_named(Bytes2,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 ->
- bit_list_or_named(Buffer,V,NamedNumberList);
- V when is_integer(V) ->
- {BinBits,_} = decode_fragmented_bits(Buffer,V),
- bit_list_or_named(BinBits,V,NamedNumberList);
- Sc -> % extension marker
- {Len,Bytes2} = decode_length(Buffer,Sc),
- bit_list_or_named(Bytes2,Len,NamedNumberList)
- end.
-
-
-%% if no named bits are declared we will return a
-%% {Unused,Bits}. Unused = integer(),
-%% Bits = binary().
-compact_bit_string(Buffer,Len,[]) ->
- {BitStr,Rest} = getbits_as_binary(Len,Buffer), % {{Unused,BinBits},NewBuffer}
- PadLen = (8 - (bit_size(BitStr) rem 8)) rem 8,
- {{PadLen,<<BitStr/bitstring,0:PadLen>>},Rest};
-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([], _) ->
- [].
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% X.691:16
-%% encode_octet_string(Constraint,Val)
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-encode_octet_string(C,{_Name,Val}) ->
- encode_octet_string(C,Val);
-encode_octet_string(C,Val) ->
- case get_constraint(C,'SizeConstraint') of
- 0 ->
- <<>>;
- 1 ->
- 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)];
- Sv when is_list(Sv) ->
- [encode_length({hd(Sv),lists:max(Sv)},length(Val)),list_to_binary(Val)];
- no ->
- [encode_length(undefined,length(Val)),list_to_binary(Val)]
- 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).
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Restricted char string types
-%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
-%% X.691:26 and X.680:34-36
-%%encode_restricted_string('BMPString',Constraints,Extension,Val)
-
-
-encode_restricted_string({Name,Val}) when is_atom(Name) ->
- encode_restricted_string(Val);
-
-encode_restricted_string(Val) when is_list(Val)->
- [encode_length(undefined,length(Val)),list_to_binary(Val)].
-
-encode_known_multiplier_string(StringType,C,{Name,Val}) when is_atom(Name) ->
- encode_known_multiplier_string(StringType,C,Val);
-
-encode_known_multiplier_string(StringType,C,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
- Result;
- {Ub,Lb} ->
- [encode_length({Ub,Lb},length(Val)),Result];
- Vl when is_list(Vl) ->
- [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),Result];
- no ->
- [encode_length(undefined,length(Val)),Result]
- end.
-
-decode_restricted_string(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- getoctets_as_list(Bytes2,Len).
-
-decode_known_multiplier_string(Bytes,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
- chars_decode(Bytes,NumBits,StringType,C,Ub);
- 0 ->
- {[],Bytes};
- Vl when is_list(Vl) ->
- {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
- chars_decode(Bytes1,NumBits,StringType,C,Len);
- no ->
- {Len,Bytes1} = decode_length(Bytes,undefined),
- chars_decode(Bytes1,NumBits,StringType,C,Len);
- {Lb,Ub}->
- {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
- chars_decode(Bytes1,NumBits,StringType,C,Len)
- end.
-
-
-encode_NumericString(C,Val) ->
- encode_known_multiplier_string('NumericString',C,Val).
-decode_NumericString(Bytes,C) ->
- decode_known_multiplier_string(Bytes,'NumericString',C,false).
-
-encode_PrintableString(C,Val) ->
- encode_known_multiplier_string('PrintableString',C,Val).
-decode_PrintableString(Bytes,C) ->
- decode_known_multiplier_string(Bytes,'PrintableString',C,false).
-
-encode_VisibleString(C,Val) -> % equivalent with ISO646String
- encode_known_multiplier_string('VisibleString',C,Val).
-decode_VisibleString(Bytes,C) ->
- decode_known_multiplier_string(Bytes,'VisibleString',C,false).
-
-encode_IA5String(C,Val) ->
- encode_known_multiplier_string('IA5String',C,Val).
-decode_IA5String(Bytes,C) ->
- decode_known_multiplier_string(Bytes,'IA5String',C,false).
-
-encode_BMPString(C,Val) ->
- encode_known_multiplier_string('BMPString',C,Val).
-decode_BMPString(Bytes,C) ->
- decode_known_multiplier_string(Bytes,'BMPString',C,false).
-
-encode_UniversalString(C,Val) ->
- encode_known_multiplier_string('UniversalString',C,Val).
-decode_UniversalString(Bytes,C) ->
- decode_known_multiplier_string(Bytes,'UniversalString',C,false).
-
-
-%% end of known-multiplier strings for which PER visible constraints are
-%% applied
-
-encode_GeneralString(_C,Val) ->
- encode_restricted_string(Val).
-decode_GeneralString(Bytes,_C) ->
- decode_restricted_string(Bytes).
-
-encode_GraphicString(_C,Val) ->
- encode_restricted_string(Val).
-decode_GraphicString(Bytes,_C) ->
- decode_restricted_string(Bytes).
-
-encode_ObjectDescriptor(_C,Val) ->
- encode_restricted_string(Val).
-decode_ObjectDescriptor(Bytes) ->
- decode_restricted_string(Bytes).
-
-encode_TeletexString(_C,Val) -> % equivalent with T61String
- encode_restricted_string(Val).
-decode_TeletexString(Bytes,_C) ->
- decode_restricted_string(Bytes).
-
-encode_VideotexString(_C,Val) ->
- encode_restricted_string(Val).
-decode_VideotexString(Bytes,_C) ->
- decode_restricted_string(Bytes).
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% 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})];
- [<<(H-Min):NumBits>>|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})];
- Ch = exit_if_false(H,element(H-Min+1,Tab)),
- [<<Ch:NumBits>>|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 bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
- Ch = ((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,
- [<<Ch:NumBits>>|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,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})];
- Ch = exit_if_false({A,B,C,D},element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)),
- [<<Ch:NumBits>>|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));
- no ->
- case StringType of
- 'IA5String' ->
- charbits(128); % 16#00..16#7F
- 'VisibleString' ->
- charbits(95); % 16#20..16#7E
- 'PrintableString' ->
- charbits(74); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
- 'NumericString' ->
- charbits(11); % $ ,"0123456789"
- 'UniversalString' ->
- 32;
- 'BMPString' ->
- 16
- 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)].
-
-%% See Table 20.3 in Dubuisson
-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) ->
- {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)),Val];
-encode_UTF8String(Val) ->
- Bin = list_to_binary(Val),
- encode_UTF8String(Bin).
-
-decode_UTF8String(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- 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
-%% 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 -> [binary()|bitstring()|list()]
-%%
-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
- [encode_length(undefined,size(Octets)),Octets].
-
-%% This code is copied from asn1_encode.erl (BER) and corrected and modified
-
-e_object_identifier({'OBJECT IDENTIFIER',V}) ->
- e_object_identifier(V);
-e_object_identifier({Cname,V}) when 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].
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_relative_oid(Val) -> CompleteList
-%% decode_relative_oid({Name,Val}) -> CompleteList
-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),EncVal].
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% decode_real(Val) -> {REALvalue,Rest}
-%% decode_real({Name,Val}) -> {REALvalue,Rest}
-decode_real(Bytes) ->
- {Len,Bytes2} = decode_length(Bytes,undefined),
- <<Bytes3:Len/binary,Rest/bitstring>> = Bytes2,
- {RealVal,Rest,Len} = ?RT_COMMON:decode_real(Bytes3,Len),
- {RealVal,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(InList) when is_list(InList) ->
- case complete1(InList) of
- <<>> ->
- <<0>>;
- Res ->
- case bit_size(Res) band 7 of
- 0 -> Res;
- Bits -> <<Res/bitstring,0:(8-Bits)>>
- end
- end;
-complete(InList) when is_binary(InList) ->
- InList;
-complete(InList) when is_bitstring(InList) ->
- PadLen = 8 - (bit_size(InList) band 7),
- <<InList/bitstring,0:PadLen>>.
-
-complete1(L) when is_list(L) ->
- list_to_bitstring(L).
-
-%% Special version of complete that does not align the completed message.
-complete_NFP(InList) when is_list(InList) ->
- list_to_bitstring(InList);
-complete_NFP(InList) when is_bitstring(InList) ->
- InList.
-
-%% unaligned helpers
-
-%% 10.5.6 NOTE: If "range" satisfies the inequality 2^m < "range" =<
-%% 2^(m+1) then the number of bits = m + 1
-
-num_bits(N) ->
- num_bits(N,1,0).
-num_bits(N,T,B) when N=<T->B;
-num_bits(N,T,B) ->num_bits(N,T bsl 1, B+1).
diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl
new file mode 100644
index 0000000000..5fbf116747
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_ber.erl
@@ -0,0 +1,1561 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(asn1rtt_ber).
+
+%% encoding / decoding of BER
+
+-export([ber_decode_nif/1,ber_decode_erlang/1,match_tags/2,ber_encode/1]).
+-export([encode_tags/2,
+ encode_tags/3,
+ skip_ExtensionAdditions/2]).
+-export([encode_boolean/2,decode_boolean/2,
+ encode_integer/2,encode_integer/3,
+ decode_integer/3,decode_integer/4,
+ encode_enumerated/2,decode_enumerated/3,
+ encode_bit_string/4,
+ decode_named_bit_string/3,
+ decode_compact_bit_string/3,
+ decode_legacy_bit_string/3,
+ decode_native_bit_string/3,
+ encode_null/2,decode_null/2,
+ encode_relative_oid/2,decode_relative_oid/2,
+ encode_object_identifier/2,decode_object_identifier/2,
+ encode_restricted_string/2,
+ decode_restricted_string/2,decode_restricted_string/3,
+ encode_universal_string/2,decode_universal_string/3,
+ encode_UTF8_string/2,decode_UTF8_string/2,
+ encode_BMP_string/2,decode_BMP_string/3,
+ encode_generalized_time/2,decode_generalized_time/3,
+ encode_utc_time/2,decode_utc_time/3]).
+
+-export([encode_open_type/2,decode_open_type/2,
+ decode_open_type_as_binary/2]).
+
+-export([decode_primitive_incomplete/2,decode_selective/2]).
+
+%% For DER.
+-export([dynamicsort_SET_components/1,dynamicsort_SETOF/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).
+
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_BMPString, 30).
+
+
+% the complete tag-word of built-in types
+-define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
+-define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
+-define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
+-define(T_OCTET_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 4). % can be CONSTRUCTED
+-define(T_NULL, ?UNIVERSAL bor ?PRIMITIVE bor 5).
+-define(T_OBJECT_IDENTIFIER,?UNIVERSAL bor ?PRIMITIVE bor 6).
+-define(T_OBJECT_DESCRIPTOR,?UNIVERSAL bor ?PRIMITIVE bor 7).
+-define(T_EXTERNAL, ?UNIVERSAL bor ?PRIMITIVE bor 8).
+-define(T_REAL, ?UNIVERSAL bor ?PRIMITIVE bor 9).
+-define(T_ENUMERATED, ?UNIVERSAL bor ?PRIMITIVE bor 10).
+-define(T_EMBEDDED_PDV, ?UNIVERSAL bor ?PRIMITIVE bor 11).
+-define(T_SEQUENCE, ?UNIVERSAL bor ?CONSTRUCTED bor 16).
+-define(T_SET, ?UNIVERSAL bor ?CONSTRUCTED bor 17).
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_UTCTime, ?UNIVERSAL bor ?PRIMITIVE bor 23).
+-define(T_GeneralizedTime, ?UNIVERSAL bor ?PRIMITIVE bor 24).
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+-define(T_UniversalString, ?UNIVERSAL bor ?PRIMITIVE bor 28). %can be constructed
+-define(T_BMPString, ?UNIVERSAL bor ?PRIMITIVE bor 30). %can be constructed
+
+ber_encode([Tlv]) ->
+ ber_encode(Tlv);
+ber_encode(Tlv) when is_binary(Tlv) ->
+ Tlv;
+ber_encode(Tlv) ->
+ asn1rt_nif:encode_ber_tlv(Tlv).
+
+ber_decode_nif(B) ->
+ asn1rt_nif:decode_ber_tlv(B).
+
+ber_decode_erlang(B) when is_binary(B) ->
+ decode_primitive(B);
+ber_decode_erlang(Tlv) ->
+ {Tlv,<<>>}.
+
+decode_primitive(Bin) ->
+ {Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
+ case Form of
+ 1 -> % constructed
+ {{TagNo,decode_constructed(V)},Rest};
+ 0 -> % primitive
+ {{TagNo,V},Rest};
+ 2 -> % constructed indefinite
+ {Vlist,Rest2} = decode_constructed_indefinite(V,[]),
+ {{TagNo,Vlist},Rest2}
+ end.
+
+decode_constructed(Bin) when byte_size(Bin) =:= 0 ->
+ [];
+decode_constructed(Bin) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ [Tlv|decode_constructed(Rest)].
+
+decode_constructed_indefinite(<<0,0,Rest/binary>>,Acc) ->
+ {lists:reverse(Acc),Rest};
+decode_constructed_indefinite(Bin,Acc) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ decode_constructed_indefinite(Rest, [Tlv|Acc]).
+
+%% decode_primitive_incomplete/2 decodes an encoded message incomplete
+%% by help of the pattern attribute (first argument).
+decode_primitive_incomplete([[default,TagNo]],Bin) -> %default
+ case decode_tag_and_length(Bin) of
+ {Form,TagNo,V,Rest} ->
+ decode_incomplete2(Form,TagNo,V,[],Rest);
+ _ ->
+ %{asn1_DEFAULT,Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[default,TagNo,Directives]],Bin) -> %default, constructed type, Directives points into this type
+ case decode_tag_and_length(Bin) of
+ {Form,TagNo,V,Rest} ->
+ decode_incomplete2(Form,TagNo,V,Directives,Rest);
+ _ ->
+ %{asn1_DEFAULT,Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[opt,TagNo]],Bin) -> %optional
+ case decode_tag_and_length(Bin) of
+ {Form,TagNo,V,Rest} ->
+ decode_incomplete2(Form,TagNo,V,[],Rest);
+ _ ->
+ %{{TagNo,asn1_NOVALUE},Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) -> %optional
+ case decode_tag_and_length(Bin) of
+ {Form,TagNo,V,Rest} ->
+ decode_incomplete2(Form,TagNo,V,Directives,Rest);
+ _ ->
+ %{{TagNo,asn1_NOVALUE},Bin}
+ asn1_NOVALUE
+ end;
+%% An optional that shall be undecoded
+decode_primitive_incomplete([[opt_undec,Tag]],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_,Tag,_,_} ->
+ decode_incomplete_bin(Bin);
+ _ ->
+ asn1_NOVALUE
+ end;
+%% A choice alternative that shall be undecoded
+decode_primitive_incomplete([[alt_undec,TagNo]|RestAlts],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_,TagNo,_,_} ->
+ decode_incomplete_bin(Bin);
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt,TagNo]|RestAlts],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_Form,TagNo,V,Rest} ->
+ {{TagNo,V},Rest};
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt,TagNo,Directives]|RestAlts],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {Form,TagNo,V,Rest} ->
+ decode_incomplete2(Form,TagNo,V,Directives,Rest);
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt_parts,TagNo]],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_Form,TagNo,V,Rest} ->
+ {{TagNo,V},Rest};
+ _ ->
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_Form,TagNo,V,Rest} ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) -> %incomlete decode
+ decode_incomplete_bin(Bin);
+decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_Form,TagNo,V,Rest} ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ Err ->
+ {error,{asn1,"tag failure",TagNo,Err}}
+ end;
+decode_primitive_incomplete([mandatory|RestTag],Bin) ->
+ {Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
+ decode_incomplete2(Form,TagNo,V,RestTag,Rest);
+%% A choice that is a toptype or a mandatory component of a
+%% SEQUENCE or SET.
+decode_primitive_incomplete([[mandatory|Directives]],Bin) ->
+ {Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
+ decode_incomplete2(Form,TagNo,V,Directives,Rest);
+decode_primitive_incomplete([],Bin) ->
+ decode_primitive(Bin).
+
+%% decode_parts_incomplete/1 receives a number of values encoded in
+%% sequence and returns the parts as unencoded binaries
+decode_parts_incomplete(<<>>) ->
+ [];
+decode_parts_incomplete(Bin) ->
+ {ok,Rest} = skip_tag(Bin),
+ {ok,Rest2} = skip_length_and_value(Rest),
+ LenPart = byte_size(Bin) - byte_size(Rest2),
+ <<Part:LenPart/binary,RestBin/binary>> = Bin,
+ [Part|decode_parts_incomplete(RestBin)].
+
+
+%% decode_incomplete2 checks if V is a value of a constructed or
+%% primitive type, and continues the decode propeerly.
+decode_incomplete2(_Form=2,TagNo,V,TagMatch,_) ->
+ %% constructed indefinite length
+ {Vlist,Rest2} = decode_constr_indef_incomplete(TagMatch,V,[]),
+ {{TagNo,Vlist},Rest2};
+decode_incomplete2(1,TagNo,V,[TagMatch],Rest) when is_list(TagMatch) ->
+ {{TagNo,decode_constructed_incomplete(TagMatch,V)},Rest};
+decode_incomplete2(1,TagNo,V,TagMatch,Rest) ->
+ {{TagNo,decode_constructed_incomplete(TagMatch,V)},Rest};
+decode_incomplete2(0,TagNo,V,_TagMatch,Rest) ->
+ {{TagNo,V},Rest}.
+
+decode_constructed_incomplete([Tags=[Ts]],Bin) when is_list(Ts) ->
+ decode_constructed_incomplete(Tags,Bin);
+decode_constructed_incomplete(_TagMatch,<<>>) ->
+ [];
+decode_constructed_incomplete([mandatory|RestTag],Bin) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ [Tlv|decode_constructed_incomplete(RestTag,Rest)];
+decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
+ when Alt =:= alt_undec; Alt =:= alt; Alt =:= alt_parts ->
+ {_Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
+ case incomplete_choice_alt(TagNo, Directives) of
+ {alt_undec,_} ->
+ LenA = byte_size(Bin) - byte_size(Rest),
+ <<A:LenA/binary,Rest/binary>> = Bin,
+ A;
+ {alt,InnerDirectives} ->
+ {Tlv,Rest} = decode_primitive_incomplete(InnerDirectives,V),
+ {TagNo,Tlv};
+ {alt_parts,_} ->
+ [{TagNo,decode_parts_incomplete(V)}];
+ no_match -> %% if a choice alternative was encoded that
+ %% was not specified in the config file,
+ %% thus decode component anonomous.
+ {Tlv,_}=decode_primitive(Bin),
+ Tlv
+ end;
+decode_constructed_incomplete([TagNo|RestTag],Bin) ->
+ case decode_primitive_incomplete([TagNo],Bin) of
+ {Tlv,Rest} ->
+ [Tlv|decode_constructed_incomplete(RestTag,Rest)];
+ asn1_NOVALUE ->
+ decode_constructed_incomplete(RestTag,Bin)
+ end;
+decode_constructed_incomplete([],Bin) ->
+ {Tlv,Rest}=decode_primitive(Bin),
+ [Tlv|decode_constructed_incomplete([],Rest)].
+
+decode_constr_indef_incomplete(_TagMatch,<<0,0,Rest/binary>>,Acc) ->
+ {lists:reverse(Acc),Rest};
+decode_constr_indef_incomplete([Tag|RestTags],Bin,Acc) ->
+ case decode_primitive_incomplete([Tag],Bin) of
+ {Tlv,Rest} ->
+ decode_constr_indef_incomplete(RestTags,Rest,[Tlv|Acc]);
+ asn1_NOVALUE ->
+ decode_constr_indef_incomplete(RestTags,Bin,Acc)
+ end.
+
+
+decode_incomplete_bin(Bin) ->
+ {ok,Rest} = skip_tag(Bin),
+ {ok,Rest2} = skip_length_and_value(Rest),
+ IncLen = byte_size(Bin) - byte_size(Rest2),
+ <<IncBin:IncLen/binary,Ret/binary>> = Bin,
+ {IncBin,Ret}.
+
+incomplete_choice_alt(TagNo,[[Alt,TagNo]|Directives]) ->
+ {Alt,Directives};
+incomplete_choice_alt(TagNo,[D]) when is_list(D) ->
+ incomplete_choice_alt(TagNo,D);
+incomplete_choice_alt(TagNo,[_H|Directives]) ->
+ incomplete_choice_alt(TagNo,Directives);
+incomplete_choice_alt(_,[]) ->
+ no_match.
+
+
+%% decode_selective(Pattern, Binary) the first argument is a pattern that tells
+%% what to do with the next element the second is the BER encoded
+%% message as a binary
+%% Returns {ok,Value} or {error,Reason}
+%% Value is a binary that in turn must be decoded to get the decoded
+%% value.
+decode_selective([],Binary) ->
+ {ok,Binary};
+decode_selective([skip|RestPattern],Binary)->
+ {ok,RestBinary}=skip_tag(Binary),
+ {ok,RestBinary2}=skip_length_and_value(RestBinary),
+ decode_selective(RestPattern,RestBinary2);
+decode_selective([[skip_optional,Tag]|RestPattern],Binary) ->
+ case skip_optional_tag(Tag,Binary) of
+ {ok,RestBinary} ->
+ {ok,RestBinary2}=skip_length_and_value(RestBinary),
+ decode_selective(RestPattern,RestBinary2);
+ missing ->
+ decode_selective(RestPattern,Binary)
+ end;
+decode_selective([[choosen,Tag]],Binary) ->
+ return_value(Tag,Binary);
+decode_selective([[choosen,Tag]|RestPattern],Binary) ->
+ case skip_optional_tag(Tag,Binary) of
+ {ok,RestBinary} ->
+ {ok,Value} = get_value(RestBinary),
+ decode_selective(RestPattern,Value);
+ missing ->
+ {ok,<<>>}
+ end;
+decode_selective(P,_) ->
+ {error,{asn1,{partial_decode,"bad pattern",P}}}.
+
+return_value(Tag,Binary) ->
+ {ok,{Tag,RestBinary}}=get_tag(Binary),
+ {ok,{LenVal,_RestBinary2}} = get_length_and_value(RestBinary),
+ {ok,<<Tag/binary,LenVal/binary>>}.
+
+
+%% skip_tag and skip_length_and_value are rutines used both by
+%% decode_partial_incomplete and decode_selective (decode/2).
+
+skip_tag(<<_:3,31:5,Rest/binary>>)->
+ skip_long_tag(Rest);
+skip_tag(<<_:3,_Tag:5,Rest/binary>>) ->
+ {ok,Rest}.
+
+skip_long_tag(<<1:1,_:7,Rest/binary>>) ->
+ skip_long_tag(Rest);
+skip_long_tag(<<0:1,_:7,Rest/binary>>) ->
+ {ok,Rest}.
+
+skip_optional_tag(<<>>,Binary) ->
+ {ok,Binary};
+skip_optional_tag(<<Tag,RestTag/binary>>,<<Tag,Rest/binary>>) ->
+ skip_optional_tag(RestTag,Rest);
+skip_optional_tag(_,_) ->
+ missing.
+
+
+skip_length_and_value(Binary) ->
+ case decode_length(Binary) of
+ {indefinite,RestBinary} ->
+ skip_indefinite_value(RestBinary);
+ {Length,RestBinary} ->
+ <<_:Length/unit:8,Rest/binary>> = RestBinary,
+ {ok,Rest}
+ end.
+
+skip_indefinite_value(<<0,0,Rest/binary>>) ->
+ {ok,Rest};
+skip_indefinite_value(Binary) ->
+ {ok,RestBinary}=skip_tag(Binary),
+ {ok,RestBinary2} = skip_length_and_value(RestBinary),
+ skip_indefinite_value(RestBinary2).
+
+get_value(Binary) ->
+ case decode_length(Binary) of
+ {indefinite,RestBinary} ->
+ get_indefinite_value(RestBinary,[]);
+ {Length,RestBinary} ->
+ <<Value:Length/binary,_Rest/binary>> = RestBinary,
+ {ok,Value}
+ end.
+
+get_indefinite_value(<<0,0,_Rest/binary>>,Acc) ->
+ {ok,list_to_binary(lists:reverse(Acc))};
+get_indefinite_value(Binary,Acc) ->
+ {ok,{Tag,RestBinary}}=get_tag(Binary),
+ {ok,{LenVal,RestBinary2}} = get_length_and_value(RestBinary),
+ get_indefinite_value(RestBinary2,[LenVal,Tag|Acc]).
+
+get_tag(<<H:1/binary,Rest/binary>>) ->
+ case H of
+ <<_:3,31:5>> ->
+ get_long_tag(Rest,[H]);
+ _ -> {ok,{H,Rest}}
+ end.
+get_long_tag(<<H:1/binary,Rest/binary>>,Acc) ->
+ case H of
+ <<0:1,_:7>> ->
+ {ok,{list_to_binary(lists:reverse([H|Acc])),Rest}};
+ _ ->
+ get_long_tag(Rest,[H|Acc])
+ end.
+
+get_length_and_value(Bin = <<0:1,Length:7,_T/binary>>) ->
+ <<Len,Val:Length/binary,Rest/binary>> = Bin,
+ {ok,{<<Len,Val/binary>>, Rest}};
+get_length_and_value(Bin = <<1:1,0:7,_T/binary>>) ->
+ get_indefinite_length_and_value(Bin);
+get_length_and_value(<<1:1,LL:7,T/binary>>) ->
+ <<Length:LL/unit:8,Rest/binary>> = T,
+ <<Value:Length/binary,Rest2/binary>> = Rest,
+ {ok,{<<1:1,LL:7,Length:LL/unit:8,Value/binary>>,Rest2}}.
+
+get_indefinite_length_and_value(<<H,T/binary>>) ->
+ get_indefinite_length_and_value(T,[H]).
+
+get_indefinite_length_and_value(<<0,0,Rest/binary>>,Acc) ->
+ {ok,{list_to_binary(lists:reverse(Acc)),Rest}};
+get_indefinite_length_and_value(Binary,Acc) ->
+ {ok,{Tag,RestBinary}}=get_tag(Binary),
+ {ok,{LenVal,RestBinary2}}=get_length_and_value(RestBinary),
+ get_indefinite_length_and_value(RestBinary2,[LenVal,Tag|Acc]).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% match_tags takes a Tlv (Tag, Length, Value) structure and matches
+%% it with the tags in TagList. If the tags does not match the function
+%% crashes otherwise it returns the remaining Tlv after that the tags have
+%% been removed.
+%%
+%% match_tags(Tlv, TagList)
+%%
+
+match_tags({T,V}, [T]) ->
+ V;
+match_tags({T,V}, [T|Tt]) ->
+ match_tags(V,Tt);
+match_tags([{T,V}], [T|Tt]) ->
+ match_tags(V, Tt);
+match_tags([{T,_V}|_]=Vlist, [T]) ->
+ Vlist;
+match_tags(Tlv, []) ->
+ Tlv;
+match_tags({Tag,_V}=Tlv, [T|_Tt]) ->
+ exit({error,{asn1,{wrong_tag,{{expected,T},{got,Tag,Tlv}}}}}).
+
+%%%
+%% skips components that do not match a tag in Tags
+skip_ExtensionAdditions([], _Tags) ->
+ [];
+skip_ExtensionAdditions([{Tag,_}|Rest]=TLV, Tags) ->
+ case [X || X=T <- Tags, T =:= Tag] of
+ [] ->
+ %% skip this TLV and continue with next
+ skip_ExtensionAdditions(Rest,Tags);
+ _ ->
+ TLV
+ end.
+
+
+%%===============================================================================
+%% Decode a tag
+%%
+%% decode_tag(OctetListBuffer) -> {{Form, (Class bsl 16)+ TagNo}, RestOfBuffer, RemovedBytes}
+%%===============================================================================
+
+decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 0:1, Length:7, V:Length/binary, RestBuffer/binary>>) when TagNo < 31 ->
+ {Form, (Class bsl 16) bor TagNo, V, RestBuffer};
+decode_tag_and_length(<<Class:2, 1:1, TagNo:5, 1:1, 0:7, T/binary>>) when TagNo < 31 ->
+ {2, (Class bsl 16) + TagNo, T, <<>>};
+decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 1:1, LL:7, Length:LL/unit:8,V:Length/binary, T/binary>>) when TagNo < 31 ->
+ {Form, (Class bsl 16) bor TagNo, V, T};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 0:1, Length:7, V:Length/binary, RestBuffer/binary>>) ->
+ {Form, (Class bsl 16) bor TagNo, V, RestBuffer};
+decode_tag_and_length(<<Class:2, 1:1, 31:5, 0:1, TagNo:7, 1:1, 0:7, T/binary>>) ->
+ {2, (Class bsl 16) bor TagNo, T, <<>>};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 1:1, LL:7, Length:LL/unit:8, V:Length/binary, T/binary>>) ->
+ {Form, (Class bsl 16) bor TagNo, V, T};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, 1:1, TagPart1:7, 0:1, TagPartLast, Buffer/binary>>) ->
+ TagNo = (TagPart1 bsl 7) bor TagPartLast,
+ {Length, RestBuffer} = decode_length(Buffer),
+ << V:Length/binary, RestBuffer2/binary>> = RestBuffer,
+ {Form, (Class bsl 16) bor TagNo, V, RestBuffer2};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, Buffer/binary>>) ->
+ {TagNo, Buffer1} = decode_tag(Buffer, 0),
+ {Length, RestBuffer} = decode_length(Buffer1),
+ << V:Length/binary, RestBuffer2/binary>> = RestBuffer,
+ {Form, (Class bsl 16) bor TagNo, V, RestBuffer2}.
+
+
+
+%% last partial tag
+decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck) ->
+ TagNo = (TagAck bsl 7) bor PartialTag,
+ {TagNo, Buffer};
+% more tags
+decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck) ->
+ TagAck1 = (TagAck bsl 7) bor PartialTag,
+ decode_tag(Buffer, TagAck1).
+
+%%=======================================================================
+%%
+%% Encode all tags in the list Tags and return a possibly deep list of
+%% bytes with tag and length encoded
+%% The taglist must be in reverse order (fixed by the asn1 compiler)
+%% e.g [T1,T2] will result in
+%% {[EncodedT2,EncodedT1|BytesSoFar],LenSoFar+LenT2+LenT1}
+%%
+
+encode_tags([Tag|Trest], BytesSoFar, LenSoFar) ->
+ {Bytes2,L2} = encode_length(LenSoFar),
+ encode_tags(Trest, [Tag,Bytes2|BytesSoFar],
+ LenSoFar + byte_size(Tag) + L2);
+encode_tags([], BytesSoFar, LenSoFar) ->
+ {BytesSoFar,LenSoFar}.
+
+encode_tags(TagIn, {BytesSoFar,LenSoFar}) ->
+ encode_tags(TagIn, BytesSoFar, LenSoFar).
+
+%%===============================================================================
+%%
+%% This comment is valid for all the encode/decode functions
+%%
+%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
+%% used for PER-coding but not for BER-coding.
+%%
+%% Val = Value. If Val is an atom then it is a symbolic integer value
+%% (i.e the atom must be one of the names in the NamedNumberList).
+%% The NamedNumberList is used to translate the atom to an integer value
+%% before encoding.
+%%
+%%===============================================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Value) -> io_list (i.e nested list with integers, binaries)
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+
+encode_open_type(Val, T) when is_list(Val) ->
+ encode_open_type(list_to_binary(Val), T);
+encode_open_type(Val, []) ->
+ {Val,byte_size(Val)};
+encode_open_type(Val, Tag) ->
+ encode_tags(Tag, Val, byte_size(Val)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Tlv, TagIn) -> Value
+%% Tlv = {Tag,V} | V where V -> binary()
+%% TagIn = [TagVal] where TagVal -> int()
+%% Value = binary with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Tlv, TagIn) ->
+ case match_tags(Tlv, TagIn) of
+ Bin when is_binary(Bin) ->
+ {InnerTlv,_} = ber_decode_nif(Bin),
+ InnerTlv;
+ TlvBytes -> TlvBytes
+ end.
+
+decode_open_type_as_binary(Tlv, TagIn)->
+ ber_encode(match_tags(Tlv, TagIn)).
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Boolean, ITU_T X.690 Chapter 8.2
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode_boolean(Integer, ReversedTagList) -> {[Octet],Len}
+%%===============================================================================
+
+encode_boolean(true, TagIn) ->
+ encode_tags(TagIn, [16#FF],1);
+encode_boolean(false, TagIn) ->
+ encode_tags(TagIn, [0],1);
+encode_boolean(X,_) ->
+ exit({error,{asn1, {encode_boolean, X}}}).
+
+
+%%===============================================================================
+%% decode_boolean(BuffList, HasTag, TotalLen) -> {true, Remain, RemovedBytes} |
+%% {false, Remain, RemovedBytes}
+%%===============================================================================
+decode_boolean(Tlv,TagIn) ->
+ Val = match_tags(Tlv, TagIn),
+ case Val of
+ <<0:8>> ->
+ false;
+ <<_:8>> ->
+ true;
+ _ ->
+ exit({error,{asn1, {decode_boolean, Val}}})
+ end.
+
+
+%%===========================================================================
+%% Integer, ITU_T X.690 Chapter 8.3
+
+%% encode_integer(Constraint, Value, Tag) -> [octet list]
+%% encode_integer(Constraint, Name, NamedNumberList, Tag) -> [octet list]
+%% Value = INTEGER | {Name,INTEGER}
+%% Tag = tag | notag
+%%===========================================================================
+
+encode_integer(Val, Tag) when is_integer(Val) ->
+ encode_tags(Tag, encode_integer(Val));
+encode_integer(Val, _Tag) ->
+ exit({error,{asn1,{encode_integer,Val}}}).
+
+
+encode_integer(Val, NamedNumberList, Tag) when is_atom(Val) ->
+ case lists:keyfind(Val, 1, NamedNumberList) of
+ {_, NewVal} ->
+ encode_tags(Tag, encode_integer(NewVal));
+ _ ->
+ exit({error,{asn1, {encode_integer_namednumber, Val}}})
+ end;
+encode_integer(Val, _NamedNumberList, Tag) ->
+ encode_tags(Tag, encode_integer(Val)).
+
+encode_integer(Val) ->
+ Bytes =
+ if
+ Val >= 0 ->
+ encode_integer_pos(Val, []);
+ true ->
+ encode_integer_neg(Val, [])
+ end,
+ {Bytes,length(Bytes)}.
+
+encode_integer_pos(0, [B|_Acc]=L) when B < 128 ->
+ L;
+encode_integer_pos(N, Acc) ->
+ encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
+
+encode_integer_neg(-1, [B1|_T]=L) when B1 > 127 ->
+ L;
+encode_integer_neg(N, Acc) ->
+ encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
+
+%%===============================================================================
+%% decode integer
+%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%===============================================================================
+
+decode_integer(Tlv, Range, NamedNumberList, TagIn) ->
+ V = match_tags(Tlv, TagIn),
+ Int = range_check_integer(decode_integer(V), Range),
+ number2name(Int, NamedNumberList).
+
+decode_integer(Tlv, Range, TagIn) ->
+ V = match_tags(Tlv, TagIn),
+ Int = decode_integer(V),
+ range_check_integer(Int, Range).
+
+decode_integer(Bin) ->
+ Len = byte_size(Bin),
+ <<Int:Len/signed-unit:8>> = Bin,
+ Int.
+
+range_check_integer(Int, Range) ->
+ case Range of
+ [] -> % No length constraint
+ Int;
+ {Lb,Ub} when Int >= Lb, Ub >= Int -> % variable length constraint
+ Int;
+ {_,_} ->
+ exit({error,{asn1,{integer_range,Range,Int}}});
+ Int -> % fixed value constraint
+ Int;
+ SingleValue when is_integer(SingleValue) ->
+ exit({error,{asn1,{integer_range,Range,Int}}});
+ _ -> % some strange constraint that we don't support yet
+ Int
+ end.
+
+number2name(Int, []) ->
+ Int;
+number2name(Int, NamedNumberList) ->
+ case lists:keyfind(Int, 2, NamedNumberList) of
+ {NamedVal,_} ->
+ NamedVal;
+ _ ->
+ Int
+ end.
+
+
+%%============================================================================
+%% Enumerated value, ITU_T X.690 Chapter 8.4
+
+%% encode enumerated value
+%%============================================================================
+encode_enumerated(Val, TagIn) when is_integer(Val) ->
+ encode_tags(TagIn, encode_integer(Val)).
+
+%%============================================================================
+%% decode enumerated value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> Value
+%%===========================================================================
+decode_enumerated(Tlv, NamedNumberList, Tags) ->
+ Buffer = match_tags(Tlv, Tags),
+ decode_enumerated_notag(Buffer, NamedNumberList, Tags).
+
+decode_enumerated_notag(Buffer, {NamedNumberList,ExtList}, _Tags) ->
+ IVal = decode_integer(Buffer),
+ case decode_enumerated1(IVal, NamedNumberList) of
+ {asn1_enum,IVal} ->
+ decode_enumerated1(IVal,ExtList);
+ EVal ->
+ EVal
+ end;
+decode_enumerated_notag(Buffer, NNList, _Tags) ->
+ IVal = decode_integer(Buffer),
+ case decode_enumerated1(IVal, NNList) of
+ {asn1_enum,_} ->
+ exit({error,{asn1, {illegal_enumerated, IVal}}});
+ EVal ->
+ EVal
+ end.
+
+decode_enumerated1(Val, NamedNumberList) ->
+ %% it must be a named integer
+ case lists:keyfind(Val, 2, NamedNumberList) of
+ {NamedVal, _} ->
+ NamedVal;
+ _ ->
+ {asn1_enum,Val}
+ end.
+
+
+%%============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.6
+%%
+%% encode bitstring value
+%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constrint Len, only valid when identifiers
+%%============================================================================
+
+encode_bit_string(C, Bits, NamedBitList, TagIn) when is_bitstring(Bits) ->
+ PadLen = (8 - (bit_size(Bits) band 7)) band 7,
+ Compact = {PadLen,<<Bits/bitstring,0:PadLen>>},
+ encode_bin_bit_string(C, Compact, NamedBitList, TagIn);
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,TagIn) when is_integer(Unused), is_binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList,TagIn);
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList, TagIn) when is_atom(FirstVal) ->
+ encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(C, [{bit,X} | RestVal], NamedBitList, TagIn) ->
+ encode_bit_string_named(C, [{bit,X} | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(C, [FirstVal| RestVal], NamedBitList, TagIn) when is_integer(FirstVal) ->
+ encode_bit_string_bits(C, [FirstVal | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(_C, 0, _NamedBitList, TagIn) ->
+ encode_tags(TagIn, <<0>>,1);
+
+encode_bit_string(_C, [], _NamedBitList, TagIn) ->
+ encode_tags(TagIn, <<0>>,1);
+
+encode_bit_string(C, IntegerVal, NamedBitList, TagIn) when is_integer(IntegerVal) ->
+ BitListVal = int_to_bitlist(IntegerVal),
+ encode_bit_string_bits(C, BitListVal, NamedBitList, TagIn).
+
+
+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,TagIn)->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits);
+ {_Min,Max} ->
+ BBLen = (byte_size(BinBits)*8)-Unused,
+ if
+ BBLen > Max ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBLen},{maximum,Max}}}}});
+ true ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits)
+ end;
+ Size ->
+ case ((byte_size(BinBits)*8)-Unused) of
+ BBSize when BBSize =< Size ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits);
+ BBSize ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBSize},{should_be,Size}}}}})
+ end
+ end.
+
+remove_unused_then_dotag(TagIn,Unused,BinBits) ->
+ case Unused of
+ 0 when byte_size(BinBits) =:= 0 ->
+ encode_tags(TagIn, <<0>>, 1);
+ 0 ->
+ Bin = <<Unused,BinBits/binary>>,
+ encode_tags(TagIn, Bin, byte_size(Bin));
+ Num ->
+ N = byte_size(BinBits)-1,
+ <<BBits:N/binary,LastByte>> = BinBits,
+ encode_tags(TagIn,
+ [Unused,binary_to_list(BBits) ++[(LastByte bsr Num) bsl Num]],
+ 1+byte_size(BinBits))
+ end.
+
+
+%%=================================================================
+%% Encode named bits
+%%=================================================================
+
+encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn) ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
+ Size =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ lists:max(ToSetPos)+1;
+ {_Min,Max} ->
+ Max;
+ TSize ->
+ TSize
+ end,
+ BitList = make_and_set_list(Size, ToSetPos, 0),
+ {Len, Unused, OctetList} = encode_bitstring(BitList),
+ encode_tags(TagIn, [Unused|OctetList],Len+1).
+
+
+%%----------------------------------------
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+%%----------------------------------------
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+get_all_bitposes([Val | Rest], NamedBitList, Ack) when 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, TagIn) when is_list(BitListVal) ->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ Constr={Min,_Max} when is_integer(Min) ->
+ %% Max may be an integer or 'MAX'
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ {Constr={_,_},[]} ->%Constr={Min,Max}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ Size ->
+ case length(BitListVal) of
+ BitSize when BitSize == Size ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ BitSize when BitSize < Size ->
+ PaddedList = pad_bit_list(Size-BitSize,BitListVal),
+ {Len, Unused, OctetList} = encode_bitstring(PaddedList),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ BitSize ->
+ exit({error,{asn1,
+ {bitstring_length, {{was,BitSize},{should_be,Size}}}}})
+ end
+
+ end.
+
+encode_constr_bit_str_bits({{_Min1,Max1},{Min2,Max2}},BitListVal,TagIn) ->
+ BitLen = length(BitListVal),
+ case BitLen of
+ Len when Len > Max2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max2}}}}});
+ Len when Len > Max1, Len < Min2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {not_allowed_interval,
+ Max1,Min2}}}}});
+ _ ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused, OctetList], Len+1)
+ end;
+encode_constr_bit_str_bits({Min,Max},BitListVal,TagIn) ->
+ 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,Max}}}}});
+ true ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused, OctetList], Len+1)
+ end.
+
+
+%% returns a list of length Size + length(BitListVal), with BitListVal
+%% as the most significant elements followed by padded zero elements
+pad_bit_list(Size, BitListVal) ->
+ Tail = lists:duplicate(Size,0),
+ lists:append(BitListVal, Tail).
+
+%%=================================================================
+%% Do the actual encoding
+%% ([bitlist]) -> {ListLen, UnusedBits, OctetList}
+%%=================================================================
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest]) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Val], 1);
+encode_bitstring(Val) ->
+ {Unused, Octet} = unused_bitlist(Val, 7, 0),
+ {1, Unused, [Octet]}.
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest], Ack, Len) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Ack | [Val]], Len + 1);
+%%even multiple of 8 bits..
+encode_bitstring([], Ack, Len) ->
+ {Len, 0, Ack};
+%% unused bits in last octet
+encode_bitstring(Rest, Ack, Len) ->
+ {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) ->
+ unused_bitlist(Rest, Trail - 1, (Bit bsl Trail) bor Ack).
+
+
+%%============================================================================
+%% decode bitstring value
+%%============================================================================
+
+decode_compact_bit_string(Buffer, Range, Tags) ->
+ case match_and_collect(Buffer, Tags) of
+ <<0>> ->
+ check_restricted_string({0,<<>>}, 0, Range);
+ <<Unused,Bits/binary>> ->
+ Val = {Unused,Bits},
+ Len = bit_size(Bits) - Unused,
+ check_restricted_string(Val, Len, Range)
+ end.
+
+decode_legacy_bit_string(Buffer, Range, Tags) ->
+ Val = case match_and_collect(Buffer, Tags) of
+ <<0>> ->
+ [];
+ <<Unused,Bits/binary>> ->
+ decode_bitstring2(byte_size(Bits), Unused, Bits)
+ end,
+ check_restricted_string(Val, length(Val), Range).
+
+decode_native_bit_string(Buffer, Range, Tags) ->
+ case match_and_collect(Buffer, Tags) of
+ <<0>> ->
+ check_restricted_string(<<>>, 0, Range);
+ <<Unused,Bits/binary>> ->
+ Size = bit_size(Bits) - Unused,
+ <<Val:Size/bitstring,_:Unused/bitstring>> = Bits,
+ check_restricted_string(Val, Size, Range)
+ end.
+
+decode_named_bit_string(Buffer, NamedNumberList, Tags) ->
+ case match_and_collect(Buffer, Tags) of
+ <<0>> ->
+ [];
+ <<Unused,Bits/binary>> ->
+ BitString = decode_bitstring2(byte_size(Bits), Unused, Bits),
+ decode_bitstring_NNL(BitString, NamedNumberList)
+ end.
+
+%%----------------------------------------
+%% Decode the in buffer to bits
+%%----------------------------------------
+decode_bitstring2(1, Unused,
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,_/binary>>) ->
+ lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0], 8-Unused);
+decode_bitstring2(Len, Unused,
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Buffer/binary>>) ->
+ [B7,B6,B5,B4,B3,B2,B1,B0|
+ decode_bitstring2(Len - 1, Unused, Buffer)].
+
+%%----------------------------------------
+%% Decode 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).
+
+%%============================================================================
+%% Null value, ITU_T X.690 Chapter 8.8
+%%
+%% encode NULL value
+%%============================================================================
+
+encode_null(_Val, TagIn) ->
+ encode_tags(TagIn, [], 0).
+
+%%============================================================================
+%% decode NULL value
+%% (Buffer, HasTag, TotalLen) -> {NULL, Remain, RemovedBytes}
+%%============================================================================
+
+decode_null(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ case Val of
+ <<>> ->
+ 'NULL';
+ _ ->
+ exit({error,{asn1,{decode_null,Val}}})
+ end.
+
+%%============================================================================
+%% Object identifier, ITU_T X.690 Chapter 8.19
+%%
+%% encode Object Identifier value
+%%============================================================================
+
+encode_object_identifier(Val, TagIn) ->
+ encode_tags(TagIn, e_object_identifier(Val)).
+
+e_object_identifier({'OBJECT IDENTIFIER', 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} = lists:mapfoldl(fun enc_obj_id_tail/2, 0, Tail),
+ {[H|R],Lh+Lr}.
+
+enc_obj_id_tail(H, Len) ->
+ {B,L} = mk_object_val(H),
+ {B,Len+L}.
+
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8th bit is always set to one
+%% except for the last octet, where it's 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+
+%%============================================================================
+%% decode Object Identifier value
+%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
+%%============================================================================
+
+decode_object_identifier(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ [AddedObjVal|ObjVals] = dec_subidentifiers(Val,0,[]),
+ {Val1, Val2} = if
+ AddedObjVal < 40 ->
+ {0, AddedObjVal};
+ AddedObjVal < 80 ->
+ {1, AddedObjVal - 40};
+ true ->
+ {2, AddedObjVal - 80}
+ end,
+ list_to_tuple([Val1, Val2 | ObjVals]).
+
+dec_subidentifiers(<<>>,_Av,Al) ->
+ lists:reverse(Al);
+dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al) ->
+ dec_subidentifiers(T,(Av bsl 7) + H,Al);
+dec_subidentifiers(<<H,T/binary>>,Av,Al) ->
+ dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al]).
+
+%%============================================================================
+%% RELATIVE-OID, ITU_T X.690 Chapter 8.20
+%%
+%% encode Relative Object Identifier
+%%============================================================================
+
+encode_relative_oid(Val,TagIn) when is_tuple(Val) ->
+ encode_relative_oid(tuple_to_list(Val),TagIn);
+encode_relative_oid(Val,TagIn) ->
+ encode_tags(TagIn, enc_relative_oid(Val)).
+
+enc_relative_oid(Tuple) when is_tuple(Tuple) ->
+ enc_relative_oid(tuple_to_list(Tuple));
+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(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ ObjVals = dec_subidentifiers(Val,0,[]),
+ list_to_tuple(ObjVals).
+
+%%============================================================================
+%% Restricted character string types, ITU_T X.690 Chapter 8.20
+%%
+%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%%============================================================================
+encode_restricted_string(OctetList, TagIn) when is_binary(OctetList) ->
+ encode_tags(TagIn, OctetList, byte_size(OctetList));
+encode_restricted_string(OctetList, TagIn) when is_list(OctetList) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%%============================================================================
+
+decode_restricted_string(Tlv, TagsIn) ->
+ Bin = match_and_collect(Tlv, TagsIn),
+ binary_to_list(Bin).
+
+decode_restricted_string(Tlv, Range, TagsIn) ->
+ Bin = match_and_collect(Tlv, TagsIn),
+ check_restricted_string(binary_to_list(Bin), byte_size(Bin), Range).
+
+check_restricted_string(Val, StrLen, Range) ->
+ case Range of
+ {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint
+ Val;
+ {{Lb,_Ub},[]} when StrLen >= Lb ->
+ Val;
+ {{Lb,_Ub},_Ext=[Min|_]} when StrLen >= Lb; StrLen >= Min ->
+ Val;
+ {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
+ StrLen =< Ub2, StrLen >= Lb2 ->
+ Val;
+ StrLen -> % fixed length constraint
+ Val;
+ {_,_} ->
+ 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
+ Val
+ end.
+
+
+%%============================================================================
+%% encode Universal string
+%%============================================================================
+
+encode_universal_string(Universal, TagIn) ->
+ OctetList = mk_uni_list(Universal),
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+mk_uni_list(In) ->
+ mk_uni_list(In,[]).
+
+mk_uni_list([],List) ->
+ lists:reverse(List);
+mk_uni_list([{A,B,C,D}|T],List) ->
+ mk_uni_list(T,[D,C,B,A|List]);
+mk_uni_list([H|T],List) ->
+ mk_uni_list(T,[H,0,0,0|List]).
+
+%%===========================================================================
+%% decode Universal strings
+%% (Buffer, Range, StringType, HasTag, LenIn) ->
+%% {String, Remain, RemovedBytes}
+%%===========================================================================
+
+decode_universal_string(Buffer, Range, Tags) ->
+ Bin = match_and_collect(Buffer, Tags),
+ Val = mk_universal_string(binary_to_list(Bin)),
+ check_restricted_string(Val, length(Val), Range).
+
+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, TagIn) when is_binary(UTF8String) ->
+ encode_tags(TagIn, UTF8String, byte_size(UTF8String));
+encode_UTF8_string(UTF8String, TagIn) ->
+ encode_tags(TagIn, UTF8String, length(UTF8String)).
+
+
+%%============================================================================
+%% decode UTF8 string
+%%============================================================================
+
+decode_UTF8_string(Tlv,TagsIn) ->
+ Val = match_tags(Tlv, TagsIn),
+ case Val of
+ [_|_]=PartList -> % constructed val
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end.
+
+
+%%============================================================================
+%% encode BMP string
+%%============================================================================
+
+encode_BMP_string(BMPString, TagIn) ->
+ OctetList = mk_BMP_list(BMPString),
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+mk_BMP_list(In) ->
+ mk_BMP_list(In, []).
+
+mk_BMP_list([],List) ->
+ lists:reverse(List);
+mk_BMP_list([{0,0,C,D}|T], List) ->
+ mk_BMP_list(T, [D,C|List]);
+mk_BMP_list([H|T], List) ->
+ mk_BMP_list(T, [H,0|List]).
+
+%%============================================================================
+%% decode (OctetList, Range(ignored), tag|notag) -> {ValList, RestList}
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+decode_BMP_string(Buffer, Range, Tags) ->
+ Bin = match_and_collect(Buffer, Tags),
+ Val = mk_BMP_string(binary_to_list(Bin)),
+ check_restricted_string(Val, length(Val), Range).
+
+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(OctetList, TagIn) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode Generalized time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_generalized_time(Tlv, _Range, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ NewVal = case Val of
+ [_H|_T]=PartList -> % constructed
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end,
+ binary_to_list(NewVal).
+
+%%============================================================================
+%% Universal time, ITU_T X.680 Chapter 40
+%%
+%% encode UTC time
+%%============================================================================
+
+encode_utc_time(OctetList, TagIn) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode UTC time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_utc_time(Tlv, _Range, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ NewVal = case Val of
+ [_|_]=PartList -> % constructed
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end,
+ binary_to_list(NewVal).
+
+
+%%============================================================================
+%% Length handling
+%%
+%% Encode length
+%%
+%% encode_length(Int) ->
+%% [<127]| [128 + Int (<127),OctetList] | [16#80]
+%%============================================================================
+
+encode_length(L) when L =< 16#7F ->
+ {[L],1};
+encode_length(L) ->
+ Oct = minimum_octets(L),
+ Len = length(Oct),
+ if
+ Len =< 126 ->
+ {[16#80 bor Len|Oct],Len+1};
+ true ->
+ exit({error,{asn1, too_long_length_oct, Len}})
+ end.
+
+%% Val must be >= 0
+minimum_octets(Val) ->
+ minimum_octets(Val, []).
+
+minimum_octets(0, Acc) ->
+ Acc;
+minimum_octets(Val, Acc) ->
+ minimum_octets(Val bsr 8, [Val band 16#FF|Acc]).
+
+
+%%===========================================================================
+%% Decode length
+%%
+%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
+%% {{Length, RestOctetsL}, NoRemovedBytes}
+%%===========================================================================
+
+decode_length(<<1:1,0:7,T/binary>>) ->
+ {indefinite,T};
+decode_length(<<0:1,Length:7,T/binary>>) ->
+ {Length,T};
+decode_length(<<1:1,LL:7,Length:LL/unit:8,T/binary>>) ->
+ {Length,T}.
+
+%% dynamicsort_SET_components(Arg) ->
+%% Res Arg -> list()
+%% Res -> list()
+%% Sorts the elements in Arg according to the encoded tag in
+%% increasing order.
+dynamicsort_SET_components(ListOfEncCs) ->
+ TagBinL = [begin
+ Bin = list_to_binary(L),
+ {dynsort_decode_tag(Bin),Bin}
+ end || L <- ListOfEncCs],
+ [E || {_,E} <- lists:keysort(1, TagBinL)].
+
+%% dynamicsort_SETOF(Arg) -> Res
+%% Arg -> list()
+%% Res -> list()
+%% Sorts the elements in Arg in increasing size
+dynamicsort_SETOF(ListOfEncVal) ->
+ BinL = lists:map(fun(L) when is_list(L) -> list_to_binary(L);
+ (B) -> B end, ListOfEncVal),
+ lists:sort(BinL).
+
+%% multiple octet tag
+dynsort_decode_tag(<<Class:2,_Form:1,31:5,Buffer/binary>>) ->
+ TagNum = dynsort_decode_tag(Buffer, 0),
+ {Class,TagNum};
+
+%% single tag (< 31 tags)
+dynsort_decode_tag(<<Class:2,_Form:1,TagNum:5,_/binary>>) ->
+ {Class,TagNum}.
+
+dynsort_decode_tag(<<0:1,PartialTag:7,_/binary>>, TagAcc) ->
+ (TagAcc bsl 7) bor PartialTag;
+dynsort_decode_tag(<<_:1,PartialTag:7,Buffer/binary>>, TagAcc0) ->
+ TagAcc = (TagAcc0 bsl 7) bor PartialTag,
+ dynsort_decode_tag(Buffer, TagAcc).
+
+
+%%-------------------------------------------------------------------------
+%% INTERNAL HELPER FUNCTIONS (not exported)
+%%-------------------------------------------------------------------------
+
+match_and_collect(Tlv, TagsIn) ->
+ Val = match_tags(Tlv, TagsIn),
+ case Val of
+ [_|_]=PartList -> % constructed val
+ collect_parts(PartList);
+ Bin when is_binary(Bin) ->
+ Bin
+ end.
+
+get_constraint(C, Key) ->
+ case lists:keyfind(Key, 1, C) of
+ false ->
+ no;
+ {_,V} ->
+ V
+ end.
+
+collect_parts(TlvList) ->
+ collect_parts(TlvList, []).
+
+collect_parts([{_,L}|Rest], Acc) when is_list(L) ->
+ collect_parts(Rest, [collect_parts(L)|Acc]);
+collect_parts([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest], _Acc) ->
+ collect_parts_bit(Rest, [Bits], Unused);
+collect_parts([{_T,V}|Rest], Acc) ->
+ collect_parts(Rest, [V|Acc]);
+collect_parts([], Acc) ->
+ list_to_binary(lists:reverse(Acc)).
+
+collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest], Acc, Uacc) ->
+ collect_parts_bit(Rest, [Bits|Acc], Unused+Uacc);
+collect_parts_bit([], Acc, Uacc) ->
+ list_to_binary([Uacc|lists:reverse(Acc)]).
diff --git a/lib/asn1/src/asn1rtt_check.erl b/lib/asn1/src/asn1rtt_check.erl
new file mode 100644
index 0000000000..e78b65a8fb
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_check.erl
@@ -0,0 +1,276 @@
+%%
+%% %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(asn1rtt_check).
+
+-export([check_bool/2,
+ check_int/3,
+ check_bitstring/3,
+ check_octetstring/2,
+ check_null/2,
+ check_objectidentifier/2,
+ check_objectdescriptor/2,
+ check_real/2,
+ check_enum/3,
+ check_restrictedstring/2]).
+
+check_bool(_Bool, asn1_DEFAULT) ->
+ true;
+check_bool(Bool, Bool) when is_boolean(Bool) ->
+ true;
+check_bool(_Bool1, Bool2) ->
+ throw({error,Bool2}).
+
+check_int(_, asn1_DEFAULT, _) ->
+ true;
+check_int(Value, Value, _) when is_integer(Value) ->
+ true;
+check_int(DefValue, Value, NNL) when is_atom(Value) ->
+ case lists:keyfind(Value, 1, NNL) of
+ {_,DefValue} ->
+ true;
+ _ ->
+ throw({error,DefValue})
+ end;
+check_int(DefaultValue, _Value, _) ->
+ throw({error,DefaultValue}).
+
+%% Two equal lists or integers
+check_bitstring(_, asn1_DEFAULT, _) ->
+ true;
+check_bitstring(V, V, _) ->
+ true;
+%% Default value as a list of 1 and 0 and user value as an integer
+check_bitstring(L=[H|T], Int, _) when is_integer(Int), is_integer(H) ->
+ case bit_list_to_int(L, length(T)) of
+ Int -> true;
+ _ -> throw({error,L,Int})
+ end;
+%% Default value as an integer, val as list
+check_bitstring(Int, Val, NBL) when is_integer(Int), is_list(Val) ->
+ BL = int_to_bit_list(Int, [], length(Val)),
+ check_bitstring(BL, Val, NBL);
+%% Default value and user value as lists of ones and zeros
+check_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL=[_H|_T]) when is_integer(H1), is_integer(H2) ->
+ L2new = remove_trailing_zeros(L2),
+ check_bitstring(L1, L2new, NBL);
+%% Default value as a list of 1 and 0 and user value as a list of atoms
+check_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_integer(H1), is_atom(H2) ->
+ L3 = bit_list_to_nbl(L1, NBL, 0, []),
+ check_bitstring(L3, L2, NBL);
+%% Both default value and user value as a list of atoms
+check_bitstring(L1=[H1|T1], L2=[H2|_T2], _)
+ when is_atom(H1), is_atom(H2), length(L1) =:= length(L2) ->
+ case lists:member(H1, L2) of
+ true ->
+ check_bitstring1(T1, L2);
+ false -> throw({error,L2})
+ end;
+%% Default value as a list of atoms and user value as a list of 1 and 0
+check_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_atom(H1), is_integer(H2) ->
+ L3 = bit_list_to_nbl(L2, NBL, 0, []),
+ check_bitstring(L1, L3, NBL);
+%% User value in compact format
+check_bitstring(DefVal,CBS={_,_}, NBL) ->
+ NewVal = cbs_to_bit_list(CBS),
+ check_bitstring(DefVal, NewVal, NBL);
+check_bitstring(DV, V, _) ->
+ throw({error,DV,V}).
+
+
+bit_list_to_int([0|Bs], ShL)->
+ bit_list_to_int(Bs, ShL-1) + 0;
+bit_list_to_int([1|Bs], ShL) ->
+ bit_list_to_int(Bs, ShL-1) + (1 bsl ShL);
+bit_list_to_int([], _) ->
+ 0.
+
+int_to_bit_list(0, Acc, 0) ->
+ Acc;
+int_to_bit_list(Int, Acc, Len) ->
+ int_to_bit_list(Int bsr 1, [Int band 1|Acc], Len - 1).
+
+bit_list_to_nbl([0|T], NBL, Pos, Acc) ->
+ bit_list_to_nbl(T, NBL, Pos+1, Acc);
+bit_list_to_nbl([1|T], NBL, Pos, Acc) ->
+ case lists:keyfind(Pos, 2, NBL) of
+ {N,_} ->
+ bit_list_to_nbl(T, NBL, Pos+1, [N|Acc]);
+ _ ->
+ throw({error,{no,named,element,at,pos,Pos}})
+ end;
+bit_list_to_nbl([], _, _, Acc) ->
+ Acc.
+
+remove_trailing_zeros(L2) ->
+ remove_trailing_zeros1(lists:reverse(L2)).
+remove_trailing_zeros1(L) ->
+ lists:reverse(lists:dropwhile(fun(0)->true;
+ (_) ->false
+ end,
+ L)).
+
+check_bitstring1([H|T], NBL) ->
+ case lists:member(H, NBL) of
+ true -> check_bitstring1(T, NBL);
+ V -> throw({error,V})
+ end;
+check_bitstring1([], _) ->
+ true.
+
+cbs_to_bit_list({Unused, <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Rest/binary>>}) when byte_size(Rest) >= 1 ->
+ [B7,B6,B5,B4,B3,B2,B1,B0|cbs_to_bit_list({Unused,Rest})];
+cbs_to_bit_list({0,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1>>}) ->
+ [B7,B6,B5,B4,B3,B2,B1,B0];
+cbs_to_bit_list({Unused,Bin}) when byte_size(Bin) =:= 1 ->
+ Used = 8-Unused,
+ <<Int:Used,_:Unused>> = Bin,
+ int_to_bit_list(Int, [], Used).
+
+
+check_octetstring(_, asn1_DEFAULT) ->
+ true;
+check_octetstring(L, L) ->
+ true;
+check_octetstring(L, Int) when is_list(L), is_integer(Int) ->
+ case integer_to_octetlist(Int) of
+ L -> true;
+ V -> throw({error,V})
+ end;
+check_octetstring(_, V) ->
+ throw({error,V}).
+
+integer_to_octetlist(Int) ->
+ integer_to_octetlist(Int, []).
+integer_to_octetlist(0, Acc) ->
+ Acc;
+integer_to_octetlist(Int, Acc) ->
+ integer_to_octetlist(Int bsr 8, [(Int band 255)|Acc]).
+
+check_null(_, asn1_DEFAULT) ->
+ true;
+check_null('NULL', 'NULL') ->
+ true;
+check_null(_, V) ->
+ throw({error,V}).
+
+check_objectidentifier(_, asn1_DEFAULT) ->
+ true;
+check_objectidentifier(OI, OI) ->
+ true;
+check_objectidentifier(DOI, OI) when is_tuple(DOI), is_tuple(OI) ->
+ check_objectidentifier1(tuple_to_list(DOI), tuple_to_list(OI));
+check_objectidentifier(_, OI) ->
+ throw({error,OI}).
+
+check_objectidentifier1([V|Rest1], [V|Rest2]) ->
+ check_objectidentifier1(Rest1, Rest2, V);
+check_objectidentifier1([V1|Rest1], [V2|Rest2]) ->
+ case reserved_objectid(V2, []) of
+ V1 ->
+ check_objectidentifier1(Rest1, Rest2, [V1]);
+ V ->
+ throw({error,V})
+ end.
+check_objectidentifier1([V|Rest1], [V|Rest2], Above) ->
+ check_objectidentifier1(Rest1, Rest2, [V|Above]);
+check_objectidentifier1([V1|Rest1], [V2|Rest2], Above) ->
+ case reserved_objectid(V2, Above) of
+ V1 ->
+ check_objectidentifier1(Rest1, Rest2, [V1|Above]);
+ V ->
+ throw({error,V})
+ end;
+check_objectidentifier1([], [], _) ->
+ true;
+check_objectidentifier1(_, V, _) ->
+ throw({error,object,identifier,V}).
+
+%% ITU-T Rec. X.680 Annex B - D
+reserved_objectid('itu-t', []) -> 0;
+reserved_objectid('ccitt', []) -> 0;
+%% arcs below "itu-t"
+reserved_objectid('recommendation', [0]) -> 0;
+reserved_objectid('question', [0]) -> 1;
+reserved_objectid('administration', [0]) -> 2;
+reserved_objectid('network-operator', [0]) -> 3;
+reserved_objectid('identified-organization', [0]) -> 4;
+
+reserved_objectid(iso, []) -> 1;
+%% arcs below "iso", note that number 1 is not used
+reserved_objectid('standard', [1]) -> 0;
+reserved_objectid('member-body', [1]) -> 2;
+reserved_objectid('identified-organization', [1]) -> 3;
+
+reserved_objectid('joint-iso-itu-t', []) -> 2;
+reserved_objectid('joint-iso-ccitt', []) -> 2;
+
+reserved_objectid(_, _) -> false.
+
+
+check_objectdescriptor(_, asn1_DEFAULT) ->
+ true;
+check_objectdescriptor(OD, OD) ->
+ true;
+check_objectdescriptor(OD, OD) ->
+ throw({error,{not_implemented_yet,check_objectdescriptor}}).
+
+check_real(_, asn1_DEFAULT) ->
+ true;
+check_real(R, R) ->
+ true;
+check_real(_, _) ->
+ throw({error,{not_implemented_yet,check_real}}).
+
+check_enum(_, asn1_DEFAULT, _) ->
+ true;
+check_enum(Val, Val, _) ->
+ true;
+check_enum(Int, Atom, Enumerations) when is_integer(Int), is_atom(Atom) ->
+ case lists:keyfind(Atom, 1, Enumerations) of
+ {_,Int} -> true;
+ _ -> throw({error,{enumerated,Int,Atom}})
+ end;
+check_enum(DefVal, Val, _) ->
+ throw({error,{enumerated,DefVal,Val}}).
+
+
+check_restrictedstring(_, asn1_DEFAULT) ->
+ true;
+check_restrictedstring(Val, Val) ->
+ true;
+check_restrictedstring([V|Rest1], [V|Rest2]) ->
+ check_restrictedstring(Rest1, Rest2);
+check_restrictedstring([V1|Rest1], [V2|Rest2]) ->
+ check_restrictedstring(V1, V2),
+ check_restrictedstring(Rest1, Rest2);
+%% tuple format of value
+check_restrictedstring({V1,V2}, [V1,V2]) ->
+ true;
+check_restrictedstring([V1,V2], {V1,V2}) ->
+ true;
+%% quadruple format of value
+check_restrictedstring({V1,V2,V3,V4}, [V1,V2,V3,V4]) ->
+ true;
+check_restrictedstring([V1,V2,V3,V4], {V1,V2,V3,V4}) ->
+ true;
+%% character string list
+check_restrictedstring(V1, V2) when is_list(V1), is_tuple(V2) ->
+ check_restrictedstring(V1, tuple_to_list(V2));
+check_restrictedstring(V1, V2) ->
+ throw({error,{restricted,string,V1,V2}}).
diff --git a/lib/asn1/src/asn1rtt_ext.erl b/lib/asn1/src/asn1rtt_ext.erl
new file mode 100644
index 0000000000..46adb2007d
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_ext.erl
@@ -0,0 +1,72 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(asn1rtt_ext).
+-export([transform_to_EXTERNAL1990/1,transform_to_EXTERNAL1994/1]).
+
+transform_to_EXTERNAL1990({_,_,_,_}=Val) ->
+ transform_to_EXTERNAL1990(tuple_to_list(Val), []);
+transform_to_EXTERNAL1990(Val) when is_tuple(Val) ->
+ %% Data already in ASN1 1990 format
+ Val.
+
+transform_to_EXTERNAL1990(['EXTERNAL'|Rest], Acc) ->
+ transform_to_EXTERNAL1990(Rest, ['EXTERNAL'|Acc]);
+transform_to_EXTERNAL1990([{syntax,Syntax}|Rest], Acc) ->
+ transform_to_EXTERNAL1990(Rest, [asn1_NOVALUE,Syntax|Acc]);
+transform_to_EXTERNAL1990([{'presentation-context-id',PCid}|Rest], Acc) ->
+ transform_to_EXTERNAL1990(Rest, [PCid,asn1_NOVALUE|Acc]);
+transform_to_EXTERNAL1990([{'context-negotiation',Context_negot}|Rest], Acc) ->
+ {_,Presentation_Cid,Transfer_syntax} = Context_negot,
+ transform_to_EXTERNAL1990(Rest, [Presentation_Cid,Transfer_syntax|Acc]);
+transform_to_EXTERNAL1990([asn1_NOVALUE|Rest], Acc) ->
+ transform_to_EXTERNAL1990(Rest, [asn1_NOVALUE|Acc]);
+transform_to_EXTERNAL1990([Data_val_desc,Data_value], Acc)
+ when is_list(Data_value)->
+ list_to_tuple(lists:reverse([{'octet-aligned',Data_value},
+ Data_val_desc|Acc]));
+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); is_binary(Data_value) ->
+ list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
+
+
+transform_to_EXTERNAL1994({'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}=V) ->
+ Identification =
+ case {DRef,IndRef} of
+ {DRef,asn1_NOVALUE} ->
+ {syntax,DRef};
+ {asn1_NOVALUE,IndRef} ->
+ {'presentation-context-id',IndRef};
+ _ ->
+ {'context-negotiation',
+ {'EXTERNAL_identification_context-negotiation',IndRef,DRef}}
+ end,
+ case Encoding of
+ {'octet-aligned',Val} when is_list(Val); is_binary(Val) ->
+ %% Transform to the EXTERNAL 1994 definition.
+ {'EXTERNAL',Identification,Data_v_desc,Val};
+ _ ->
+ %% Keep the EXTERNAL 1990 definition to avoid losing
+ %% information.
+ V
+ end.
diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl
new file mode 100644
index 0000000000..aa6cf4da0a
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_per.erl
@@ -0,0 +1,979 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(asn1rtt_per).
+
+-export([setext/1, fixextensions/2,
+ skipextensions/3, getbit/1, getchoice/3,
+ set_choice/3,encode_integer/2,
+ encode_small_number/1,
+ encode_constrained_number/2,
+ encode_length/1,
+ encode_length/2,
+ encode_bit_string/3,
+ encode_object_identifier/1,
+ encode_relative_oid/1,
+ complete/1,
+ encode_open_type/1,
+ encode_GeneralString/2,
+ encode_GraphicString/2,
+ encode_TeletexString/2,
+ encode_VideotexString/2,
+ encode_ObjectDescriptor/2,
+ encode_UTF8String/1,
+ encode_octet_string/3,
+ encode_known_multiplier_string/4,
+ octets_to_complete/2]).
+
+-define('16K',16384).
+-define('32K',32768).
+-define('64K',65536).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(false) ->
+ [0];
+setext(true) ->
+ [1].
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum)|pre_complete_bits(ExtNum,ExtBits)]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) ->
+ Prev = Nr - 1,
+ case ExtensionBitstr of
+ <<_:Prev,1:1,_/bitstring>> ->
+ {Len,Bytes1} = decode_length(Bytes0),
+ <<_:Len/binary,Bytes2/bitstring>> = Bytes1,
+ skipextensions(Bytes2, Nr+1, ExtensionBitstr);
+ <<_:Prev,0:1,_/bitstring>> ->
+ skipextensions(Bytes0, Nr+1, ExtensionBitstr);
+ _ ->
+ Bytes0
+ 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}).
+
+
+getbit(Buffer) ->
+ <<B:1,Rest/bitstring>> = Buffer,
+ {B,Rest}.
+
+getbits(Buffer, Num) when is_bitstring(Buffer) ->
+ <<Bs:Num,Rest/bitstring>> = Buffer,
+ {Bs,Rest}.
+
+align(Bin) when is_binary(Bin) ->
+ Bin;
+align(BitStr) when is_bitstring(BitStr) ->
+ AlignBits = bit_size(BitStr) rem 8,
+ <<_:AlignBits,Rest/binary>> = BitStr,
+ Rest.
+
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as an integer with bit significance as in buffer.
+getoctets(Buffer, Num) when is_binary(Buffer) ->
+ <<Val:Num/integer-unit:8,RestBin/binary>> = Buffer,
+ {Val,RestBin};
+getoctets(Buffer, Num) when is_bitstring(Buffer) ->
+ AlignBits = bit_size(Buffer) rem 8,
+ <<_:AlignBits,Val:Num/integer-unit:8,RestBin/binary>> = Buffer,
+ {Val,RestBin}.
+
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as a binary
+getoctets_as_bin(Bin,Num) when is_binary(Bin) ->
+ <<Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,RestBin};
+getoctets_as_bin(Bin,Num) when is_bitstring(Bin) ->
+ AlignBits = bit_size(Bin) rem 8,
+ <<_:AlignBits,Val:Num/binary,RestBin/binary>> = Bin,
+ {Val,RestBin}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 ->
+ [0, % the value is in the root set
+ encode_constrained_number({0,Len1-1},N)];
+ N when is_integer(N) ->
+ [0]; % no encoding if only 0 or 1 alternative
+ false ->
+ [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_constrained_number({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.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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(Val) when is_list(Val) ->
+ Bin = list_to_binary(Val),
+ case byte_size(Bin) of
+ Size when Size > 255 ->
+ [encode_length(Size),21,<<Size:16>>,Bin];
+ Size ->
+ [encode_length(Size),20,Size,Bin]
+ end;
+encode_open_type(Val) when is_binary(Val) ->
+ case byte_size(Val) of
+ Size when Size > 255 ->
+ [encode_length(Size),21,<<Size:16>>,Val]; % octets implies align
+ Size ->
+ [encode_length(Size),20,Size,Val]
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint, Value) -> CompleteList
+%%
+encode_integer([{Rc,_Ec}],Val) when is_tuple(Rc) ->
+ try
+ [0|encode_integer([Rc], Val)]
+ catch
+ _:{error,{asn1,_}} ->
+ [1|encode_unconstrained_number(Val)]
+ end;
+encode_integer([], Val) ->
+ encode_unconstrained_number(Val);
+%% The constraint is the effective constraint, and in this case is a number
+encode_integer([{'SingleValue',V}], V) ->
+ [];
+encode_integer([{'ValueRange',{Lb,Ub}=VR,Range,PreEnc}],Val)
+ when Val >= Lb, Ub >= Val ->
+ %% this case when NamedNumberList
+ encode_constrained_number(VR, Range, PreEnc, Val);
+encode_integer([{'ValueRange',{Lb,'MAX'}}], Val) ->
+ encode_semi_constrained_number(Lb, Val);
+encode_integer([{'ValueRange',{'MIN',_}}], Val) ->
+ encode_unconstrained_number(Val);
+encode_integer([{'ValueRange',VR={_Lb,_Ub}}], Val) ->
+ encode_constrained_number(VR, Val);
+encode_integer(_,Val) ->
+ exit({error,{asn1,{illegal_value,Val}}}).
+
+
+%% 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(Val) when Val < 64 ->
+ [10,7,Val];
+encode_small_number(Val) ->
+ [1|encode_semi_constrained_number(0, Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2, 6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2)
+ end.
+
+%% X.691:10.7 Encoding of a semi-constrained whole number
+encode_semi_constrained_number(Lb, Val) ->
+ Val2 = Val - Lb,
+ Oct = eint_positive(Val2),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ [20,Len+1,Len|Oct];
+ Len < 256 ->
+ [encode_length(Len),20,Len|Oct];
+ true ->
+ [encode_length(Len),21,<<Len:16>>|Oct]
+ end.
+
+decode_semi_constrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes),
+ getoctets(Bytes2, Len).
+
+encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) ->
+ Val2 = Val-Lb,
+ [10,N,Val2];
+encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) when N < 256->
+ %% N is 8 or 16 (1 or 2 octets)
+ Val2 = Val-Lb,
+ [20,N,Val2];
+encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) -> % N>255
+ %% N is 8 or 16 (1 or 2 octets)
+ Val2 = Val-Lb,
+ [21,<<N:16>>,Val2];
+encode_constrained_number({Lb,_Ub},Range,_,Val) ->
+ Val2 = Val-Lb,
+ if
+ Range =< 16#1000000 -> % max 3 octets
+ Octs = eint_positive(Val2),
+ L = length(Octs),
+ [encode_length({1,3},L),[20,L,Octs]];
+ Range =< 16#100000000 -> % max 4 octets
+ Octs = eint_positive(Val2),
+ L = length(Octs),
+ [encode_length({1,4},L),[20,L,Octs]];
+ Range =< 16#10000000000 -> % max 5 octets
+ Octs = eint_positive(Val2),
+ L = length(Octs),
+ [encode_length({1,5},L),[20,L,Octs]];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end.
+
+encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 1 -> [];
+ Range == 2 ->
+ [Val2];
+ Range =< 4 ->
+ [10,2,Val2];
+ Range =< 8 ->
+ [10,3,Val2];
+ Range =< 16 ->
+ [10,4,Val2];
+ Range =< 32 ->
+ [10,5,Val2];
+ Range =< 64 ->
+ [10,6,Val2];
+ Range =< 128 ->
+ [10,7,Val2];
+ Range =< 255 ->
+ [10,8,Val2];
+ Range =< 256 ->
+ [20,1,Val2];
+ Range =< 65536 ->
+ [20,2,<<Val2:16>>];
+ Range =< (1 bsl (255*8)) ->
+ Octs = binary:encode_unsigned(Val2),
+ RangeOcts = binary:encode_unsigned(Range - 1),
+ OctsLen = byte_size(Octs),
+ RangeOctsLen = byte_size(RangeOcts),
+ LengthBitsNeeded = minimum_bits(RangeOctsLen - 1),
+ [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end;
+encode_constrained_number({_,_},Val) ->
+ exit({error,{asn1,{illegal_value,Val}}}).
+
+decode_constrained_number(Buffer,VR={Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+ decode_constrained_number(Buffer,VR,Range).
+
+decode_constrained_number(Buffer,{Lb,_Ub},Range) ->
+ % 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 =< (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) ->
+ Oct = if
+ Val >= 0 ->
+ eint(Val, []);
+ true ->
+ enint(Val, [])
+ end,
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ [20,Len + 1,Len|Oct];
+ Len < 256 ->
+ [20,Len + 2,<<2:2,Len:14>>|Oct];
+ true ->
+ [encode_length(Len),21,<<Len:16>>|Oct]
+ end.
+
+%% used for positive Values which don't need a sign bit
+%% returns a list
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+%% 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(Len) -> % unconstrained
+ if
+ Len < 128 ->
+ [20,1,Len];
+ Len < 16384 ->
+ <<20,2,2:2,Len:14>>;
+ true -> % should be able to endode length >= 16384 i.e. fragmented length
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end.
+
+encode_length(undefined, Len) -> % un-constrained
+ encode_length(Len);
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length({Lb,Ub}=Vr, 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(Len);
+encode_length({{Lb,Ub}=Vr,Ext}, Len)
+ when Ub =< 65535 ,Lb >= 0,Len=<Ub, is_list(Ext) ->
+ %% constrained extensible
+ [0|encode_constrained_number(Vr,Len)];
+encode_length({{Lb,_},Ext},Len) when is_list(Ext) ->
+ [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 ->
+ [10,7,Len-1];
+encode_small_length(Len) ->
+ [1,encode_length(Len)].
+
+
+decode_length(Buffer) -> % un-constrained
+ case align(Buffer) of
+ <<0:1,Oct:7,Rest/binary>> ->
+ {Oct,Rest};
+ <<2:2,Val:14,Rest/binary>> ->
+ {Val,Rest};
+ <<3:2,_Val:14,_Rest/binary>> ->
+ %% this case should be fixed
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}})
+ end.
+
+decode_length(Buffer, {Lb,Ub}) when Ub =< 65535, Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer, {Lb,Ub});
+decode_length(Buffer, {Lb,_Ub}) when is_integer(Lb), Lb >= 0 -> % Ub > 65535
+ decode_length(Buffer).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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, Bits, NamedBitList) when is_bitstring(Bits) ->
+ PadLen = (8 - (bit_size(Bits) band 7)) band 7,
+ Compact = {PadLen,<<Bits/bitstring,0:PadLen>>},
+ encode_bin_bit_string(C, Compact, NamedBitList);
+encode_bit_string(C, {Unused,BinBits}=Bin, 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);% consider the constraint
+
+encode_bit_string(C, BL=[{bit,_} | _RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes(BL, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+encode_bit_string(Int, BitListValue, _)
+ when is_list(BitListValue),is_integer(Int),Int =< 16 ->
+ %% The type is constrained by a single value size constraint
+ %% range_check(Int,length(BitListValue)),
+ [40,Int,length(BitListValue),BitListValue];
+encode_bit_string(Int, BitListValue, _)
+ when is_list(BitListValue),is_integer(Int), Int =< 255 ->
+ %% The type is constrained by a single value size constraint
+ %% range_check(Int,length(BitListValue)),
+ [2,40,Int,length(BitListValue),BitListValue];
+encode_bit_string(Int, BitListValue, _)
+ when is_list(BitListValue),is_integer(Int), Int < ?'64K' ->
+ {Code,DesiredLength,Length} =
+ case length(BitListValue) of
+ B1 when B1 > Int ->
+ exit({error,{'BIT_STRING_length_greater_than_SIZE',
+ Int,BitListValue}});
+ B1 when B1 =< 255,Int =< 255 ->
+ {40,Int,B1};
+ B1 when B1 =< 255 ->
+ {42,<<Int:16>>,B1};
+ B1 ->
+ {43,<<Int:16>>,<<B1:16>>}
+ end,
+ %% The type is constrained by a single value size constraint
+ [2,Code,DesiredLength,Length,BitListValue];
+encode_bit_string(no, BitListValue,[])
+ when is_list(BitListValue) ->
+ [encode_length(length(BitListValue)),
+ 2|BitListValue];
+encode_bit_string({{Fix,Fix},Ext}, BitListValue,[])
+ when is_integer(Fix), is_list(Ext) ->
+ case length(BitListValue) of
+ Len when Len =< Fix ->
+ [0|encode_bit_string(Fix, BitListValue, [])];
+ _ ->
+ [1|encode_bit_string(no, BitListValue, [])]
+ end;
+encode_bit_string(C, BitListValue,[])
+ when is_list(BitListValue) ->
+ [encode_length(C, length(BitListValue)),
+ 2|BitListValue];
+encode_bit_string(no, BitListValue,_NamedBitList)
+ when is_list(BitListValue) ->
+ %% this case with an unconstrained BIT STRING can be made more efficient
+ %% if the complete driver can take a special code so the length field
+ %% is encoded there.
+ NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue))),
+ [encode_length(length(NewBitLVal)),2|NewBitLVal];
+encode_bit_string({{Fix,Fix},Ext}, BitListValue, NamedBitList)
+ when is_integer(Fix), is_list(Ext) ->
+ case length(BitListValue) of
+ Len when Len =< Fix ->
+ [0|encode_bit_string(Fix, BitListValue, NamedBitList)];
+ _ ->
+ [1|encode_bit_string(no, BitListValue, NamedBitList)]
+ end;
+encode_bit_string(C, BitListValue, _NamedBitList)
+ when is_list(BitListValue) -> % C = {_,'MAX'}
+ NewBitLVal = bit_string_trailing_zeros(BitListValue, C),
+ [encode_length(C, length(NewBitLVal)),2|NewBitLVal];
+
+
+%% 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).
+
+bit_string_trailing_zeros(BitList,C) when is_integer(C) ->
+ bit_string_trailing_zeros1(BitList,C,C);
+bit_string_trailing_zeros(BitList,{Lb,Ub}) when is_integer(Lb) ->
+ bit_string_trailing_zeros1(BitList,Lb,Ub);
+bit_string_trailing_zeros(BitList,{{Lb,Ub},_}) when is_integer(Lb) ->
+ bit_string_trailing_zeros1(BitList,Lb,Ub);
+bit_string_trailing_zeros(BitList,_) ->
+ BitList.
+
+bit_string_trailing_zeros1(BitList,Lb,Ub) ->
+ case length(BitList) of
+ Lb -> BitList;
+ B when B < Lb -> BitList++lists:duplicate(Lb-B, 0);
+ D -> F = fun(L,LB,LB,_,_)->lists:reverse(L);
+ ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun);
+ (L,L1,_,UB,_)when L1 =< UB -> lists:reverse(L);
+ (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING,
+ BitList}}) end,
+ F(lists:reverse(BitList),D,Lb,Ub,F)
+ end.
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),i.e. number unused bits in least sign. byte of
+%% BinBits = binary().
+encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList)
+ when is_integer(C),C=<16 ->
+ range_check(C, bit_size(BinBits) - Unused),
+ [45,C,byte_size(BinBits),BinBits];
+encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList)
+ when is_integer(C), C =< 255 ->
+ range_check(C, bit_size(BinBits) - Unused),
+ [2,45,C,byte_size(BinBits),BinBits];
+encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList)
+ when is_integer(C), C =< 65535 ->
+ range_check(C, bit_size(BinBits) - Unused),
+ case byte_size(BinBits) of
+ Size when Size =< 255 ->
+ [2,46,<<C:16>>,Size,BinBits];
+ Size ->
+ [2,47,<<C:16>>,<<Size:16>>,BinBits]
+ end;
+encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) ->
+ {Unused1,Bin1} =
+ %% removes all trailing bits if NamedBitList is not empty
+ remove_trailing_bin(NamedBitList,UnusedAndBin),
+ case C of
+ {Lb,Ub} when is_integer(Lb),is_integer(Ub) ->
+ Size = byte_size(Bin1),
+ [encode_length({Lb,Ub}, Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)];
+ no ->
+ Size = byte_size(Bin1),
+ [encode_length(Size*8 - Unused1),
+ 2|octets_unused_to_complete(Unused1, Size, Bin1)];
+ {{Fix,Fix},Ext} when is_integer(Fix),is_list(Ext) ->
+ case byte_size(Bin1)*8 - Unused1 of
+ Size when Size =< Fix ->
+ [0|encode_bin_bit_string(Fix,UnusedAndBin,NamedBitList)];
+ _Size ->
+ [1|encode_bin_bit_string(no,UnusedAndBin,NamedBitList)]
+ end;
+ Sc ->
+ Size = byte_size(Bin1),
+ [encode_length(Sc, Size*8 - Unused1),
+ 2|octets_unused_to_complete(Unused1,Size,Bin1)]
+ end.
+
+range_check(C,C) when is_integer(C) ->
+ ok;
+range_check(C1,C2) when is_integer(C1) ->
+ exit({error,{asn1,{bit_string_out_of_range,{C1,C2}}}}).
+
+remove_trailing_bin([], {Unused,Bin}) ->
+ {Unused,Bin};
+remove_trailing_bin(_NamedNumberList,{_Unused,<<>>}) ->
+ {0,<<>>};
+remove_trailing_bin(NamedNumberList, {_Unused,Bin}) ->
+ Size = byte_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});
+ _ ->
+ {Unused2,Bin}
+ end.
+
+
+trailingZeroesInNibble(0) ->
+ 4;
+trailingZeroesInNibble(1) ->
+ 0;
+trailingZeroesInNibble(2) ->
+ 1;
+trailingZeroesInNibble(3) ->
+ 0;
+trailingZeroesInNibble(4) ->
+ 2;
+trailingZeroesInNibble(5) ->
+ 0;
+trailingZeroesInNibble(6) ->
+ 1;
+trailingZeroesInNibble(7) ->
+ 0;
+trailingZeroesInNibble(8) ->
+ 3;
+trailingZeroesInNibble(9) ->
+ 0;
+trailingZeroesInNibble(10) ->
+ 1;
+trailingZeroesInNibble(11) ->
+ 0;
+trailingZeroesInNibble(12) -> %#1100
+ 2;
+trailingZeroesInNibble(13) ->
+ 0;
+trailingZeroesInNibble(14) ->
+ 1;
+trailingZeroesInNibble(15) ->
+ 0.
+
+
+%%%%%%%%%%%%%%%
+%%
+
+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:keyfind(Val, 1, NamedBitList) of
+ {_ValName, ValPos} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ false ->
+ 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([], _) ->
+ [].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(_C, true, _Val) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string({_,_}=SZ, false, Val) ->
+ Len = length(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),
+ 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),
+ try
+ [encode_length(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}}).
+
+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(Len)|octets_to_complete(Len, B)].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+
+encode_restricted_string(Val) when is_list(Val)->
+ Len = length(Val),
+ [encode_length(Len)|octets_to_complete(Len, Val)].
+
+encode_known_multiplier_string(SizeC, NumBits, CharOutTab, Val) ->
+ Result = chars_encode2(Val, NumBits, CharOutTab),
+ case SizeC of
+ Ub when is_integer(Ub), Ub*NumBits =< 16 ->
+ Result;
+ Ub when is_integer(Ub), Ub =<65535 -> % fixed length
+ [2,Result];
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb},length(Val)),2,Result];
+ no ->
+ [encode_length(length(Val)),2,Result]
+ end.
+
+encode_GeneralString(_C,Val) ->
+ encode_restricted_string(Val).
+
+encode_GraphicString(_C,Val) ->
+ encode_restricted_string(Val).
+
+encode_ObjectDescriptor(_C,Val) ->
+ encode_restricted_string(Val).
+
+encode_TeletexString(_C,Val) -> % equivalent with T61String
+ encode_restricted_string(Val).
+
+encode_VideotexString(_C,Val) ->
+ encode_restricted_string(Val).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint
+%% PermittedAlphabet into account.
+%%
+%% This function only encodes the value part and NOT the length.
+
+chars_encode2([H|T],NumBits,T1={Min,Max,notab}) when H =< Max, H >= Min ->
+ [pre_complete_bits(NumBits,H-Min)|chars_encode2(T,NumBits,T1)];
+chars_encode2([H|T],NumBits,T1={Min,Max,Tab}) when H =< Max, H >= Min ->
+ [pre_complete_bits(NumBits,exit_if_false(H,element(H-Min+1,Tab)))|
+ chars_encode2(T,NumBits,T1)];
+chars_encode2([{A,B,C,D}|T],NumBits,T1={Min,_Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [pre_complete_bits(NumBits,
+ ((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min)|
+ chars_encode2(T,NumBits,T1)];
+chars_encode2([H={A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [pre_complete_bits(NumBits,exit_if_false(H,element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)))|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_NumBits,{_Min,_Max,_Tab}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+exit_if_false(V,false)->
+ exit({error,{asn1,{"illegal value according to Permitted alphabet constraint",V}}});
+exit_if_false(_,V) ->V.
+
+pre_complete_bits(NumBits,Val) when NumBits =< 8 ->
+ [10,NumBits,Val];
+pre_complete_bits(NumBits,Val) when NumBits =< 16 ->
+ [10,NumBits-8,Val bsr 8,10,8,(Val band 255)];
+pre_complete_bits(NumBits,Val) when NumBits =< 2040 -> % 255 * 8
+ Unused = (8 - (NumBits rem 8)) rem 8,
+ Len = NumBits + Unused,
+ [30,Unused,Len div 8,<<(Val bsl Unused):Len>>].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_UTF8String(Val) -> CompleteList
+%% Val -> <<utf8encoded binary>>
+%% CompleteList -> [apropriate codes and values for driver complete]
+%%
+encode_UTF8String(Val) when is_binary(Val) ->
+ Sz = byte_size(Val),
+ [encode_length(Sz),octets_to_complete(Sz, Val)];
+encode_UTF8String(Val) ->
+ encode_UTF8String(list_to_binary(Val)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier(Val) ->
+ OctetList = e_object_identifier(Val),
+ Octets = list_to_binary(OctetList),
+ Sz = byte_size(Octets),
+ [encode_length(Sz),
+ octets_to_complete(Sz, Octets)].
+
+e_object_identifier({'OBJECT IDENTIFIER',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]].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_relative_oid(Val) -> CompleteList
+%% encode_relative_oid({Name,Val}) -> CompleteList
+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]),
+ Sz = byte_size(Octets),
+ [encode_length(Sz)|octets_to_complete(Sz, Octets)].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 asn1rt_nif:encode_per_complete(L) of
+ <<>> -> <<0>>;
+ Bin -> Bin
+ end.
+
+octets_to_complete(Len,Val) when Len < 256 ->
+ [20,Len,Val];
+octets_to_complete(Len,Val) ->
+ [21,<<Len:16>>,Val].
+
+octets_unused_to_complete(Unused,Len,Val) when Len < 256 ->
+ [30,Unused,Len,Val];
+octets_unused_to_complete(Unused,Len,Val) ->
+ [31,Unused,<<Len:16>>,Val].
diff --git a/lib/asn1/src/asn1rtt_per_common.erl b/lib/asn1/src/asn1rtt_per_common.erl
new file mode 100644
index 0000000000..e7edc2b65f
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_per_common.erl
@@ -0,0 +1,126 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(asn1rtt_per_common).
+
+-include("asn1_records.hrl").
+
+-export([decode_fragmented/3,
+ decode_compact_bit_string/1,
+ decode_legacy_bit_string/1,
+ decode_named_bit_string/2,
+ decode_chars/2,decode_chars/3,
+ decode_chars_16bit/1,
+ decode_big_chars/2,
+ decode_oid/1,decode_relative_oid/1]).
+
+-define('16K',16384).
+
+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).
+
+decode_named_bit_string(Val, NNL) ->
+ Bits = [B || <<B:1>> <= Val],
+ decode_named_bit_string_1(0, Bits, NNL, []).
+
+decode_legacy_bit_string(Val) ->
+ [B || <<B:1>> <= Val].
+
+decode_compact_bit_string(Val) ->
+ PadLen = (8 - (bit_size(Val) band 7)) band 7,
+ {PadLen,<<Val/bitstring,0:PadLen>>}.
+
+decode_chars(Val, N) ->
+ [C || <<C:N>> <= Val].
+
+decode_chars(Val, N, Chars) ->
+ [element(C+1, Chars) || <<C:N>> <= Val].
+
+decode_chars_16bit(Val) ->
+ Cs = [C || <<C:16>> <= Val],
+ decode_chars_16bit_1(Cs).
+
+decode_big_chars(Val, N) ->
+ decode_big_chars_1(decode_chars(Val, N)).
+
+decode_oid(Octets) ->
+ [First|Rest] = dec_subidentifiers(Octets, 0, []),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ list_to_tuple(Idlist).
+
+decode_relative_oid(Octets) ->
+ list_to_tuple(dec_subidentifiers(Octets, 0, [])).
+
+%%%
+%%% Internal functions.
+%%%
+
+decode_named_bit_string_1(Pos, [0|Bt], Names, Acc) ->
+ decode_named_bit_string_1(Pos+1, Bt, Names, Acc);
+decode_named_bit_string_1(Pos, [1|Bt], Names, Acc) ->
+ case lists:keyfind(Pos, 2, Names) of
+ {Name,_} ->
+ decode_named_bit_string_1(Pos+1, Bt, Names, [Name|Acc]);
+ false ->
+ decode_named_bit_string_1(Pos+1, Bt, Names, [{bit,Pos}|Acc])
+ end;
+decode_named_bit_string_1(_Pos, [], _Names, Acc) ->
+ lists:reverse(Acc).
+
+decode_chars_16bit_1([H|T]) when H < 256 ->
+ [H|decode_chars_16bit_1(T)];
+decode_chars_16bit_1([H|T]) ->
+ [{0,0,H bsr 8,H band 255}|decode_chars_16bit_1(T)];
+decode_chars_16bit_1([]) -> [].
+
+decode_big_chars_1([H|T]) when H < 256 ->
+ [H|decode_big_chars_1(T)];
+decode_big_chars_1([H|T]) ->
+ [list_to_tuple(binary_to_list(<<H:32>>))|decode_big_chars_1(T)];
+decode_big_chars_1([]) -> [].
+
+dec_subidentifiers([H|T], Av, Al) when H >=16#80 ->
+ dec_subidentifiers(T, (Av bsl 7) bor (H band 16#7F), Al);
+dec_subidentifiers([H|T], Av, Al) ->
+ dec_subidentifiers(T, 0, [(Av bsl 7) bor H|Al]);
+dec_subidentifiers([], _Av, Al) ->
+ lists:reverse(Al).
diff --git a/lib/asn1/src/asn1rtt_real_common.erl b/lib/asn1/src/asn1rtt_real_common.erl
new file mode 100644
index 0000000000..22a1f4c4dd
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_real_common.erl
@@ -0,0 +1,292 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(asn1rtt_real_common).
+
+-export([encode_real/1,decode_real/1,
+ ber_encode_real/1]).
+
+%%============================================================================
+%%
+%% Real value, ITU_T X.690 Chapter 8.5
+%%============================================================================
+%%
+%% encode real value
+%%============================================================================
+
+ber_encode_real(0) ->
+ {[],0};
+ber_encode_real('PLUS-INFINITY') ->
+ {[64],1};
+ber_encode_real('MINUS-INFINITY') ->
+ {[65],1};
+ber_encode_real(Val) when is_tuple(Val); is_list(Val) ->
+ encode_real(Val).
+
+%%%%%%%%%%%%%%
+%% only base 2 encoding!
+%% binary encoding:
+%% +------------+ +------------+ +-+-+-+-+---+---+
+%% | (tag)9 | | n + p + 1 | |1|S|BB |FF |EE |
+%% +------------+ +------------+ +-+-+-+-+---+---+
+%%
+%% +------------+ +------------+
+%% | | | |
+%% +------------+ ...+------------+
+%% n octets for exponent
+%%
+%% +------------+ +------------+
+%% | | | |
+%% +------------+ ...+------------+
+%% p octets for pos mantissa
+%%
+%% S is 0 for positive sign
+%% 1 for negative sign
+%% BB: encoding base, 00 = 2, (01 = 8, 10 = 16)
+%% 01 and 10 not used
+%% FF: scale factor 00 = 0 (used in base 2 encoding)
+%% EE: encoding of the exponent:
+%% 00 - on the following octet
+%% 01 - on the 2 following octets
+%% 10 - on the 3 following octets
+%% 11 - encoding of the length of the two's-complement encoding of
+%% exponent on the following octet, and two's-complement
+%% encoding of exponent on the other octets.
+%%
+%% In DER and base 2 encoding the mantissa is encoded as value 0 or
+%% bit shifted until it is an odd number. Thus, do this for BER as
+%% well.
+
+encode_real(Real) ->
+ encode_real([], Real).
+
+encode_real(_C, {Mantissa, Base, Exponent}) when Base =:= 2 ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+ {Man,ExpAdd} = truncate_zeros(Mantissa), %% DER adjustment
+ Exp = Exponent + ExpAdd,
+ OctExp = if Exp >= 0 -> list_to_binary(encode_pos_integer(Exp, []));
+ true -> list_to_binary(encode_neg_integer(Exp, []))
+ end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+ SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval
+ true -> 1
+ end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+ SFactor = 0,
+ OctExpLen = byte_size(OctExp),
+ if OctExpLen > 255 ->
+ exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+ true -> true %% make real assert later..
+ end,
+ {LenCode, EOctets} = case OctExpLen of % bit 2,1
+ 1 -> {0, OctExp};
+ 2 -> {1, OctExp};
+ 3 -> {2, OctExp};
+ _ -> {3, <<OctExpLen, OctExp/binary>>}
+ end,
+ BB = 0, %% 00 for base 2
+ FirstOctet = <<1:1,SignBit:1,BB:2,SFactor:2,LenCode:2>>,
+ OctMantissa = if Man > 0 -> list_to_binary(real_mininum_octets(Man));
+ true -> list_to_binary(real_mininum_octets(-(Man))) % signbit keeps track of sign
+ end,
+ %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+ Bin = <<FirstOctet/binary, EOctets/binary, OctMantissa/binary>>,
+ {Bin, size(Bin)};
+encode_real(C, {Mantissa,Base,Exponent})
+ when Base =:= 10, is_integer(Mantissa), is_integer(Exponent) ->
+ %% always encode as NR3 due to DER on the format
+ %% mmmm.Eseeee where
+ %% m := digit
+ %% s := '-' | '+' | []
+ %% '+' only allowed in +0
+ %% e := digit
+ %% ex: 1234.E-5679
+ ManStr = integer_to_list(Mantissa),
+
+ encode_real_as_string(C,ManStr,Exponent);
+encode_real(_C, {_,Base,_}) ->
+ exit({error,{asn1, {encode_real_non_supported_encoding, Base}}});
+%% base 10
+encode_real(C, Real) when is_list(Real) ->
+ %% The Real string may come in as a NR1, NR2 or NR3 string.
+ {Mantissa, Exponent} =
+ case string:tokens(Real,"Ee") of
+ [NR2] ->
+ {NR2,0};
+ [NR3MB,NR3E] ->
+ %% remove beginning zeros
+ {NR3MB,list_to_integer(NR3E)}
+ end,
+
+ %% .Decimal | Number | Number.Decimal
+ ZeroDecimal =
+ fun("0") -> "";
+ (L) -> L
+ end,
+ {NewMantissa,LenDecimal} =
+ case Mantissa of
+ [$.|Dec] ->
+ NewMan = remove_trailing_zeros(Dec),
+ {NewMan,length(ZeroDecimal(NewMan))};
+ _ ->
+ case string:tokens(Mantissa,",.") of
+ [Num] -> %% No decimal-mark
+ {integer_to_list(list_to_integer(Num)),0};
+ [Num,Dec] ->
+ NewDec = ZeroDecimal(remove_trailing_zeros(Dec)),
+ NewMan = integer_to_list(list_to_integer(Num)) ++ NewDec,
+ {integer_to_list(list_to_integer(NewMan)),
+ length(NewDec)}
+ end
+ end,
+
+ encode_real_as_string(C, NewMantissa, Exponent - LenDecimal).
+
+encode_real_as_string(_C, Mantissa, Exponent)
+ when is_list(Mantissa), is_integer(Exponent) ->
+ %% Remove trailing zeros in Mantissa and add this to Exponent
+ TruncMant = remove_trailing_zeros(Mantissa),
+
+ ExpIncr = length(Mantissa) - length(TruncMant),
+
+ ExpStr = integer_to_list(Exponent + ExpIncr),
+
+ ExpBin =
+ case ExpStr of
+ "0" ->
+ <<"E+0">>;
+ _ ->
+ ExpB = list_to_binary(ExpStr),
+ <<$E,ExpB/binary>>
+ end,
+ ManBin = list_to_binary(TruncMant),
+ NR3 = 3,
+ {<<NR3,ManBin/binary,$.,ExpBin/binary>>,
+ 2 + byte_size(ManBin) + byte_size(ExpBin)}.
+
+remove_trailing_zeros(IntStr) ->
+ case lists:dropwhile(fun($0)-> true;
+ (_) -> false
+ end, lists:reverse(IntStr)) of
+ [] ->
+ "0";
+ ReversedIntStr ->
+ lists:reverse(ReversedIntStr)
+ end.
+
+truncate_zeros(Num) ->
+ truncate_zeros(Num, 0).
+truncate_zeros(0, Sum) ->
+ {0,Sum};
+truncate_zeros(M, Sum) ->
+ case M band 16#f =:= M band 16#e of
+ true -> truncate_zeros(M bsr 1, Sum+1);
+ _ -> {M,Sum}
+ end.
+
+
+%%============================================================================
+%% decode real value
+%%
+%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
+%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
+%% RestBuff}
+%%
+%% only for base 2 decoding sofar!!
+%%============================================================================
+
+decode_real(Buffer) ->
+ Sz = byte_size(Buffer),
+ {RealVal,<<>>,Sz} = decode_real2(Buffer, [], Sz, 0),
+ RealVal.
+
+decode_real2(Buffer, _C, 0, _RemBytes) ->
+ {0,Buffer};
+decode_real2(Buffer0, _C, Len, RemBytes1) ->
+ <<First, Buffer2/binary>> = Buffer0,
+ if
+ First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
+ First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
+ First =:= 1 orelse First =:= 2 orelse First =:= 3 ->
+ %% charcter string encoding of base 10
+ {NRx,Rest} = split_binary(Buffer2,Len-1),
+ {binary_to_list(NRx),Rest,Len};
+ true ->
+ %% have some check here to verify only supported bases (2)
+ %% not base 8 or 16
+ <<_B7:1,Sign:1,BB:2,_FF:2,EE:2>> = <<First>>,
+ Base =
+ case BB of
+ 0 -> 2; % base 2, only one so far
+ _ -> exit({error,{asn1, {non_supported_base, BB}}})
+ end,
+ {FirstLen, {Exp, Buffer3,_Rb2}, RemBytes2} =
+ case EE of
+ 0 -> {2, decode_integer2(1, Buffer2, RemBytes1), RemBytes1+1};
+ 1 -> {3, decode_integer2(2, Buffer2, RemBytes1), RemBytes1+2};
+ 2 -> {4, decode_integer2(3, Buffer2, RemBytes1), RemBytes1+3};
+ 3 ->
+ <<ExpLen1,RestBuffer/binary>> = Buffer2,
+ { ExpLen1 + 2,
+ decode_integer2(ExpLen1, RestBuffer, RemBytes1),
+ RemBytes1+ExpLen1}
+ end,
+ %% io:format("FirstLen: ~w, Exp: ~w, Buffer3: ~w ~n",
+
+ Length = Len - FirstLen,
+ <<LongInt:Length/unit:8,RestBuff/binary>> = Buffer3,
+ {{Mantissa, Buffer4}, RemBytes3} =
+ if Sign =:= 0 ->
+ %% io:format("sign plus~n"),
+ {{LongInt, RestBuff}, 1 + Length};
+ true ->
+ %% io:format("sign minus~n"),
+ {{-LongInt, RestBuff}, 1 + Length}
+ end,
+ {{Mantissa, Base, Exp}, Buffer4, RemBytes2+RemBytes3}
+ end.
+
+encode_pos_integer(0, [B|_Acc]=L) when B < 128 ->
+ L;
+encode_pos_integer(N, Acc) ->
+ encode_pos_integer(N bsr 8, [N band 16#ff| Acc]).
+
+encode_neg_integer(-1, [B1|_T]=L) when B1 > 127 ->
+ L;
+encode_neg_integer(N, Acc) ->
+ encode_neg_integer(N bsr 8, [N band 16#ff|Acc]).
+
+
+%% Val must be >= 0
+real_mininum_octets(Val) ->
+ real_mininum_octets(Val, []).
+
+real_mininum_octets(0, Acc) ->
+ Acc;
+real_mininum_octets(Val, Acc) ->
+ real_mininum_octets(Val bsr 8, [Val band 16#FF | Acc]).
+
+%% decoding postitive integer values.
+decode_integer2(Len, <<0:1,_:7,_Bs/binary>> = Bin, RemovedBytes) ->
+ <<Int:Len/unit:8,Buffer2/binary>> = Bin,
+ {Int,Buffer2,RemovedBytes};
+%% decoding negative integer values.
+decode_integer2(Len, <<1:1,B2:7,Bs/binary>>, RemovedBytes) ->
+ <<N:Len/unit:8,Buffer2/binary>> = <<B2,Bs/binary>>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ {Int,Buffer2,RemovedBytes}.
diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl
new file mode 100644
index 0000000000..8efe9a7b0f
--- /dev/null
+++ b/lib/asn1/src/asn1rtt_uper.erl
@@ -0,0 +1,1045 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(asn1rtt_uper).
+
+-export([setext/1, fixoptionals/3,
+ fixextensions/2,
+ skipextensions/3, getbit/1, getchoice/3 ]).
+-export([set_choice/3, encode_integer/2, encode_integer/3]).
+-export([encode_small_number/1, encode_constrained_number/2,
+ encode_boolean/1,
+ encode_length/1, encode_length/2,
+ encode_bit_string/3]).
+-export([encode_octet_string/1,encode_octet_string/2,
+ encode_relative_oid/1,
+ encode_object_identifier/1,
+ complete/1, complete_NFP/1]).
+
+ -export([encode_open_type/1]).
+
+ -export([encode_UniversalString/2,
+ encode_PrintableString/2,
+ encode_GeneralString/2,
+ encode_GraphicString/2,
+ encode_TeletexString/2,
+ encode_VideotexString/2,
+ encode_VisibleString/2,
+ encode_UTF8String/1,
+ encode_BMPString/2,
+ encode_IA5String/2,
+ encode_NumericString/2,
+ encode_ObjectDescriptor/2
+ ]).
+
+-define('16K',16384).
+-define('32K',32768).
+-define('64K',65536).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(false) ->
+ <<0:1>>;
+setext(true) ->
+ <<1:1>>.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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>>};
+
+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.
+
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum),<<ExtBits:ExtNum>>]
+ 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(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) ->
+ Prev = Nr - 1,
+ case ExtensionBitstr of
+ <<_:Prev,1:1,_/bitstring>> ->
+ {Len,Bytes1} = decode_length(Bytes0),
+ <<_:Len/binary,Bytes2/bitstring>> = Bytes1,
+ skipextensions(Bytes2, Nr+1, ExtensionBitstr);
+ <<_:Prev,0:1,_/bitstring>> ->
+ skipextensions(Bytes0, Nr+1, ExtensionBitstr);
+ _ ->
+ Bytes0
+ 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}).
+
+
+getbit(Buffer) ->
+ <<B:1,Rest/bitstring>> = Buffer,
+ {B,Rest}.
+
+getbits(Buffer, Num) when is_bitstring(Buffer) ->
+ <<Bs:Num,Rest/bitstring>> = Buffer,
+ {Bs,Rest}.
+
+
+%% Pick the first Num octets.
+%% Returns octets as an integer with bit significance as in buffer.
+getoctets(Buffer, Num) when is_bitstring(Buffer) ->
+ <<Val:Num/integer-unit:8,RestBitStr/bitstring>> = Buffer,
+ {Val,RestBitStr}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 ->
+ [<<0:1>>, % the value is in the root set
+ encode_integer([{'ValueRange',{0,Len1-1}}],N)];
+ N when is_integer(N) ->
+ <<0:1>>; % no encoding if only 0 or 1 alternative
+ false ->
+ [<<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.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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(Val) when is_list(Val) ->
+ encode_open_type(list_to_binary(Val));
+encode_open_type(Val) when is_binary(Val) ->
+ [encode_length(byte_size(Val)),Val].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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:keyfind(V, 1, NamedNumberList) of
+ {_,NewV} ->
+ encode_integer(C, NewV);
+ false ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C, V, _NamedNumberList) when is_integer(V) ->
+ encode_integer(C, V).
+
+encode_integer([{Rc,_Ec}],Val) when is_tuple(Rc) ->
+ try
+ [<<0:1>>,encode_integer([Rc], Val)]
+ catch
+ _:{error,{asn1,_}} ->
+ [<<1:1>>,encode_unconstrained_number(Val)]
+ 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.
+
+%% 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(Val) when Val < 64 ->
+ <<Val:7>>;
+encode_small_number(Val) ->
+ [<<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)
+ end.
+
+%% X.691:10.7 Encoding of a semi-constrained whole number
+encode_semi_constrained_number(Lb, Val) ->
+ %% encoding in minimum number of octets preceeded by a length
+ Val2 = Val - Lb,
+ Bin = eint_bin_positive(Val2),
+ Size = byte_size(Bin),
+ if
+ Size < 128 ->
+ [<<Size>>,Bin];
+ Size < 16384 ->
+ [<<2:2,Size:14>>,Bin];
+ true ->
+ [encode_length(Size),Bin]
+ end.
+
+decode_semi_constrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V,Bytes3}.
+
+encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ NumBits = num_bits(Range),
+ <<Val2:NumBits>>;
+encode_constrained_number(Range,Val) ->
+ exit({error,{asn1,{integer_range,Range,value,Val}}}).
+
+
+decode_constrained_number(Buffer, {Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+ NumBits = num_bits(Range),
+ {Val,Remain} = getbits(Buffer,NumBits),
+ {Val+Lb,Remain}.
+
+%% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint_bin_2Cs(Val),
+ Len = byte_size(Oct),
+ if
+ Len < 128 ->
+ [<<Len>>,Oct]; % equiv with encode_length(undefined,Len) but faster
+ Len < 16384 ->
+ [<<2:2,Len:14>>,Oct];
+ true ->
+ [encode_length(Len),<<Len:16>>,Oct]
+ end;
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ Len = byte_size(Oct),
+ if
+ Len < 128 ->
+ [<<Len>>,Oct]; % equiv with encode_length(undefined,Len) but faster
+ Len < 16384 ->
+ [<<2:2,Len:14>>,Oct];
+ true ->
+ [encode_length(Len),Oct]
+ end.
+
+
+eint_bin_2Cs(Int) ->
+ case eint_bin_positive(Int) of
+ <<B,_/binary>> = Bin when B > 16#7f ->
+ <<0,Bin/binary>>;
+ Bin -> Bin
+ end.
+
+%% returns the integer as a binary
+eint_bin_positive(Val) when Val < 16#100 ->
+ <<Val>>;
+eint_bin_positive(Val) when Val < 16#10000 ->
+ <<Val:16>>;
+eint_bin_positive(Val) when Val < 16#1000000 ->
+ <<Val:24>>;
+eint_bin_positive(Val) when Val < 16#100000000 ->
+ <<Val:32>>;
+eint_bin_positive(Val) ->
+ list_to_binary([eint_bin_positive2(Val bsr 32),<<Val:32>>]).
+
+eint_bin_positive2(Val) when Val < 16#100 ->
+ <<Val>>;
+eint_bin_positive2(Val) when Val < 16#10000 ->
+ <<Val:16>>;
+eint_bin_positive2(Val) when Val < 16#1000000 ->
+ <<Val:24>>;
+eint_bin_positive2(Val) when Val < 16#100000000 ->
+ <<Val:32>>;
+eint_bin_positive2(Val) ->
+ [eint_bin_positive2(Val bsr 32),<<Val:32>>].
+
+
+
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ list_to_binary([B1|T]);
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|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(Len) -> % un-constrained
+ if
+ Len < 128 ->
+ <<Len>>;
+ Len < 16384 ->
+ <<2:2,Len:14>>;
+ true -> % should be able to endode length >= 16384
+ error({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end.
+
+encode_length(undefined, Len) -> % unconstrained
+ encode_length(Len);
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined, Len);
+encode_length({Lb,Ub}=Vr, 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(Len);
+encode_length({{Lb,Ub}=Vr,Ext},Len)
+ when Ub =< 65535, Lb >= 0, Len =< Ub, is_list(Ext) ->
+ %% constrained extensible
+ [<<0:1>>,encode_constrained_number(Vr,Len)];
+encode_length({{Lb,_Ub},Ext}, Len) when is_list(Ext) ->
+ [<<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 ->
+ <<(Len-1):7>>;
+encode_small_length(Len) ->
+ [<<1:1>>,encode_length(Len)].
+
+
+%% un-constrained
+decode_length(<<0:1,Oct:7,Rest/bitstring>>) ->
+ {Oct,Rest};
+decode_length(<<2:2,Val:14,Rest/bitstring>>) ->
+ {Val,Rest};
+decode_length(<<3:2,_:14,_Rest/bitstring>>) ->
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}}).
+
+ % X.691:11
+encode_boolean(true) ->
+ <<1:1>>;
+encode_boolean(false) ->
+ <<0:1>>;
+encode_boolean(Val) ->
+ exit({error,{asn1,{encode_boolean,Val}}}).
+
+
+%%============================================================================
+%%============================================================================
+%% 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 are present
+
+
+%% when the value is a list of {Unused,BinBits}, where
+%% Unused = integer(),
+%% BinBits = binary().
+
+encode_bit_string(C, Bits, NamedBitList) when is_bitstring(Bits) ->
+ PadLen = (8 - (bit_size(Bits) band 7)) band 7,
+ Compact = {PadLen,<<Bits/bitstring,0:PadLen>>},
+ encode_bit_string(C, Compact, NamedBitList);
+encode_bit_string(C, {Unused,BinBits}=Bin, NamedBitList)
+ when is_integer(Unused), is_binary(BinBits) ->
+ encode_bin_bit_string(C, Bin, NamedBitList);
+
+encode_bit_string(C, BitListVal, NamedBitList) ->
+ encode_bit_string1(C, BitListVal, NamedBitList).
+
+%% when the value is a list of named bits
+encode_bit_string1(C, [FirstVal|_RestVal]=LoNB, NamedBitList)
+ when is_atom(FirstVal) ->
+ ToSetPos = get_all_bitposes(LoNB, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos, 0),
+ encode_bit_string1(C, BitList, NamedBitList);
+encode_bit_string1(C, [{bit,_No}|_RestVal]=BL, NamedBitList) ->
+ ToSetPos = get_all_bitposes(BL, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos, 0),
+ encode_bit_string1(C, BitList, NamedBitList);
+%% when the value is a list of ones and zeroes
+encode_bit_string1(Int, BitListValue, _)
+ when is_list(BitListValue), is_integer(Int) ->
+ %% The type is constrained by a single value size constraint
+ bit_list2bitstr(Int, BitListValue);
+encode_bit_string1(no, BitListValue, [])
+ when is_list(BitListValue) ->
+ Len = length(BitListValue),
+ [encode_length(Len),bit_list2bitstr(Len,BitListValue)];
+encode_bit_string1(C, BitListValue,[])
+ when is_list(BitListValue) ->
+ Len = length(BitListValue),
+ [encode_length(C, Len),bit_list2bitstr(Len,BitListValue)];
+encode_bit_string1(no, BitListValue,_NamedBitList)
+ when is_list(BitListValue) ->
+ NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue))),
+ Len = length(NewBitLVal),
+ [encode_length(Len),bit_list2bitstr(Len,NewBitLVal)];
+encode_bit_string1(C, BitListValue, _NamedBitList)
+ when is_list(BitListValue) ->% C = {_,'MAX'}
+ NewBitStr = bitstr_trailing_zeros(BitListValue, C),
+ [encode_length(C, bit_size(NewBitStr)),NewBitStr];
+
+
+%% when the value is an integer
+encode_bit_string1(C, IntegerVal, NamedBitList) when is_integer(IntegerVal)->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string1(C, BitList, NamedBitList).
+
+bit_list2bitstr(Len,BitListValue) ->
+ case length(BitListValue) of
+ Len ->
+ << <<B:1>> || B <- BitListValue>>;
+ L when L > Len -> % truncate
+ <<(<< <<B:1>> || B <- BitListValue>>):Len/bitstring>>;
+ L -> % Len > L -> pad
+ <<(<< <<B:1>> || B <- BitListValue>>)/bitstring,0:(Len-L)>>
+ end.
+
+adjust_trailing_zeros(Len, Bin) when Len =:= bit_size(Bin) ->
+ Bin;
+adjust_trailing_zeros(Len, Bin) when Len > bit_size(Bin) ->
+ <<Bin/bitstring,0:(Len-bit_size(Bin))>>;
+adjust_trailing_zeros(Len,Bin) ->
+ <<Bin:Len/bitstring>>.
+
+bitstr_trailing_zeros(BitList, C) when is_integer(C) ->
+ bitstr_trailing_zeros1(BitList, C, C);
+bitstr_trailing_zeros(BitList, {Lb,Ub}) when is_integer(Lb) ->
+ bitstr_trailing_zeros1(BitList,Lb,Ub);
+bitstr_trailing_zeros(BitList, {{Lb,Ub},_}) when is_integer(Lb) ->
+ bitstr_trailing_zeros1(BitList, Lb, Ub);
+bitstr_trailing_zeros(BitList, _) ->
+ bit_list2bitstr(length(BitList), BitList).
+
+bitstr_trailing_zeros1(BitList, Lb, Ub) ->
+ case length(BitList) of
+ Lb -> bit_list2bitstr(Lb, BitList);
+ B when B < Lb -> bit_list2bitstr(Lb, BitList);
+ D -> F = fun(L,LB,LB,_,_)->bit_list2bitstr(LB,lists:reverse(L));
+ ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun);
+ (L,L1,_,UB,_)when L1 =< UB ->
+ bit_list2bitstr(L1,lists:reverse(L));
+ (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING,
+ BitList}}) end,
+ F(lists:reverse(BitList),D,Lb,Ub,F)
+ end.
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),i.e. number unused bits in least sign. byte of
+%% BinBits = binary().
+encode_bin_bit_string(C, {_,BinBits}, _NamedBitList)
+ when is_integer(C), C =< 16 ->
+ adjust_trailing_zeros(C, BinBits);
+encode_bin_bit_string(C, {_Unused,BinBits}, _NamedBitList)
+ when is_integer(C) ->
+ adjust_trailing_zeros(C, BinBits);
+encode_bin_bit_string(C, {_,_}=UnusedAndBin, NamedBitList) ->
+ %% removes all trailing bits if NamedBitList is not empty
+ BitStr = remove_trailing_bin(NamedBitList, UnusedAndBin),
+ case C of
+ {Lb,Ub} when is_integer(Lb),is_integer(Ub) ->
+ [encode_length({Lb,Ub},bit_size(BitStr)),BitStr];
+ no ->
+ [encode_length(bit_size(BitStr)),BitStr];
+ Sc ->
+ [encode_length(Sc,bit_size(BitStr)),BitStr]
+ end.
+
+
+remove_trailing_bin([], {Unused,Bin}) ->
+ BS = bit_size(Bin)-Unused,
+ <<BitStr:BS/bitstring,_:Unused>> = Bin,
+ BitStr;
+remove_trailing_bin(_NamedNumberList, {_Unused,<<>>}) ->
+ <<>>;
+remove_trailing_bin(NamedNumberList, {_Unused,Bin}) ->
+ Size = byte_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});
+ _ ->
+ BS = bit_size(Bin) - Unused2,
+ <<BitStr:BS/bitstring,_:Unused2>> = Bin,
+ BitStr
+ 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.
+
+
+%%%%%%%%%%%%%%%
+%%
+
+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:keyfind(Val, 1, NamedBitList) of
+ {_ValName, ValPos} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ false ->
+ 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([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Val)
+%% encode_octet_string(Constraint, Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(Val) ->
+ try
+ [encode_length(length(Val)),list_to_binary(Val)]
+ catch
+ error:{error,{asn1,{encode_length,_}}} ->
+ encode_fragmented_octet_string(Val)
+ end.
+
+encode_octet_string(C, Val) ->
+ case C of
+ 1 ->
+ list_to_binary(Val);
+ 2 ->
+ 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) ->
+ 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
+ end.
+
+
+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(Len),B].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string('BMPString',Constraints,Extension,Val)
+
+
+encode_restricted_string(Val) when is_list(Val)->
+ [encode_length(length(Val)),list_to_binary(Val)].
+
+encode_known_multiplier_string(StringType, C, 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
+ Result;
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb}, length(Val)),Result];
+ Vl when is_list(Vl) ->
+ [encode_length({lists:min(Vl),lists:max(Vl)}, length(Val)),Result];
+ no ->
+ [encode_length(length(Val)),Result]
+ end.
+
+encode_NumericString(C,Val) ->
+ encode_known_multiplier_string('NumericString',C,Val).
+
+encode_PrintableString(C,Val) ->
+ encode_known_multiplier_string('PrintableString',C,Val).
+
+encode_VisibleString(C,Val) -> % equivalent with ISO646String
+ encode_known_multiplier_string('VisibleString',C,Val).
+
+encode_IA5String(C,Val) ->
+ encode_known_multiplier_string('IA5String',C,Val).
+
+encode_BMPString(C,Val) ->
+ encode_known_multiplier_string('BMPString',C,Val).
+
+encode_UniversalString(C,Val) ->
+ encode_known_multiplier_string('UniversalString',C,Val).
+
+
+%% end of known-multiplier strings for which PER visible constraints are
+%% applied
+
+encode_GeneralString(_C,Val) ->
+ encode_restricted_string(Val).
+
+encode_GraphicString(_C,Val) ->
+ encode_restricted_string(Val).
+
+encode_ObjectDescriptor(_C,Val) ->
+ encode_restricted_string(Val).
+
+encode_TeletexString(_C,Val) -> % equivalent with T61String
+ encode_restricted_string(Val).
+
+encode_VideotexString(_C,Val) ->
+ encode_restricted_string(Val).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 ->
+ [<<(H-Min):NumBits>>|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min ->
+ Ch = exit_if_false(H,element(H-Min+1,Tab)),
+ [<<Ch:NumBits>>|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)
+ Ch = ((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,
+ [<<Ch:NumBits>>|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)
+ Ch = exit_if_false({A,B,C,D},element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)),
+ [<<Ch:NumBits>>|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));
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+get_CharOutTab(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789");
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ {Min,Max,create_char_tab(Min,Chars)}
+ 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)].
+
+%% See Table 20.3 in Dubuisson
+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).
+
+
+%% UTF8String
+encode_UTF8String(Val) when is_binary(Val) ->
+ [encode_length(byte_size(Val)),Val];
+encode_UTF8String(Val) ->
+ Bin = list_to_binary(Val),
+ encode_UTF8String(Bin).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% 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 -> [binary()|bitstring()|list()]
+%%
+encode_object_identifier(Val) ->
+ OctetList = e_object_identifier(Val),
+ Octets = list_to_binary(OctetList), % performs a flatten at the same time
+ [encode_length(byte_size(Octets)),Octets].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V}) ->
+ e_object_identifier(V);
+e_object_identifier(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]].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_relative_oid(Val) -> CompleteList
+%% encode_relative_oid({Name,Val}) -> CompleteList
+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(byte_size(Octets)),Octets].
+
+
+get_constraint([{Key,V}],Key) ->
+ V;
+get_constraint([],_Key) ->
+ no;
+get_constraint(C,Key) ->
+ case lists:keyfind(Key, 1, C) of
+ false ->
+ no;
+ {_,V} ->
+ V
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+complete(InList) when is_list(InList) ->
+ case complete1(InList) of
+ <<>> ->
+ <<0>>;
+ Res ->
+ case bit_size(Res) band 7 of
+ 0 -> Res;
+ Bits -> <<Res/bitstring,0:(8-Bits)>>
+ end
+ end;
+complete(Bin) when is_binary(Bin) ->
+ case Bin of
+ <<>> -> <<0>>;
+ _ -> Bin
+ end;
+complete(InList) when is_bitstring(InList) ->
+ PadLen = 8 - (bit_size(InList) band 7),
+ <<InList/bitstring,0:PadLen>>.
+
+complete1(L) when is_list(L) ->
+ list_to_bitstring(L).
+
+%% Special version of complete that does not align the completed message.
+complete_NFP(InList) when is_list(InList) ->
+ list_to_bitstring(InList);
+complete_NFP(InList) when is_bitstring(InList) ->
+ InList.
+
+%% unaligned helpers
+
+%% 10.5.6 NOTE: If "range" satisfies the inequality 2^m < "range" =<
+%% 2^(m+1) then the number of bits = m + 1
+
+num_bits(N) -> num_bits(N, 1, 0).
+
+num_bits(N,T,B) when N =< T -> B;
+num_bits(N,T,B) -> num_bits(N, T bsl 1, B+1).
diff --git a/lib/asn1/src/prepare_templates.erl b/lib/asn1/src/prepare_templates.erl
new file mode 100644
index 0000000000..83155b2e52
--- /dev/null
+++ b/lib/asn1/src/prepare_templates.erl
@@ -0,0 +1,135 @@
+%%
+%% %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(prepare_templates).
+-export([gen_asn1ct_rtt/1,gen_asn1ct_eval/1]).
+
+gen_asn1ct_rtt(Ms) ->
+ io:format("%% Generated by ~s. DO NOT EDIT THIS FILE.\n"
+ "%%\n"
+ "%% Input files:\n", [?MODULE]),
+ [io:put_chars(["%% ",M,$\n]) || M <- Ms],
+ io:nl(),
+ io:put_chars("-module(asn1ct_rtt).\n"
+ "-export([assert_defined/1,dependencies/1,code/0]).\n"
+ "\n"),
+ Forms = lists:sort(lists:append([abstract(M) || M <- Ms])),
+ Exp = lists:sort(exports(Forms)),
+ defined(Exp),
+ io:nl(),
+ Calls = calls(Forms),
+ R = sofs:relation(Calls),
+ Fam0 = sofs:relation_to_family(R),
+ Fam = sofs:to_external(Fam0),
+ dependencies(Fam),
+ io:nl(),
+ Funcs = [begin
+ Bin = list_to_binary([$\n|erl_pp:function(Func)]),
+ {{M,F,A},Bin}
+ end || {M,{function,_,F,A,_}=Func} <- Forms],
+ io:format("code() ->\n~p.\n\n", [Funcs]),
+ halt(0).
+
+gen_asn1ct_eval([File]) ->
+ {ok,Funcs} = file:consult(File),
+ asn1ct_func:start_link(),
+ [asn1ct_func:need(MFA) || MFA <- Funcs],
+ io:format("%% Generated by ~s. DO NOT EDIT THIS FILE.\n"
+ "%%\n"
+ "%% Input file: ~s\n\n", [?MODULE,File]),
+ io:format("-module(~s).\n", [filename:rootname(File)]),
+ gen_asn1ct_eval_exp(Funcs),
+ asn1ct_func:generate(group_leader()),
+ halt(0).
+
+gen_asn1ct_eval_exp(Funcs) ->
+ io:put_chars("-export(["),
+ gen_asn1ct_eval_exp_1(Funcs, ""),
+ io:put_chars("]).\n").
+
+gen_asn1ct_eval_exp_1([{_,F,A}|T], Sep) ->
+ io:put_chars(Sep),
+ io:format("~p/~p", [F,A]),
+ gen_asn1ct_eval_exp_1(T, ",\n");
+gen_asn1ct_eval_exp_1([], _) -> ok.
+
+defined([H|T]) ->
+ io:format("assert_defined(~p) -> ok", [H]),
+ case T of
+ [] ->
+ io:put_chars(".\n");
+ [_|_] ->
+ io:put_chars(";\n"),
+ defined(T)
+ end.
+
+dependencies([{K,V}|T]) ->
+ io:format("dependencies(~p) ->\n~p;\n", [K,V]),
+ dependencies(T);
+dependencies([]) ->
+ io:put_chars("dependencies(_) -> [].\n").
+
+abstract(File) ->
+ {ok,{M0,[{abstract_code,Abstract}]}} =
+ beam_lib:chunks(File, [abstract_code]),
+ {raw_abstract_v1,Forms} = Abstract,
+ M = module(M0),
+ [{M,F} || F <- Forms].
+
+module(M0) ->
+ "asn1rtt_" ++ M = atom_to_list(M0),
+ list_to_atom(M).
+
+exports([{M,{attribute,_,export,L}}|T]) ->
+ [{M,F,A} || {F,A} <- L] ++ exports(T);
+exports([_|T]) ->
+ exports(T);
+exports([]) -> [].
+
+calls([{M,{function,_,F,A,Body}}|T]) ->
+ MFA = {M,F,A},
+ case find_calls(Body, M) -- [MFA] of
+ [] ->
+ calls(T);
+ [_|_]=Calls ->
+ [{MFA,Callee} || Callee <- Calls] ++ calls(T)
+ end;
+calls([_|T]) ->
+ calls(T);
+calls([]) -> [].
+
+find_calls([{call,_,{atom,_,F},Args}|T], M) ->
+ Calls = find_calls(Args, M) ++ find_calls(T, M),
+ Arity = length(Args),
+ case is_bif(F, Arity) of
+ false ->
+ [{M,F,Arity}|Calls];
+ true ->
+ Calls
+ end;
+find_calls([{'fun',_,{function,F,A}}|T], M) ->
+ [{M,F,A}|find_calls(T, M)];
+find_calls([H|T], M) ->
+ find_calls(H, M) ++ find_calls(T, M);
+find_calls(Tuple, M) when is_tuple(Tuple) ->
+ find_calls(tuple_to_list(Tuple), M);
+find_calls(_, _) -> [].
+
+is_bif(F, Arity) ->
+ erl_internal:bif(F, Arity).
diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile
index 6e6374baf1..6e6ab4639c 100644
--- a/lib/asn1/test/Makefile
+++ b/lib/asn1/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -83,6 +83,7 @@ MODULES= \
testInfObj \
testParameterizedInfObj \
testMergeCompile \
+ testMultipleLevels \
testDeepTConstr \
testTimer \
testMegaco \
@@ -101,7 +102,6 @@ MODULES= \
test_driver_load \
testSelectionTypes \
test_undecoded_rest \
- test_inline \
testTcapsystem \
testNBAPsystem \
test_compile_options \
@@ -109,6 +109,7 @@ MODULES= \
test_modified_x420 \
testX420 \
test_x691 \
+ testWSParamClass \
asn1_test_lib \
asn1_app_test \
asn1_appup_test \
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 12569273bd..62418e554e 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -1,6 +1,7 @@
+%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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,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).
@@ -51,11 +33,11 @@
%% Suite definition
%%------------------------------------------------------------------------------
-suite() -> [{ct_hooks, [ts_install_cth]},
- {timetrap,{minutes,60}}].
+suite() -> [{ct_hooks, [ts_install_cth]}].
all() ->
- [{group, parallel},
+ [{group, compile},
+ {group, parallel},
{group, app_test},
{group, appup_test},
@@ -71,26 +53,25 @@ 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},
+ [cover,
{group, ber},
% Uses 'P-Record', 'Constraints', 'MEDIA-GATEWAY-CONTROL'...
{group, [], [parse,
test_driver_load,
test_undecoded_rest,
- test_inline,
specialized_decodes,
special_decode_performance,
testMegaco,
@@ -105,6 +86,7 @@ groups() ->
testInvokeMod,
per,
ber_other,
+ der,
h323test,
per_GeneralString]},
testChoPrim,
@@ -116,6 +98,7 @@ groups() ->
testChoTypeRefPrim,
testChoTypeRefSeq,
testChoTypeRefSet,
+ testMultipleLevels,
testDef,
testOpt,
testSeqDefault,
@@ -184,13 +167,13 @@ groups() ->
testINSTANCE_OF,
testTCAP,
test_ParamTypeInfObj,
- test_WS_ParamClass,
test_Defed_ObjectIdentifier,
testSelectionType,
testSSLspecs,
testNortel,
- % Uses 'PKCS7'
- {group, [], [test_modified_x420,
+ % Uses 'PKCS7', 'InformationFramework'
+ {group, [], [test_WS_ParamClass,
+ test_modified_x420,
testX420]},
testTcapsystem,
testNBAPsystem,
@@ -198,18 +181,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) ->
@@ -224,8 +201,6 @@ parallel(Options) ->
%%------------------------------------------------------------------------------
init_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
- true = code:add_patha(PrivDir),
Config.
end_per_suite(_Config) ->
@@ -238,11 +213,15 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(Func, Config) ->
- CaseDir = filename:join([?config(priv_dir, Config), ?MODULE, Func]),
+ CaseDir = filename:join(?config(priv_dir, Config), Func),
ok = filelib:ensure_dir(filename:join([CaseDir, dummy_file])),
true = code:add_patha(CaseDir),
- [{case_dir, CaseDir}|Config].
+ Dog = case Func of
+ testX420 -> ct:timetrap({minutes, 90});
+ _ -> ct:timetrap({minutes, 60})
+ end,
+ [{case_dir, CaseDir}, {watchdog, Dog}|Config].
end_per_testcase(_Func, Config) ->
code:del_path(?config(case_dir, Config)).
@@ -253,14 +232,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) ->
@@ -322,6 +295,28 @@ case_dir([C|Config], Opt) ->
%% Test cases
%%------------------------------------------------------------------------------
+%% Cover run-time functions that are only called by the ASN.1 compiler
+%% (if any).
+cover(_) ->
+ Wc = filename:join([code:lib_dir(asn1),"ebin","asn1ct_eval_*.beam"]),
+ Beams = filelib:wildcard(Wc),
+ true = Beams =/= [],
+ [begin
+ M0 = filename:basename(Beam),
+ M1 = filename:rootname(M0),
+ M = list_to_atom(M1),
+ "asn1ct_eval_" ++ Group0 = M1,
+ Group = list_to_atom(Group0),
+ io:format("%%\n"
+ "%% ~s\n"
+ "%%\n", [M]),
+ asn1ct_func:start_link(),
+ [asn1ct_func:need({Group,F,A}) ||
+ {F,A} <- M:module_info(exports), F =/= module_info],
+ asn1ct_func:generate(group_leader())
+ end || Beam <- Beams],
+ ok.
+
testPrim(Config) -> test(Config, fun testPrim/3).
testPrim(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["Prim", "Real"], Config, [Rule|Opts]),
@@ -338,22 +333,27 @@ 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) ->
asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config, [Rule|Opts]),
testPrimStrings_cases(Rule),
- ?only_ber(testPrimStrings:more_strings(Rule)).
+ asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
+ [legacy_bit_string,Rule|Opts]),
+ testPrimStrings:bit_string(Rule),
+ asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
+ [compact_bit_string,Rule|Opts]),
+ testPrimStrings:bit_string(Rule),
+ testPrimStrings:more_strings(Rule).
testPrimStrings_cases(Rule) ->
testPrimStrings:bit_string(Rule),
- testPrimStrings:bit_string_unnamed(Rule),
testPrimStrings:octet_string(Rule),
testPrimStrings:numeric_string(Rule),
testPrimStrings:other_strings(Rule),
@@ -367,10 +367,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,
- [Rule|Opts])),
- ?only_ber_nif(testPrimStrings_cases(Rule)),
- ?only_ber_nif(testPrimStrings:more_strings(Rule)).
+ asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
+ [Rule|Opts]),
+ testPrimStrings_cases(Rule),
+ testPrimStrings:more_strings(Rule).
testChoPrim(Config) -> test(Config, fun testChoPrim/3).
testChoPrim(Config, Rule, Opts) ->
@@ -395,7 +395,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]),
@@ -426,6 +426,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]),
@@ -451,7 +456,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) ->
@@ -511,8 +517,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",
@@ -628,32 +633,63 @@ 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).
c_string(Config, Rule, Opts) ->
- asn1_test_lib:compile("String", Config, [Rule|Opts]).
+ asn1_test_lib:compile("String", Config, [Rule|Opts]),
+ asn1ct:test('String').
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()].
+der(Config) ->
+ asn1_test_lib:compile_all(ber_modules(), Config, [der]).
module_test(M, Config, Rule, Opts) ->
asn1_test_lib:compile(M, Config, [Rule|Opts]),
@@ -665,12 +701,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},
@@ -681,21 +717,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,
@@ -721,16 +742,12 @@ value_test(Config, Rule, Opts) ->
'ObjIdValues':'mobileDomainId'()).
value_bad_enum_test(Config) ->
- case ?MODULE of
- asn1_SUITE ->
- {error, _} = asn1ct:compile(?config(data_dir, Config)
- ++ "BadEnumValue1",
- [{outdir, ?config(case_dir, Config)}]);
- _ -> {skip, "Runs in asn1_SUITE only"}
- end.
+ {error, _} = asn1ct:compile(?config(data_dir, Config) ++
+ "BadEnumValue1",
+ [{outdir, ?config(case_dir, Config)}]).
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}),
@@ -741,7 +758,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).
@@ -754,14 +771,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}).
@@ -774,24 +791,24 @@ testConstraints(Config, Rule, Opts) ->
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).
@@ -842,18 +859,13 @@ testInvokeMod(Config, Rule, Opts) ->
{ok, _Result2} = 'PrimStrings':encode('Bs1', [1, 0, 1, 0]).
testExport(Config) ->
- case ?MODULE of
- asn1_SUITE ->
- {error, {asn1, _Reason}} =
- asn1ct:compile(filename:join(?config(data_dir, Config),
- "IllegalExport"),
- [{outdir, ?config(case_dir, Config)}]);
- _ ->
- {skip, "Runs in asn1_SUITE only"}
- end.
+ {error, {asn1, _Reason}} =
+ asn1ct:compile(filename:join(?config(data_dir, Config),
+ "IllegalExport"),
+ [{outdir, ?config(case_dir, Config)}]).
testImport(Config) ->
- test(Config, fun testImport/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun testImport/3).
testImport(Config, Rule, Opts) ->
{error, _} = asn1ct:compile(filename:join(?config(data_dir, Config),
"ImportsFrom"),
@@ -891,19 +903,14 @@ testOpenTypeImplicitTag(Config, Rule, Opts) ->
testOpenTypeImplicitTag:main(Rule).
duplicate_tags(Config) ->
- case ?MODULE of
- asn1_SUITE ->
- DataDir = ?config(data_dir, Config),
- CaseDir = ?config(case_dir, Config),
- {error, {asn1, [{error, {type, _, _, 'SeqOpt1Imp', {asn1, {duplicates_of_the_tags, _}}}}]}} =
- asn1ct:compile(filename:join(DataDir, "SeqOptional2"),
- [abs, {outdir, CaseDir}]);
- _ ->
- {skip, "Runs in asn1_SUITE only"}
- end.
+ DataDir = ?config(data_dir, Config),
+ CaseDir = ?config(case_dir, Config),
+ {error, {asn1, [{error, {type, _, _, 'SeqOpt1Imp',
+ {asn1, {duplicates_of_the_tags, _}}}}]}} =
+ asn1ct:compile(filename:join(DataDir, "SeqOptional2"),
+ [abs, {outdir, CaseDir}]).
-rtUI(Config) -> test(Config, fun rtUI/3, [per, per_bin, ber,
- ber_bin, ber_bin_v2]).
+rtUI(Config) -> test(Config, fun rtUI/3).
rtUI(Config, Rule, Opts) ->
asn1_test_lib:compile("Prim", Config, [Rule|Opts]),
{ok, _} = asn1rt:info('Prim').
@@ -919,19 +926,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).
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(),
@@ -941,7 +948,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",
@@ -949,13 +956,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]),
@@ -963,19 +969,24 @@ 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]).
+test_WS_ParamClass(Config) -> test(Config, fun test_WS_ParamClass/3).
+test_WS_ParamClass(Config, Rule, Opts) ->
+ asn1_test_lib:compile("InformationFramework", Config, [Rule|Opts]),
+ ?only_ber(testWSParamClass:main(Rule)),
+ ok.
test_Defed_ObjectIdentifier(Config) ->
- asn1_test_lib:compile("UsefulDefinitions", Config, [ber_bin]).
+ test(Config, fun test_Defed_ObjectIdentifier/3).
+test_Defed_ObjectIdentifier(Config, Rule, Opts) ->
+ asn1_test_lib:compile("UsefulDefinitions", Config, [Rule|Opts]).
testSelectionType(Config) -> test(Config, fun testSelectionType/3).
testSelectionType(Config, Rule, Opts) ->
@@ -983,19 +994,13 @@ 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]),
testSSLspecs:run(Rule),
-
- case code:which(asn1ct) of
- cover_compiled ->
- ok;
- _ ->
- ok = testSSLspecs:compile_inline(Config, Rule),
- ok = testSSLspecs:run_inline(Rule)
- end.
+ ok = testSSLspecs:compile_combined(Config, Rule),
+ ok = testSSLspecs:run_combined(Rule).
testNortel(Config) -> test(Config, fun testNortel/3).
testNortel(Config, Rule, Opts) ->
@@ -1006,47 +1011,25 @@ test_undecoded_rest(Config, Rule, Opts) ->
asn1_test_lib:compile("P-Record", 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;
- _ -> test_undecoded_rest:test(undec_rest, Config)
- end.
-
-test_inline(Config) ->
- test(Config, fun test_inline/3, [ber, ber_bin, ber_bin_v2]).
-test_inline(Config, Rule, Opts) ->
- case code:which(asn1ct) of
- cover_compiled ->
- {skip, "Not runnable when cover compiled"};
- _ ->
- test_inline:compile(Config, Opts),
- test_inline:main(Config, Rule),
- test_inline:inline1(Config, Rule, Opts),
- test_inline:performance2()
- end.
+ test_undecoded_rest:test(undec_rest, Config).
testTcapsystem(Config) ->
- test(Config, fun testTcapsystem/3, [ber, ber_bin, ber_bin_v2]).
+ test(Config, fun testTcapsystem/3).
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).
test_compile_options(Config) ->
- case code:which(asn1ct) of
- cover_compiled ->
- {skip, "Not runnable when cover compiled"};
- _ ->
- ok = test_compile_options:wrong_path(Config),
- ok = test_compile_options:path(Config),
- ok = test_compile_options:noobj(Config),
- ok = test_compile_options:record_name_prefix(Config),
- ok = test_compile_options:verbose(Config),
- ok = test_compile_options:warnings_as_errors(Config)
- end.
+ ok = test_compile_options:wrong_path(Config),
+ ok = test_compile_options:path(Config),
+ ok = test_compile_options:noobj(Config),
+ ok = test_compile_options:record_name_prefix(Config),
+ ok = test_compile_options:verbose(Config),
+ ok = test_compile_options:warnings_as_errors(Config).
testDoubleEllipses(Config) -> test(Config, fun testDoubleEllipses/3).
testDoubleEllipses(Config, Rule, Opts) ->
@@ -1054,8 +1037,7 @@ testDoubleEllipses(Config, Rule, Opts) ->
testDoubleEllipses:main(Rule).
test_modified_x420(Config) ->
- test(Config, fun test_modified_x420/3,
- [ber,ber_bin,ber_bin_v2,{ber_bin_v2,[nif]}]).
+ test(Config, fun test_modified_x420/3, [ber]).
test_modified_x420(Config, Rule, Opts) ->
Files = [filename:join(modified_x420, F) || F <- ["PKCS7",
"InformationFramework",
@@ -1071,7 +1053,7 @@ testX420(Config) ->
"sparc-sun-solaris2.10" ->
{skip,"Too slow for an old Sparc"};
_ ->
- test(Config, fun testX420/3, [ber, ber_bin, ber_bin_v2])
+ test(Config, fun testX420/3, [ber])
end.
testX420(Config, Rule, Opts) ->
testX420:compile(Rule, [der|Opts], Config),
@@ -1079,13 +1061,12 @@ testX420(Config, Rule, Opts) ->
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, []),
@@ -1096,8 +1077,7 @@ 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,
@@ -1137,6 +1117,7 @@ test_modules() ->
"Int",
"MAP-commonDataTypes",
"Null",
+ "NullTest",
"Octetstr",
"One",
"P-Record",
@@ -1187,46 +1168,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) -> [];
@@ -1269,11 +1221,11 @@ testName2Number(Config) ->
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) -> [];
@@ -1284,7 +1236,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),
@@ -1293,7 +1245,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]),
@@ -1307,191 +1259,6 @@ smp(Config) ->
{skipped,"No smp support"}
end.
-per_performance(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- NifDir = filename:join(PrivDir,"nif"),
- ErlDir = filename:join(PrivDir,"erl"),
- 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]),
-
- Modules = ['NBAP-CommonDataTypes',
- 'NBAP-Constants',
- 'NBAP-Containers',
- 'NBAP-IEs',
- 'NBAP-PDU-Contents',
- 'NBAP-PDU-Discriptions'],
-
-
- PreNif = fun() ->
- code:add_patha(NifDir),
- lists:foreach(fun(M) ->
- code:purge(M),
- code:load_file(M)
- end,Modules)
- end,
-
- PreErl = fun() ->
- code:add_patha(ErlDir),
- lists:foreach(fun(M) ->
- code:purge(M),
- code:load_file(M)
- end,Modules)
- end,
-
- Func = fun() ->
- element(1,timer:tc(
- asn1_wrapper,encode,['NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- Msg]))
- end,
-
- nif_vs_erlang_performance({{{PreNif,Func},{PreErl,Func}},100000,32}).
-
-ber_performance(Config) ->
-
- Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ok = testNBAPsystem:compile(Config, [ber_bin, optimize, nif]),
-
-
- BerFun = fun() ->
- {ok,B} = asn1_wrapper:encode('NBAP-PDU-Discriptions',
- 'NBAP-PDU', Msg),
- asn1_wrapper:decode(
- 'NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- B)
- end,
- nif_vs_erlang_performance({BerFun,100000,32}).
-
-cert_pem_performance(Config) when is_list(Config) ->
- cert_pem_performance({100000, 32});
-cert_pem_performance({N,S}) ->
- nif_vs_erlang_performance({fun pem_performance:cert_pem/0,N,S}).
-
-dsa_pem_performance(Config) when is_list(Config) ->
- dsa_pem_performance({100000, 32});
-dsa_pem_performance({N,S}) ->
- nif_vs_erlang_performance({fun pem_performance:dsa_pem/0,N,S}).
-
-
-nif_vs_erlang_performance({{TC1,TC2},N,Sched}) ->
- random:seed({123,456,789}),
- io:format("Running a ~p sample with ~p max procs...~n~n",[N,Sched]),
-
- {True,False} = exec(TC1,TC2,Sched,N+1),
-
- io:format("~ndone!~n"),
-
- io:format("~n"),TStats = print_stats(strip(True,N div 20)),
- io:format("~n"),FStats = print_stats(strip(False,N div 20)),
- Str = io_lib:format("~nNifs are ~.3f% faster than erlang!~n",
- [(element(2,FStats) - element(2,TStats)) /
- element(2,FStats) * 100]),
- io:format(Str),
- {comment, lists:flatten(Str)};
-nif_vs_erlang_performance({T,N,Sched}) ->
- PTC1 = fun() ->
- application:set_env(asn1, nif_loadable, true)
- end,
- PTC2 = fun() ->
- application:set_env(asn1, nif_loadable, false)
- end,
- TC = fun() ->
- element(1,timer:tc(T))
- end,
- nif_vs_erlang_performance({{{PTC1,TC},{PTC2,TC}},N,Sched}).
-
-
-print_stats(Data) ->
- Length = length(Data),
- Mean = lists:sum(Data) / Length,
- Variance = lists:foldl(fun(N,Acc) -> math:pow(N - Mean, 2)+Acc end, 0, Data),
- StdDev = math:sqrt(Variance / Length),
- Median = lists:nth(round(Length/2),Data),
- Min = lists:min(Data),
- Max = lists:max(Data),
- if Length < 20 ->
- io:format("Data: ~w~n",[Data]);
- true ->
- ok
- end,
- io:format("Length: ~p~nMean: ~p~nStdDev: ~p~nMedian: ~p~nMin: ~p~nMax: ~p~n",
- [Length,Mean,StdDev,Median,Min,Max]),
- {Length,Mean,StdDev,Median,Min,Max}.
-
-collect(Acc) ->
- receive
- {Tag,Val} ->
- Prev = proplists:get_value(Tag,Acc,[]),
- collect(lists:keystore(Tag,1,Acc,{Tag,[Val|Prev]}))
- after 100 ->
- Acc
- end.
-
-exec(One,Two,Max,N) ->
- exec(One,Two,Max,N,{[],[]}).
-exec(_,_,_,1,{D1,D2}) ->
- {lists:flatten(D1),lists:flatten(D2)};
-exec({PreOne,One} = O,{PreTwo,Two} = T,MaxProcs, N, {D1,D2}) ->
- Num = random:uniform(round(N/2)),
- if Num rem 3 == 0 ->
- timer:sleep(Num rem 1000);
- true ->
- ok
- end,
- Procs = random:uniform(MaxProcs),
- io:format("\tBatch: ~p items in ~p processes, ~p left~n",[Num,Procs,N-Num]),
- if Num rem 2 == 1 ->
- erlang:garbage_collect(),
- PreOne(),
- MoreOne = pexec(One, Num, Procs, []),
- erlang:garbage_collect(),
- PreTwo(),
- MoreTwo = pexec(Two, Num, Procs, []);
- true ->
- erlang:garbage_collect(),
- PreTwo(),
- MoreTwo = pexec(Two, Num, Procs, []),
- erlang:garbage_collect(),
- PreOne(),
- MoreOne = pexec(One, Num, Procs, [])
- end,
- exec(O,T,MaxProcs,N-Num,{[MoreOne|D1],
- [MoreTwo|D2]}).
-
-pexec(_Fun, _, 0, []) ->
- [];
-pexec(Fun, _, 0, [{Ref,Pid}|Rest]) ->
- receive
- {data,D} ->
- [D|pexec(Fun,0,0,[{Ref,Pid}|Rest])];
- {'DOWN', Ref, process, Pid, normal} ->
- pexec(Fun, 0,0,Rest)
- end;
-pexec(Fun, 0, 1, AccProcs) ->
- pexec(Fun, 0, 0, AccProcs);
-pexec(Fun, N, 1, AccProcs) ->
- [Fun()|pexec(Fun, N - 1, 1, AccProcs)];
-pexec(Fun, N, Procs, AccProcs) ->
- S = self(),
- Pid = spawn(fun() ->
- S ! {data,pexec(Fun,N,1,[])}
- end),
- Ref = erlang:monitor(process, Pid),
- pexec(Fun, N, Procs - 1, [{Ref,Pid}|AccProcs]).
-
-strip(Data,Num) ->
- {_,R} = lists:split(Num,lists:sort(Data)),
- element(2,lists:split(Num,lists:reverse(R))).
-
-faster(A,B) ->
- (B - A)/B * 100.
-
enc_dec(1, Msg, N) ->
worker_loop(N, Msg);
enc_dec(NumOfProcs,Msg, N) ->
@@ -1529,7 +1296,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/Def.py b/lib/asn1/test/asn1_SUITE_data/Def.py
deleted file mode 100644
index ff08ed6386..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/Def.py
+++ /dev/null
@@ -1,31 +0,0 @@
-Def DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-Def1 ::= SEQUENCE
-{
- bool0 [0] BOOLEAN,
- bool1 [1] BOOLEAN DEFAULT false,
- bool2 [2] BOOLEAN DEFAULT false,
- bool3 [3] BOOLEAN DEFAULT false
-}
-
-
-Def2 ::= SEQUENCE
-{
- bool10 [10] BOOLEAN,
- bool11 [11] BOOLEAN DEFAULT false,
- bool12 [12] BOOLEAN DEFAULT false,
- bool13 [13] BOOLEAN
-}
-
-
-Def3 ::= SEQUENCE
-{
- bool30 [30] BOOLEAN DEFAULT false,
- bool31 [31] BOOLEAN DEFAULT false,
- bool32 [32] BOOLEAN DEFAULT false,
- bool33 [33] BOOLEAN DEFAULT false
-}
-
-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 b7cc74ab07..b07dcd8baa 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-2012. All Rights Reserved.
+-- Copyright Ericsson AB 2001-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
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/NullTest.asn1 b/lib/asn1/test/asn1_SUITE_data/NullTest.asn1
new file mode 100644
index 0000000000..041b20a4c1
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/NullTest.asn1
@@ -0,0 +1,14 @@
+NullTest DEFINITIONS ::=
+BEGIN
+
+NullTestData ::= SEQUENCE {
+ body NullBody,
+ tail INTEGER
+}
+
+NullBody ::= CHOICE {
+ null [0] NULL,
+ notNull [1] INTEGER
+}
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/Opt.py b/lib/asn1/test/asn1_SUITE_data/Opt.py
deleted file mode 100644
index 48c2a09b64..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/Opt.py
+++ /dev/null
@@ -1,31 +0,0 @@
-Opt DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-Opt1 ::= SEQUENCE
-{
- bool0 [0] BOOLEAN,
- bool1 [1] BOOLEAN OPTIONAL,
- bool2 [2] BOOLEAN OPTIONAL,
- bool3 [3] BOOLEAN OPTIONAL
-}
-
-
-Opt2 ::= SEQUENCE
-{
- bool10 [10] BOOLEAN,
- bool11 [11] BOOLEAN OPTIONAL,
- bool12 [12] BOOLEAN OPTIONAL,
- bool13 [13] BOOLEAN
-}
-
-
-Opt3 ::= SEQUENCE
-{
- bool30 [30] BOOLEAN OPTIONAL,
- bool31 [31] BOOLEAN OPTIONAL,
- bool32 [32] BOOLEAN OPTIONAL,
- bool33 [33] BOOLEAN OPTIONAL
-}
-
-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/Prim.asn1 b/lib/asn1/test/asn1_SUITE_data/Prim.asn1
index 1a905988f5..c3d54dbbb3 100644
--- a/lib/asn1/test/asn1_SUITE_data/Prim.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/Prim.asn1
@@ -22,6 +22,8 @@ BEGIN
Enum ::= ENUMERATED {monday(1),tuesday(2),wednesday(3),thursday(4),
friday(5),saturday(6),sunday(7)}
+ SingleEnumVal ::= ENUMERATED {true}
+ SingleEnumValExt ::= ENUMERATED {true, ...}
ObjId ::= OBJECT IDENTIFIER
@@ -29,4 +31,21 @@ BEGIN
Null ::= NULL
+ -- Test that REAL numbers can co-exist with other data types.
+ App-X-Real ::= REAL (WITH COMPONENTS {
+ mantissa (-16777215..16777215),
+ base (2),
+ exponent (-125..128) } )
+
+ Seq ::= SEQUENCE {
+ n Null,
+ i1 INTEGER (0..63),
+ e1 SingleEnumVal,
+ i2 INTEGER (0..63),
+ e2 SingleEnumVal,
+ i3 INTEGER (0..63),
+ b Bool,
+ i4 INTEGER (0..63)
+ }
+
END
diff --git a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1
index d287840f30..cfaf4cf034 100644
--- a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1
@@ -55,20 +55,49 @@ 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
NsExpCon ::= [71] EXPLICIT NumericString
Ps ::= PrintableString
+ Ps11 ::= PrintableString (FROM ("0123456789*"))
Ts ::= TeletexString
Vxs ::= VideotexString
Vis ::= VisibleString
+ Vis8 ::= VisibleString (FROM ("01234567"))
IA5 ::= IA5String
+ IA5Visible ::= IA5String (FROM (" ".."~"))
Grs ::= GraphicString
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/SeqOf.py b/lib/asn1/test/asn1_SUITE_data/SeqOf.py
deleted file mode 100644
index c941418934..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/SeqOf.py
+++ /dev/null
@@ -1,45 +0,0 @@
-SeqOf DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-
-Seq1 ::= SEQUENCE
-{
- bool1 BOOLEAN,
- int1 INTEGER,
- seq1 SEQUENCE OF SeqIn DEFAULT {}
-}
-
-Seq2 ::= SEQUENCE
-{
- seq2 SEQUENCE OF SeqIn DEFAULT {},
- bool2 BOOLEAN,
- int2 INTEGER
-}
-
-Seq3 ::= SEQUENCE
-{
- bool3 BOOLEAN,
- seq3 SEQUENCE OF SeqIn DEFAULT {},
- int3 INTEGER
-}
-
-Seq4 ::= SEQUENCE
-{
- seq41 [41] SEQUENCE OF SeqIn DEFAULT {},
- seq42 [42] SEQUENCE OF SeqIn DEFAULT {},
- seq43 [43] SEQUENCE OF SeqIn DEFAULT {}
-}
-
-
-
-SeqIn ::= SEQUENCE
-{
- boolIn BOOLEAN,
- intIn INTEGER
-}
-
-
-
-
-END
diff --git a/lib/asn1/test/asn1_SUITE_data/SetOf.py b/lib/asn1/test/asn1_SUITE_data/SetOf.py
deleted file mode 100644
index 4e2ea16fcc..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/SetOf.py
+++ /dev/null
@@ -1,42 +0,0 @@
-SetOf DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-
-Set1 ::= SET
-{
- bool1 BOOLEAN,
- int1 INTEGER,
- set1 SET OF SetIn DEFAULT {}
-}
-
-Set2 ::= SET
-{
- set2 SET OF SetIn DEFAULT {},
- bool2 BOOLEAN,
- int2 INTEGER
-}
-
-Set3 ::= SET
-{
- bool3 BOOLEAN,
- set3 SET OF SetIn DEFAULT {},
- int3 INTEGER
-}
-
-Set4 ::= SET
-{
- set41 [41] SET OF SetIn DEFAULT {},
- set42 [42] SET OF SetIn DEFAULT {},
- set43 [43] SET OF SetIn DEFAULT {}
-}
-
-
-
-SetIn ::= SET
-{
- boolIn BOOLEAN,
- intIn INTEGER
-}
-
-END
diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
index 8148381d92..d46560979d 100644
--- a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
+++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
@@ -7,7 +7,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -140,7 +140,7 @@ run3() ->
Barring = #'AC-BarringConfig'{
'ac-BarringFactor' = p00,
'ac-BarringTime' = s4,
- 'ac-BarringForSpecialAC' = [0,0,0,0,0]},
+ 'ac-BarringForSpecialAC' = <<0:5>>},
roundtrip(SI),
roundtrip(SI#'SystemInformationBlockType2'{
'ssac-BarringForMMTEL-Voice-r9'=Barring}),
diff --git a/lib/asn1/test/asn1_SUITE_data/testobj.erl b/lib/asn1/test/asn1_SUITE_data/testobj.erl
index be7ceee7d1..8d34a473ae 100644
--- a/lib/asn1/test/asn1_SUITE_data/testobj.erl
+++ b/lib/asn1/test/asn1_SUITE_data/testobj.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -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..1225e36778 100644
--- a/lib/asn1/test/asn1_app_test.erl
+++ b/lib/asn1/test/asn1_app_test.erl
@@ -138,7 +138,9 @@ 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,asn1ct_func,asn1ct_rtt,
+ asn1ct_eval_ext,asn1ct_eval_per,asn1ct_eval_uper],
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..1e40fd7b9e 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -61,15 +61,12 @@ compile_erlang(Mod, Config, Options) ->
[{i, CaseDir}, {outdir, CaseDir}|Options]).
should_load(File, Options) ->
- should_load(File, lists:member(abs, Options),
- proplists:lookup(inline, Options)).
-
-should_load(_File, true, _Inline) ->
- false;
-should_load(_File, _Abs, {inline, Module}) when Module /= true ->
- {module, Module};
-should_load(File, _Abs, _Inline) ->
- {module, list_to_atom(strip_extension(filename:basename(File)))}.
+ case lists:member(abs, Options) of
+ true ->
+ false;
+ false ->
+ {module,list_to_atom(strip_extension(filename:basename(File)))}
+ end.
strip_extension(File) ->
strip_extension(File, filename:extension(File)).
@@ -87,14 +84,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 +151,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 +164,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..ac194fe38b 100644
--- a/lib/asn1/test/asn1_wrapper.erl
+++ b/lib/asn1/test/asn1_wrapper.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -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/ber_decode_error.erl b/lib/asn1/test/ber_decode_error.erl
index ff6e386a88..1c4b4c6894 100644
--- a/lib/asn1/test/ber_decode_error.erl
+++ b/lib/asn1/test/ber_decode_error.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
@@ -21,31 +21,34 @@
-export([run/1]).
--include_lib("test_server/include/test_server.hrl").
-
run([]) ->
- ?line {ok,B} = asn1_wrapper:encode('Constructed','S3',{'S3',17}),
- ?line [T,L|V] = lists:flatten(B),
- ?line Bytes = [T,L+3|V] ++ [2,1,3],
- ?line case asn1_wrapper:decode('Constructed','S3',Bytes) of
- {error,{asn1,{unexpected,_}}} -> ok
- end,
- %% Unexpected bytes must be accepted if there is an extensionmark
- ?line {ok,{'S3ext',17}} = asn1_wrapper:decode('Constructed','S3ext',Bytes),
- ok;
-run([driver]) ->
- %% test of OTP-4797, bad indata to driver does not cause an EXIT
- ?line {error,_Reason} = asn1rt:decode('Constructed','S3',[3,5]),
- ok;
-run([nif]) ->
- %% test of OTP-4797, bad indata to driver does not cause an EXIT
- ?line {error,_Reason} = asn1rt:decode('Constructed','S3',[3,5]),
- ok.
-
+ {ok,B} = asn1_wrapper:encode('Constructed','S3',{'S3',17}),
+ [T,L|V] = lists:flatten(B),
+ Bytes = [T,L+3|V] ++ [2,1,3],
+ case asn1_wrapper:decode('Constructed','S3',Bytes) of
+ {error,{asn1,{unexpected,_}}} -> ok
+ end,
+ %% Unexpected bytes must be accepted if there is an extensionmark
+ {ok,{'S3ext',17}} = asn1_wrapper:decode('Constructed','S3ext',Bytes),
+ %% Truncated tag.
+ {error,{asn1,{invalid_tag,_}}} =
+ (catch 'Constructed':decode('I', <<31,255,255>>)),
+ %% Overlong tag.
+ {error,{asn1,{invalid_tag,_}}} =
+ (catch 'Constructed':decode('I', <<31,255,255,255,127>>)),
+ %% Invalid length.
+ {error,{asn1,{invalid_length,_}}} =
+ (catch 'Constructed':decode('I', <<8,255>>)),
+ %% Other errors.
+ {error,{asn1,{invalid_value,_}}} =
+ (catch 'Constructed':decode('I', <<>>)),
+ {error,{asn1,{invalid_value,_}}} =
+ (catch 'Constructed':decode('I', <<8,7>>)),
+ ok.
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 543c106e8a..c8d9008641 100644
--- a/lib/asn1/test/testConstraints.erl
+++ b/lib/asn1/test/testConstraints.erl
@@ -30,59 +30,20 @@ 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)
@@ -90,136 +51,106 @@ int_constraints(Rules) ->
%% where one value is predefined.
%%==========================================================
- ?line {ok,BytesSV3} = asn1_wrapper:encode('Constraints','SingleValue3',1),
- ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue3',
- lists:flatten(BytesSV3)),
- ?line {ok,BytesSV3_2} = asn1_wrapper:encode('Constraints','SingleValue3',5),
- ?line {ok,5} = asn1_wrapper:decode('Constraints','SingleValue3',
- lists:flatten(BytesSV3_2)),
- ?line {ok,BytesSV3_3} = asn1_wrapper:encode('Constraints','SingleValue3',10),
- ?line {ok,10} = asn1_wrapper:decode('Constraints','SingleValue3',
- lists:flatten(BytesSV3_3)),
+ 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,
- ?line {ok,BytesFoo} = asn1_wrapper:encode('Constraints','Range256to65536',
- LastNumWithoutLengthEncoding),
- ?line {ok,LastNumWithoutLengthEncoding} =
- asn1_wrapper:decode('Constraints','Range256to65536',lists:flatten(BytesFoo)),
+ roundtrip('Range256to65536', LastNumWithoutLengthEncoding),
FirstNumWithLengthEncoding = 65537,
- ?line {ok,BytesBar} = asn1_wrapper:encode('LargeConstraints','RangeMax',
- FirstNumWithLengthEncoding),
- ?line {ok,FirstNumWithLengthEncoding} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBar)),
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumWithLengthEncoding),
FirstNumOver16_6 = 16777217,
- ?line {ok, BytesBaz} =
- asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_6),
- ?line {ok, FirstNumOver16_6} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBaz)),
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_6),
FirstNumOver16_8 = 4294967297,
- ?line {ok, BytesQux} =
- asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_8),
- ?line {ok, FirstNumOver16_8} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesQux)),
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_8),
FirstNumOver16_10 = 1099511627776,
- ?line {ok, BytesBur} =
- asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10),
- ?line {ok, FirstNumOver16_10} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)),
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10),
FirstNumOver16_10 = 1099511627776,
- ?line {ok, BytesBur} =
- asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10),
- ?line {ok, FirstNumOver16_10} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)),
+ roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10),
HalfMax = 1 bsl (128*8),
- ?line {ok, BytesHalfMax} =
- asn1_wrapper:encode('LargeConstraints','RangeMax', HalfMax),
- ?line {ok, HalfMax} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesHalfMax)),
+ roundtrip('LargeConstraints', 'RangeMax', HalfMax),
Max = 1 bsl (255*8),
- ?line {ok, BytesMax} =
- asn1_wrapper:encode('LargeConstraints','RangeMax', Max),
- ?line {ok, Max} =
- asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesMax)),
+ roundtrip('LargeConstraints', 'RangeMax', Max),
%% Random number within longlong range
LongLong = 12672809400538808320,
- ?line {ok, BytesLongLong} =
- asn1_wrapper:encode('Constraints','LongLong', LongLong),
- ?line {ok, LongLong} =
- asn1_wrapper:decode('Constraints','LongLong',lists:flatten(BytesLongLong)),
+ 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/testContextSwitchingTypes.erl b/lib/asn1/test/testContextSwitchingTypes.erl
index 4f67942922..40dbe25015 100644
--- a/lib/asn1/test/testContextSwitchingTypes.erl
+++ b/lib/asn1/test/testContextSwitchingTypes.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -52,22 +52,36 @@ test(Config) ->
check_EXTERNAL({'EXTERNAL',Identif,DVD,DV})->
- ?line ok=check_EXTERNAL_Idef(Identif),
- ?line ok = check_EXTERNAL_DVD(DVD),
- ?line ok = check_EXTERNAL_DV(DV).
-check_EXTERNAL_Idef({Alt,_}) when Alt=='context-negotiation';
- Alt=='presentation-context-id';
- Alt==syntax ->
- ok;
-check_EXTERNAL_Idef(I) ->
- {error,"failed on identification alternative",I}.
-check_EXTERNAL_DVD(DVD) when is_list(DVD) ->
- ok;
-check_EXTERNAL_DVD(asn1_NOVALUE) ->
- ok;
-check_EXTERNAL_DVD(DVD) ->
- {error,"failed on data-value-descriptor alternative",DVD}.
-check_EXTERNAL_DV(DV) when is_list(DV);is_binary(DV) ->
- ok;
-check_EXTERNAL_DV(DV) ->
- {error,"failed on data-value alternative",DV}.
+ %% EXTERNAL in the 1994 format.
+ case Identif of
+ {'context-negotiation',_} ->
+ ok;
+ {'presentation-context-id',Id} ->
+ true = is_integer(Id);
+ {syntax,ObjId} ->
+ check_object_identifier(ObjId)
+ end,
+ check_EXTERNAL_DVD(DVD),
+ check_EXTERNAL_DV(DV);
+check_EXTERNAL({'EXTERNAL',ObjId,IndirectRef,Descriptor,Enc})->
+ %% EXTERNAL in the 1990 format.
+ check_object_identifier(ObjId),
+ true = is_integer(IndirectRef),
+ true = is_binary(Descriptor) orelse is_list(Descriptor),
+ case Enc of
+ {arbitrary,_} -> ok;
+ {'single-ASN1-type',_} -> ok;
+ {'octet-aligned',_} -> ok
+ end.
+
+check_EXTERNAL_DVD(DVD) when is_list(DVD) -> ok;
+check_EXTERNAL_DVD(asn1_NOVALUE) -> ok.
+
+check_EXTERNAL_DV(DV) when is_list(DV); is_binary(DV) -> ok.
+
+check_object_identifier(Tuple) when is_tuple(Tuple) ->
+ %% An OBJECT IDENTIFIER is a tuple with integer elements.
+ case [E || E <- tuple_to_list(Tuple),
+ not is_integer(E)] of
+ [] -> ok
+ end.
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/testDoubleEllipses.erl b/lib/asn1/test/testDoubleEllipses.erl
index 9030a99ce2..1032156b91 100644
--- a/lib/asn1/test/testDoubleEllipses.erl
+++ b/lib/asn1/test/testDoubleEllipses.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
@@ -51,7 +51,7 @@ main(_Rules) ->
b = [1,0,1,0], e = true,
c = false, f = 14, g = 16}),
?line {ok,#'SeqAltV2'{a = 10, d = 12,
- b = [1,0,1,0], e = true,
+ b = <<2#1010:4>>, e = true,
h = asn1_NOVALUE, i = asn1_NOVALUE,
c = false, f = 14, g = 16}} =
asn1_wrapper:decode('DoubleEllipses','SeqAltV2',Bytes3),
@@ -62,7 +62,7 @@ main(_Rules) ->
h = "PS", i = 13,
c = false, f = 14, g = 16}),
?line {ok,#'SeqAlt'{a = 10, d = 12,
- b = [1,0,1,0], e = true,
+ b = <<2#1010:4>>, e = true,
c = false, f = 14, g = 16}} =
asn1_wrapper:decode('DoubleEllipses','SeqAlt',Bytes4),
@@ -83,7 +83,7 @@ main(_Rules) ->
b = [1,0,1,0], e = true,
c = false, f = 14, g = 16}),
?line {ok,#'SetAltV2'{a = 10, d = 12,
- b = [1,0,1,0], e = true,
+ b = <<2#1010:4>>, e = true,
h = asn1_NOVALUE, i = asn1_NOVALUE,
c = false, f = 14, g = 16}} =
asn1_wrapper:decode('DoubleEllipses','SetAltV2',Bytes7),
@@ -94,7 +94,7 @@ main(_Rules) ->
h = "PS", i = 13,
c = false, f = 14, g = 16}),
?line {ok,#'SetAlt'{a = 10, d = 12,
- b = [1,0,1,0], e = true,
+ b = <<2#1010:4>>, e = true,
c = false, f = 14, g = 16}} =
asn1_wrapper:decode('DoubleEllipses','SetAlt',Bytes8),
ok.
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/testNBAPsystem.erl b/lib/asn1/test/testNBAPsystem.erl
index 4e8381e51e..0f4459f5b2 100644
--- a/lib/asn1/test/testNBAPsystem.erl
+++ b/lib/asn1/test/testNBAPsystem.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -142,8 +142,7 @@ audit_req() ->
protocolIEs =
[#'ProtocolIE-Field'{id=114,
criticality=ignore,
- value={'Start-Of-Audit-Sequence-Indicator',
- 'start-of-audit-sequence' }
+ value='start-of-audit-sequence'
}
]
}.
diff --git a/lib/asn1/test/testParamBasic.erl b/lib/asn1/test/testParamBasic.erl
index b5780195b8..a10468d592 100644
--- a/lib/asn1/test/testParamBasic.erl
+++ b/lib/asn1/test/testParamBasic.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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,8 @@ main(Rules) ->
?line {ok,Bytes12} =
asn1_wrapper:encode('ParamBasic','T12',
#'T12'{number = 11,
- string = [1,0,1,0,1]}),
- ?line {ok,{'T12',11,[1,0,1,0,1]}} =
+ string = <<2#10101:5>>}),
+ {ok,{'T12',11,<<2#10101:5>>}} =
asn1_wrapper:decode('ParamBasic','T12',Bytes12),
?line {ok,Bytes13} =
@@ -54,8 +54,8 @@ main(Rules) ->
?line {ok,Bytes14} =
asn1_wrapper:encode('ParamBasic','T22',
#'T22'{number = 11,
- string = [1,0,1,0,1]}),
- ?line {ok,{'T22',11,[1,0,1,0,1]}} =
+ string = <<2#10101:5>>}),
+ {ok,{'T22',11,<<2#10101:5>>}} =
asn1_wrapper:decode('ParamBasic','T22',Bytes14),
case Rules of
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/testPrim.erl b/lib/asn1/test/testPrim.erl
index 0d4427ba69..91fb9fffca 100644
--- a/lib/asn1/test/testPrim.erl
+++ b/lib/asn1/test/testPrim.erl
@@ -513,6 +513,14 @@ enum(Rules) ->
case catch asn1_wrapper:encode('Prim','Enum',4) of Enum -> Enum end,
ok
end,
+
+ case Rules of
+ Per when Per =:= per; Per =:= uper ->
+ {ok,<<0>>} = 'Prim':encode('SingleEnumVal', true),
+ {ok,<<0>>} = 'Prim':encode('SingleEnumValExt', true);
+ ber ->
+ ok
+ end,
ok.
diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl
index b1c5172b95..f8b0c5b05a 100644
--- a/lib/asn1/test/testPrimStrings.erl
+++ b/lib/asn1/test/testPrimStrings.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -20,7 +20,6 @@
-module(testPrimStrings).
-export([bit_string/1]).
--export([bit_string_unnamed/1]).
-export([octet_string/1]).
-export([numeric_string/1]).
-export([other_strings/1]).
@@ -37,93 +36,35 @@ bit_string(Rules) ->
%%==========================================================
%% Bs1 ::= BIT STRING
%%==========================================================
+
+ bs_roundtrip('Bs1', 0, <<>>),
+ bs_roundtrip('Bs1', 4, <<1:3>>),
+ bs_roundtrip('Bs1', 15, <<15:4>>),
+ bs_roundtrip('Bs1', 255, <<255:8>>),
+
+ bs_roundtrip('Bs1', 256, [0,0,0,0,0,0,0,0,1]),
+ bs_roundtrip('Bs1', 257, [1,0,0,0,0,0,0,0,1]),
+ bs_roundtrip('Bs1', 444, [0,0,1,1,1,1,0,1,1]),
- ?line {ok,Bytes1} = asn1_wrapper:encode('PrimStrings','Bs1',0),
- ?line {ok,[]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes1)),
-
- ?line {ok,Bytes2} = asn1_wrapper:encode('PrimStrings','Bs1',4),
- ?line {ok,[0,0,1]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes2)),
-
- ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','Bs1',15),
- ?line {ok,[1,1,1,1]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes3)),
-
- ?line {ok,Bytes4} = asn1_wrapper:encode('PrimStrings','Bs1',255),
- ?line {ok,[1,1,1,1,1,1,1,1]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes4)),
-
- ?line {ok,Bytes5} = asn1_wrapper:encode('PrimStrings','Bs1',256),
- ?line {ok,[0,0,0,0,0,0,0,0,1]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes5)),
-
- ?line {ok,Bytes6} = asn1_wrapper:encode('PrimStrings','Bs1',257),
- ?line {ok,[1,0,0,0,0,0,0,0,1]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes6)),
-
- ?line {ok,Bytes7} = asn1_wrapper:encode('PrimStrings','Bs1',444),
- ?line {ok,[0,0,1,1,1,1,0,1,1]} = 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,[1,1,1,1,1,1,1,1]} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes11)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,Bytes12} = asn1_wrapper:encode('PrimStrings','Bs1',[0,1,0,0,1,0]),
- ?line {ok,[0,1,0,0,1,0]} =
- 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,[1,0,0,0,0,0,0,0,0]} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes13)),
- ok;
- per ->
- ?line {ok,Bytes12} = asn1_wrapper:encode('PrimStrings','Bs1',[0,1,0,0,1,0]),
- ?line {ok,[0,1,0,0,1,0]} =
- 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,[1,0,0,0,0,0,0,0,0]} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes13)),
- ok
- end,
-
- ?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,[0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]} =
- 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,[0,1,0,0,1,0,0,1,0,0,1,0]} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes15)),
-
- ?line Bytes16 = [35,9,3,2,0,234,3,3,7,156,0],
- ?line {ok,[1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,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,[0,1,0,0,1,0,0,1,0,0,1,0]} =
- 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,[1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]} =
- asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes18)),
- ok;
-
- per ->
- ok
- end,
+ {ok,Enc1} = 'PrimStrings':encode('Bs1', 12345678901234567890),
+ {ok,_} = 'PrimStrings':decode('Bs1', Enc1),
+
+ bs_roundtrip('Bs1', [1,1,1,1,1,1,1,1]),
+ bs_roundtrip('Bs1', [0,1,0,0,1,0]),
+ bs_roundtrip('Bs1', [1,0,0,0,0,0,0,0,0]),
+ bs_roundtrip('Bs1', [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]),
+ case asn1_wrapper:erule(Rules) of
+ ber ->
+ bs_decode('Bs1', <<35,8,3,2,0,73,3,2,4,32>>,
+ [0,1,0,0,1,0,0,1,0,0,1,0]),
+ bs_decode('Bs1', <<35,9,3,2,0,234,3,3,7,156,0>>,
+ [1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]),
+ bs_decode('Bs1', <<35,128,3,2,0,234,3,3,7,156,0,0,0>>,
+ [1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]);
+ per ->
+ ok
+ end,
%%==========================================================
@@ -156,77 +97,55 @@ bit_string(Rules) ->
%% 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]),
+ bs_roundtrip('Bs3', [0,1,1,0,0,1,0], [mo,tu,fr]),
%%==========================================================
%% Bs7 ::= BIT STRING (SIZE (24))
%%==========================================================
- ?line {ok,Bytes33} = asn1_wrapper:encode('PrimStrings','Bs7',53245),
- ?line {ok,[1,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0]} =
- asn1_wrapper:decode('PrimStrings','Bs7',Bytes33),
-
- ?line {ok,Bytes34} = asn1_wrapper:encode('PrimStrings','Bs7',[1,0,1,0]),
- ?line {ok,[1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]} =
- asn1_wrapper:decode('PrimStrings','Bs7',Bytes34),
+ bs_roundtrip('Bs7', 53245,
+ [1,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0]),
+ bs_roundtrip('Bs7', [1,0,1,0],
+ [1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),
%%==========================================================
%% BsPri ::= [PRIVATE 61] BIT STRING
%%==========================================================
- ?line {ok,Bytes41} = asn1_wrapper:encode('PrimStrings','BsPri',45),
- ?line {ok,[1,0,1,1,0,1]} = asn1_wrapper:decode('PrimStrings','BsPri',lists:flatten(Bytes41)),
+ bs_roundtrip('BsPri', 45, [1,0,1,1,0,1]),
- ?line {ok,Bytes42} = asn1_wrapper:encode('PrimStrings','BsPri',211),
- ?line {ok,[1,1,0,0,1,0,1,1]} = asn1_wrapper:decode('PrimStrings','BsPri',lists:flatten(Bytes42)),
-
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,[0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]} =
- asn1_wrapper:decode('PrimStrings','BsPri',[223,61,4,5,75,226,96]),
-
- ?line {ok,[0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]} =
- asn1_wrapper:decode('PrimStrings','BsPri',[255,61,128,3,4,5,75,226,96,0,0]),
-
- ?line {ok,[0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]} =
- asn1_wrapper:decode('PrimStrings','BsPri',[255,61,9,3,2,0,75,3,3,5,226,96]),
-
- ?line {ok,[0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]} =
- asn1_wrapper:decode('PrimStrings','BsPri',[255,61,128,3,2,0,75,3,3,5,226,96,0,0]),
- ok;
-
- per ->
- ok
- end,
+ bs_roundtrip('BsPri', 211, [1,1,0,0,1,0,1,1]),
+ case asn1_wrapper:erule(Rules) of
+ ber ->
+ bs_decode('BsPri', <<223,61,4,5,75,226,96>>,
+ [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]),
+ bs_decode('BsPri', <<255,61,128,3,4,5,75,226,96,0,0>>,
+ [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]),
+ bs_decode('BsPri', <<255,61,9,3,2,0,75,3,3,5,226,96>>,
+ [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]),
+ bs_decode('BsPri', <<255,61,128,3,2,0,75,3,3,5,226,96,0,0>>,
+ [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]);
+ per ->
+ ok
+ end,
%%==========================================================
%% BsExpPri ::= [PRIVATE 61] EXPLICIT BIT STRING
%%==========================================================
- ?line {ok,Bytes51} = asn1_wrapper:encode('PrimStrings','BsExpPri',45),
- ?line {ok,[1,0,1,1,0,1]} =
- asn1_wrapper:decode('PrimStrings','BsExpPri',lists:flatten(Bytes51)),
-
- ?line {ok,Bytes52} = asn1_wrapper:encode('PrimStrings','BsExpPri',211),
- ?line {ok,[1,1,0,0,1,0,1,1]} =
- asn1_wrapper:decode('PrimStrings','BsExpPri',lists:flatten(Bytes52)),
+ bs_roundtrip('BsExpPri', 45, [1,0,1,1,0,1]),
+ bs_roundtrip('BsExpPri', 211, [1,1,0,0,1,0,1,1]),
- ?line case asn1_wrapper:erule(Rules) of
- ber ->
- ?line {ok,[0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]} =
- asn1_wrapper:decode('PrimStrings','BsExpPri',[255,61,6,3,4,5,75,226,96]),
- ok;
-
- per ->
- ok
- end,
+ case asn1_wrapper:erule(Rules) of
+ ber ->
+ bs_decode('BsExpPri', <<255,61,6,3,4,5,75,226,96>>,
+ [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]);
+ per ->
+ ok
+ end,
%%==========================================================
%% TestS ::= BIT STRING {a(0),b(1)} (SIZE (3..8)), test case for OTP-4353
@@ -248,14 +167,10 @@ bit_string(Rules) ->
%% BS5932 ::= BIT STRING (SIZE (5..MAX))
%% test case for OTP-5932
%%==========================================================
+ bs_roundtrip('BSMAX', [1,0,1,0,1]),
case asn1_wrapper:erule(Rules) of
ber ->
- ?line {error,_} = asn1_wrapper:encode('PrimStrings','BSMAX',
- [1,0,1]),
- ?line {ok,Bytes55} =
- asn1_wrapper:encode('PrimStrings','BSMAX',[1,0,1,0,1]),
- ?line {ok,[1,0,1,0,1]} =
- asn1_wrapper:decode('PrimStrings','BSMAX',Bytes55);
+ {error,_} = 'PrimStrings':encode('BSMAX', [1,0,1]);
_ ->
ok
end,
@@ -274,47 +189,13 @@ bit_string(Rules) ->
end,
BSList255 = BSmaker(BSmaker,0,255,{1,0},[]),
+ bs_roundtrip('BS255', BSList255),
BSList256 = BSmaker(BSmaker,0,256,{1,0},[]),
+ bs_roundtrip('BS256', BSList256),
BSList1024 = BSmaker(BSmaker,0,1024,{1,0},[]),
- ?line {ok,Bytes56} =
- asn1_wrapper:encode('PrimStrings','BS255',BSList255),
- ?line {ok,BSList255} =
- asn1_wrapper:decode('PrimStrings','BS255',Bytes56),
- ?line {ok,Bytes57} =
- asn1_wrapper:encode('PrimStrings','BS256',BSList256),
- ?line {ok,BSList256} =
- asn1_wrapper:decode('PrimStrings','BS256',Bytes57),
- ?line {ok,Bytes58} =
- asn1_wrapper:encode('PrimStrings','BS1024',BSList1024),
- ?line {ok,BSList1024} =
- asn1_wrapper:decode('PrimStrings','BS1024',Bytes58).
-
-
-
-bit_string_unnamed(Rules) ->
- case asn1_wrapper:erule(Rules) of
- ber ->
- ok;
- per ->
- ?line {ok,Bytes1} =
- case catch asn1_wrapper:encode('PrimStrings','TransportLayerAddress',[0,1,1,0]) of
- Ret = {ok,_} -> Ret;
- Err ->
- Config = file:consult(test_config),
- ?line OutDir = ?config(priv_dir,Config),
- MyOut = "/home/bertil/daily_build",
- file:copy(filename:join([OutDir,"PrimStrings.erl"]),
- filename:join([MyOut,"PrimStrings.erl"])),
- file:copy(filename:join([OutDir,"PrimStrings.beam"]),
- filename:join([MyOut,"PrimStrings.beam"])),
- file:copy(code:which(asn1rt_per_v1),
- filename:join([MyOut,"asn1rt_per_v1.beam"])),
- file:copy(filename:join([code:lib_dir(asn1),src,"asn1rt_per_v1.erl"]),filename:join([MyOut,"asn1rt_per_v1.erl"])),
- io:format("Err: ~p~n",[Err]),
- Err
- end,
- ?line {ok,[0,1,1,0]} = asn1_wrapper:decode('PrimStrings','TransportLayerAddress',lists:flatten(Bytes1))
- end.
+ bs_roundtrip('BS1024', BSList1024),
+
+ bs_roundtrip('TransportLayerAddress', [0,1,1,0]).
octet_string(Rules) ->
@@ -338,69 +219,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 +252,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 +360,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 +384,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 +406,6 @@ numeric_string(Rules) ->
ok.
-
-
-
other_strings(_Rules) ->
@@ -517,48 +413,30 @@ 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', []),
+ roundtrip('Ps11', "*0123456789*"),
%%==========================================================
%% 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', []),
+ roundtrip('Vis8', "7654321001234567"),
+ roundtrip('Vis8', []),
%%==========================================================
%% 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",
+ roundtrip('IA5', IA5_1),
- ?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('IA5Visible', lists:seq($\s, $~)),
ok.
@@ -568,94 +446,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 +514,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 +546,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 +564,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 +574,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 +599,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 +708,44 @@ 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.
+
+bs_roundtrip(Type, Value) ->
+ bs_roundtrip(Type, Value, Value).
+
+bs_roundtrip(Type, Value, Expected) ->
+ M = 'PrimStrings',
+ {ok,Encoded} = M:encode(Type, Value),
+ case M:decode(Type, Encoded) of
+ {ok,Expected} ->
+ ok;
+ {ok,Other} ->
+ Expected = convert(Other, Expected)
+ end.
+
+bs_decode(Type, Encoded, Expected) ->
+ M = 'PrimStrings',
+ case M:decode(Type, Encoded) of
+ {ok,Expected} ->
+ ok;
+ {ok,Other} ->
+ Expected = convert(Other, Expected)
+ end.
+
+convert(Val, E) when is_bitstring(Val) ->
+ convert_1(Val, E);
+convert({Unused,Bin}, E) ->
+ Sz = bit_size(Bin) - Unused,
+ <<Val:Sz/bitstring,_:Unused>> = Bin,
+ convert_1(Val, E);
+convert(List, E) when is_list(List) ->
+ Val = << <<B:1>> || B <- List >>,
+ convert_1(Val, E).
+
+convert_1(Val, E) when is_list(E) ->
+ [B || <<B:1>> <= Val];
+convert_1(Val, E) when is_bitstring(E) -> Val.
diff --git a/lib/asn1/test/testSSLspecs.erl b/lib/asn1/test/testSSLspecs.erl
index 51ef134e5f..08da92243e 100644
--- a/lib/asn1/test/testSSLspecs.erl
+++ b/lib/asn1/test/testSSLspecs.erl
@@ -20,7 +20,7 @@
-module(testSSLspecs).
--export([compile/2,run/1,compile_inline/2,run_inline/1]).
+-export([compile/2,run/1,compile_combined/2,run_combined/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -42,15 +42,13 @@ 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_combined(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],
- ok = remove_db_file_inline(CaseDir),
- asn1_test_lib:compile("OTP-PKIX.set.asn", Config, Options);
-compile_inline(_Config, _Rule) ->
- ok.
+ der, compact_bit_string, asn1config],
+ ok = remove_db_files_combined(CaseDir),
+ asn1_test_lib:compile("OTP-PKIX.set.asn", Config, Options).
remove_db_files(Dir) ->
?line ok = remove_db_file(Dir ++ "PKIX1Explicit93.asn1db"),
@@ -65,7 +63,7 @@ remove_db_file(File) ->
Err
end.
-remove_db_file_inline(Dir) ->
+remove_db_files_combined(Dir) ->
?line ok = remove_db_file(Dir ++ "OTP-PKIX.asn1db"),
?line ok = remove_db_file(Dir ++ "SSL-PKIX.asn1db"),
?line ok = remove_db_file(Dir ++ "PKIXAttributeCertificate.asn1db"),
@@ -73,15 +71,12 @@ 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 ->
- run1(1);
-run(_) ->
- ok.
+run(ber) ->
+ run1(1).
run1(6) ->
?line f1(6),
?line f2(6),
-%% ?line transform3(ex(7)),
?line transform4(ex(7));
run1(N) ->
?line f1(N),
@@ -100,20 +95,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,12 +141,10 @@ 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_combined(ber) ->
Cert = cert(),
?line {ok,{'CertificatePKIX1Explicit88',{Type,UnDec},_,_}} = 'OTP-PKIX':decode_TBSCert_exclusive(Cert),
?line {ok,_} = 'OTP-PKIX':decode_part(Type,UnDec),
- ok;
-run_inline(_) ->
ok.
cert() ->
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/testSeqSetDefaultVal.erl b/lib/asn1/test/testSeqSetDefaultVal.erl
index ab484db5f2..bd6c9428e2 100644
--- a/lib/asn1/test/testSeqSetDefaultVal.erl
+++ b/lib/asn1/test/testSeqSetDefaultVal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -134,7 +134,7 @@ main(_Rules) ->
c={5,<<64>>},
d=0}),
- ?line {ok,{'SeqBS',[1,0,1,0,1,1,0],2698,[second],[]}} =
+ {ok,{'SeqBS',[1,0,1,0,1,1,0],2698,[second],<<>>}} =
asn1_wrapper:decode('Default','SeqBS',[48,3,131,1,0]),
?line {ok,{'SeqBS',[1,0,1,0,1,1,0],2698,[second],[1,0,0,1]}} =
@@ -161,7 +161,7 @@ main(_Rules) ->
c={5,<<64>>},
d=0}),
- ?line {ok,{'SetBS',[1,0,1,0,1,1,0],2698,[second],[]}} =
+ {ok,{'SetBS',[1,0,1,0,1,1,0],2698,[second],<<>>}} =
asn1_wrapper:decode('Default','SetBS',[49,3,131,1,0]),
?line {ok,{'SetBS',[1,0,1,0,1,1,0],2698,[second],[1,0,0,1]}} =
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..aba13c94de 100644
--- a/lib/asn1/test/testTCAP.erl
+++ b/lib/asn1/test/testTCAP.erl
@@ -1,7 +1,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
@@ -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(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..61d69edd0e 100644
--- a/lib/asn1/test/testTypeValueNotation.erl
+++ b/lib/asn1/test/testTypeValueNotation.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -21,41 +21,16 @@
-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,
enum = a,
- bitstr = [1, 0, 1, 0],
+ bitstr = <<2#1010:4>>,
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/testWSParamClass.erl b/lib/asn1/test/testWSParamClass.erl
new file mode 100644
index 0000000000..ae67ca8b81
--- /dev/null
+++ b/lib/asn1/test/testWSParamClass.erl
@@ -0,0 +1,17 @@
+-module(testWSParamClass).
+-export([main/1]).
+
+main(_) ->
+ IF = 'InformationFramework',
+ roundtrip({'Attribute',IF:'id-at-objectClass'(),
+ [IF:'id-at-objectClass'()],
+ asn1_NOVALUE}),
+ roundtrip({'Attribute',IF:'id-at-objectClass'(),
+ [],[]}),
+ ok.
+
+roundtrip(Data) ->
+ IF = 'InformationFramework',
+ {ok,Enc} = asn1_wrapper:encode(IF, 'Attribute', Data),
+ {ok,Data} = IF:decode('Attribute', Enc),
+ ok.
diff --git a/lib/asn1/test/testX420.erl b/lib/asn1/test/testX420.erl
index b222b15204..70bdb0640d 100644
--- a/lib/asn1/test/testX420.erl
+++ b/lib/asn1/test/testX420.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
@@ -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} | Options]),
diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl
index 4e732308d8..179299c78d 100644
--- a/lib/asn1/test/test_compile_options.erl
+++ b/lib/asn1/test/test_compile_options.erl
@@ -51,14 +51,13 @@ path(Config) ->
{ok,CWD} = file:get_cwd(),
?line file:set_cwd(filename:join([DataDir,subdir])),
- %%?line ok=asn1ct:compile(filename:join([DataDir,"../MyMerge.set.asn"]),[{inline,mymerge},{outdir,OutDir}]),
- ?line ok=asn1ct:compile("../MyMerge.set.asn",[{inline,mymerge},{outdir,OutDir}]),
+ ok = asn1ct:compile("../MyMerge.set.asn",[{outdir,OutDir}]),
?line ok=outfiles_check(OutDir),
?line outfiles_remove(OutDir),
file:set_cwd(filename:join([DataDir,subdir,subsubdir])),
- ?line ok = asn1ct:compile('../../MyMerge.set.asn',[{inline,mymerge},{i,'..'},{outdir,OutDir}]),
+ ok = asn1ct:compile('../../MyMerge.set.asn',[{i,'..'},{outdir,OutDir}]),
?line ok=outfiles_check(OutDir,outfiles2()),
file:set_cwd(CWD),
@@ -92,7 +91,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} =
@@ -181,11 +181,10 @@ outfiles_check(OutDir,[H|T]) ->
outfiles_check(OutDir,T).
outfiles1() ->
- ["mymerge.erl","mymerge.beam","MyMerge.asn1db","MyMerge.beam",
+ ["MyMerge.asn1db","MyMerge.beam",
"MyMerge.erl","MyMerge.hrl"].
outfiles2() ->
- ["MyMerge.beam","mymerge.erl","MyMerge.asn1db","MyMerge.erl",
- "mymerge.beam"].
+ ["MyMerge.beam","MyMerge.asn1db","MyMerge.erl"].
outfiles_remove(OutDir) ->
lists:foreach(fun(F)-> file:delete(filename:join([OutDir,F])) end,
diff --git a/lib/asn1/test/test_inline.erl b/lib/asn1/test/test_inline.erl
deleted file mode 100644
index 62625572e3..0000000000
--- a/lib/asn1/test/test_inline.erl
+++ /dev/null
@@ -1,270 +0,0 @@
-%%
-%% %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%
-%%
-%%
--module(test_inline).
-
--export([compile/2,main/2,inline1/3,performance/1,performance2/0]).
--export([mvrasn_inlined_encdec/2,mvrasn_encdec/2,
- mi_encdec/2,m_encdec/2]).
-
--include_lib("test_server/include/test_server.hrl").
--define(times, 5000).
--define(times2, 50000).
-
-compile(Config, Options) ->
- CaseDir = ?config(case_dir, Config),
- asn1_test_lib:compile("Mvrasn.set.asn", Config, [{inline, mvrasn_inlined}|Options]),
- asn1_test_lib:compile("Mod.set.asn", Config, [{inline, m}|Options]),
- ok = remove_inlined_files(CaseDir, [filename:join([CaseDir, X])||X<-["m.erl", "m.beam"]]),
- asn1_test_lib:compile("Mod.set.asn", Config, [inline|Options]),
- ok = remove_inlined_files(CaseDir, []).
-
-inline1(Config, Rule, Opt) ->
- CaseDir = ?config(case_dir, Config),
-
- asn1_test_lib:compile("P-Record", Config, [{inline, 'inlined_P_Record'}|Opt]),
- test_inline1(),
-
- ok=remove_inlined_files2(CaseDir, ber_bin_v2),
-
- case Rule of
- ber_bin_v2 ->
- asn1_test_lib:compile("P-Record", Config,
- [ber_bin, inline, asn1config, optimize|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]),
- test_inline2(Rule, 'p_record'),
- remove_inlined_files4(CaseDir, Rule);
- _ ->
- ok
- end.
-
-main(Config, _Erule) ->
- Val = val(Config),
- ?line {ok,Bytes}=asn1_wrapper:encode(mvrasn_inlined,'InsertSubscriberDataArg',Val),
- ?line {ok,_Val2}=asn1_wrapper:decode(mvrasn_inlined,'InsertSubscriberDataArg',Bytes).
-
-test_inline1() ->
- PRecMsg = {'PersonnelRecord',{'Name',"Sven","S","Svensson"},
- "manager",123,"20000202",{'Name',"Inga","K","Svensson"},
- asn1_DEFAULT},
- ?line {ok,Bytes}=asn1_wrapper:encode('inlined_P_Record','PersonnelRecord',
- PRecMsg),
- ?line {ok,_}=asn1_wrapper:decode('inlined_P_Record',
- 'PersonnelRecord',Bytes).
-
-test_inline2(ber_bin_v2,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));
-test_inline2(_,_) ->
- ok.
-
-val(Config) ->
- {ok,Val} = asn1ct:value('Mvrasn','InsertSubscriberDataArg',
- [{i, ?config(case_dir, Config)}]),
- Val.
-
-performance(Config) ->
- Val = val(Config),
- %% warm up
- timer:tc(?MODULE,mvrasn_inlined_encdec,[2,Val]),
- %% performance test
- ?line {Time1,ok}=timer:tc(?MODULE,mvrasn_inlined_encdec,[?times,Val]),
- %% warm up
- timer:tc(?MODULE,mvrasn_encdec,[2,Val]),
- %% performance test
- ?line {Time2,ok}=timer:tc(?MODULE,mvrasn_encdec,[?times,Val]),
-
- ?line Comment = "inlined_code: "++
- integer_to_list(round(Time1/?times))++
- " micro,<br>original_code: "++
- integer_to_list(round(Time2/?times))++
-% " micro,~ninlined_code[inline]: "++
-% integer_to_list(round(Time3/?times))++
- " micro",
- {comment,Comment}.
-
-
-mvrasn_inlined_encdec(0,_) ->
- ok;
-mvrasn_inlined_encdec(N,V) ->
- ?line {ok,B}=mvrasn_inlined:encode('InsertSubscriberDataArg',V),
- ?line {ok,_R}=mvrasn_inlined:decode('InsertSubscriberDataArg',B),
- mvrasn_inlined_encdec(N-1,V).
-
-mvrasn_encdec(0,_) ->
- ok;
-mvrasn_encdec(N,V) ->
- ?line {ok,B}='Mvrasn-11-6':encode('InsertSubscriberDataArg',V),
- ?line {ok,_R}='Mvrasn-11-6':decode('InsertSubscriberDataArg',B),
- mvrasn_encdec(N-1,V).
-
-%% mvrasn_inlined_i_encdec(0,_) ->
-%% ok;
-%% mvrasn_inlined_i_encdec(N,V) ->
-%% {ok,B}=mvrasn_inlined_i:encode('InsertSubscriberDataArg',V),
-%% {ok,_R}=mvrasn_inlined_i:decode('InsertSubscriberDataArg',B),
-%% mvrasn_inlined_i_encdec(N-1,V).
-
-performance2() ->
- Val = mval(),
- %% warm up
- timer:tc(?MODULE,mi_encdec,[?times,Val]),
- %% performance test
- {Time1,_R1}=timer:tc(?MODULE,mi_encdec,[?times2,Val]),
- %% warm up
- timer:tc(?MODULE,m_encdec,[?times,Val]),
- %% performance test
- {Time2,_R2}=timer:tc(?MODULE,m_encdec,[?times2,Val]),
- ?line Comment = "inlined_code: "++
- integer_to_list(round(Time1/?times2))++
- " micro,<br>original_code: "++
- integer_to_list(round(Time2/?times2))++
- " micro<br>"++
- "The inlined code was "++
- integer_to_list(round(((Time2-Time1)/Time2)*100))++
- " % faster than the original code.",
- {comment,Comment}.
-
-mi_encdec(0,_) ->
- ok;
-mi_encdec(N,Val) ->
- {ok,B}=m:encode('L',Val),
- {ok,_R}=m:decode('L',B),
-% io:format("a"),
- mi_encdec(N-1,Val).
-
-m_encdec(0,_) ->
- ok;
-m_encdec(N,Val) ->
- {ok,B}='Mod':encode('L',Val),
- {ok,_R}='Mod':decode('L',B),
- m_encdec(N-1,Val).
-
-
--record('L', {country, region, name}).
--record('OtherName', {locationName, thingName}).
--record('FamilyName', {prefix, secondname}).
--record('Lang', {l}).
--record('Inhabitant', {name, country}).
--record('Country', {name, language}).
--record('PersonName', {name1, name2}).
--record('LocName', {region, name}).
--record('Reg', {name, inhabitants}).
-
-
-mval() ->
- 'L'().
-'L'() ->
- #'L'{
- country='Co'(),
- region='Reg'(),
- name='Name'(othername)}.
-'Co'() ->
- 'Country'().
-'Country'()->
- #'Country'{name='Name'(othername),
- language='Lang'()}.
-'Lang'()->
- #'Lang'{l="englsh"}.
-'Reg'() ->
- #'Reg'{
- name='Name'(othername),
- inhabitants='Inhabitants'()}.
-'Inhabitants'()->
- lists:duplicate(5,'Inhabitant'()).
-'Inhabitant'()->
- #'Inhabitant'{name='Name'(person),
- country='Country'()}.
-'Name'(person) ->
- {person,'PersonName'()};
-'Name'(othername) ->
- {othername,'OtherName'()}.
-'PersonName'()->
- #'PersonName'{name1='FirstName'(firstname),
- name2='FamilyName'()}.
-'OtherName'()->
- #'OtherName'{locationName='LocName'(),
- thingName='ThingName'()}.
-'FirstName'(firstname)->
- {firstname,"Henry"};
-'FirstName'(nickname) ->
- {nickname,"nick"}.
-'FamilyName'() ->
- #'FamilyName'{prefix=none,
- secondname="Lloyd"}.
-'ThingName'()->
- "Enkoping".
-'LocName'()->
- #'LocName'{
- region=svealand,
- name="Enkoping"}.
-
-remove_inlined_files(Dir,Files) ->
- ModList=[filename:join([Dir,X])||X<-["Mod"]],
- FileList=Files++ mods2files(ModList,".asn1db")++
- mods2files(ModList,".beam")++
- mods2files(ModList,".erl")++mods2files(ModList,".hrl"),
- lists:foreach(fun(X) ->
- io:format("X: ~p~n",[X]),
- ?line ok=file:delete(X)
- end,FileList),
- ok.
-mods2files(ModList,Extension) ->
- [X++Extension||X<-ModList].
-
-
-remove_inlined_files2(Dir,Rule) ->
- ?line ok=remove_inlined_files3(Dir,Rule),
- TargetErl=filename:join([Dir,"inlined_P_Record.erl"]),
- TargetBeam=filename:join([Dir,"inlined_P_Record.beam"]),
- lists:foreach(fun(X) ->
- ?line ok=file:delete(X)
- end,[TargetErl,TargetBeam]),
- ok.
-remove_inlined_files3(Dir,ber_bin_v2) ->
- Erl=filename:join([Dir,"P-Record.erl"]),
- Beam=filename:join([Dir,"P-Record.beam"]),
- Asn1DB=filename:join([Dir,"P-Record.asn1db"]),
- Hrl=filename:join([Dir,"P-Record.hrl"]),
- lists:foreach(fun(X) ->
- ?line ok=file:delete(X)
- end,[Erl,Beam,Asn1DB,Hrl]),
- ok;
-remove_inlined_files3(_,_) ->
- ok.
-
-remove_inlined_files4(Dir,ber_bin_v2) ->
- Erl=filename:join([Dir,"p_record.erl"]),
- Beam=filename:join([Dir,"p_record.beam"]),
- Asn1DB=filename:join([Dir,"p_record.asn1db"]),
- Hrl=filename:join([Dir,"p_record.hrl"]),
- ErlBak=filename:join([Dir,"p_record.erl.bak"]),
- file:delete(ErlBak),
- lists:foreach(fun(X) ->
- ?line ok=file:delete(X)
- end,[Erl,Beam,Asn1DB,Hrl]),
- ok;
-remove_inlined_files4(_,_) ->
- ok.
diff --git a/lib/asn1/test/test_modified_x420.erl b/lib/asn1/test/test_modified_x420.erl
index ae9d1989fb..a525fd6ae1 100644
--- a/lib/asn1/test/test_modified_x420.erl
+++ b/lib/asn1/test/test_modified_x420.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
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 b4b4e0b431..3c4f3ff122 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.8.1
+ASN1_VSN = 2.0.1.1
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index b6d4a633cb..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>2012</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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>
@@ -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>
diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml
index 803a71de07..4fa92d5583 100644
--- a/lib/common_test/doc/src/cover_chapter.xml
+++ b/lib/common_test/doc/src/cover_chapter.xml
@@ -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_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 27d56fd47d..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>
@@ -439,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
@@ -455,9 +455,9 @@ terminate(State) ->
using the normal SASL mechanisms. </cell>
</row>
<row>
- <cell>cth_surefire</cell>
- <cell>no</cell>
- <cell><p>Captures all test results and outputs them as surefire
+ <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>
@@ -467,13 +467,14 @@ terminate(State) ->
<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 a constructed from the <c>url_base</c> and a relative path
+ 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>
+ <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>
+ <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>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index c6749d6960..d871908952 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>2007</year><year>2012</year>
+ <year>2007</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -104,6 +104,7 @@
[-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]
@@ -125,6 +126,7 @@
<title>Run tests using test specification</title>
<pre>
ct_run -spec TestSpec1 TestSpec2 .. TestSpecN
+ [-join_specs]
[-config ConfigFile1 ConfigFile2 .. ConfigFileN]
[-userconfig CallbackModule1 ConfigString1 and CallbackModule2
ConfigString2 and .. and CallbackModuleN ConfigStringN]
@@ -138,6 +140,7 @@
[-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]
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 8c3b13951d..5d2c065385 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,295 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If an event handler installed in the CT Master event
+ manager took too long to respond during the termination
+ phase, CT Master crashed because of a timeout after 5
+ secs. This would leave the system in a bad state. The
+ problem has been solved by means of a 30 min timeout
+ value and if CT Master gets a timeout after that time, it
+ now kills the event manager and shuts down properly.</p>
+ <p>
+ Own Id: OTP-10634 Aux Id: kunagi-347 [258] </p>
+ </item>
+ <item>
+ <p>
+ Printing with any of the ct printout functions from an
+ event handler installed by Common Test, would cause a
+ deadlock. This problem has been solved.</p>
+ <p>
+ Own Id: OTP-10826 Aux Id: seq12250 </p>
+ </item>
+ <item>
+ <p>
+ Using the force_stop flag/option to interrupt a test run
+ caused a crash in Common Test. This problem has been
+ solved.</p>
+ <p>
+ Own Id: OTP-10832</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Removed depricated run_test program, use ct_run instead.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-9052</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Test case execution time increases with size of test run.</p>
+ <p>
+ Own Id: OTP-10855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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>
+ 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>
+ 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>When a test case failed because of a timetrap time
+ out, the <c>Config</c> data for the case was lost in the
+ following call to <c>end_per_testcase/2</c>, and also in
+ calls to the CT Hook function
+ <c>post_end_per_testcase/4</c>. This problem has been
+ solved and the <c>Config</c> data is now correctly passed
+ to the above functions after a timetrap timeout
+ failure.</p>
+ <p>
+ Own Id: OTP-10070 Aux Id: kunagi-175 [86] </p>
+ </item>
+ <item>
+ <p>
+ Some calls to deprecated and removed functions in snmp
+ are removed from ct_snmp.</p>
+ <p>
+ Own Id: OTP-10088 Aux Id: kunagi-176 [87] </p>
+ </item>
+ <item>
+ <p>In test_server, the same process would supervise the
+ currently running test case and be group leader (and IO
+ server) for the test case. Furthermore, when running
+ parallel test cases, new temporary supervisor/group
+ leader processes were spawned and the process that was
+ group leader for sequential test cases would not be
+ active. That would lead to several problems:</p>
+ <p>* Processes started by init_per_suite will inherit the
+ group leader of the init_per_suite process (and that
+ group leader would not process IO requests when parallel
+ test cases was running). If later a parallel test case
+ caused such a processto print using (for example)
+ io:format/2, the calling would hang.</p>
+ <p>* Similarly, if a process was spawned from a parallel
+ test case, it would inherit the temporary group leader
+ for that parallel test case. If that spawned process
+ later - when the group of parallel tests have finished -
+ attempted to print something, its group leader would be
+ dead and there would be <c>badarg</c> exception.</p>
+ <p>Those problems have been solved by having group
+ leaders separate from the processes that supervises the
+ test cases, and keeping temporary group leader process
+ for parallel test cases alive until no more process in
+ the system use them as group leaders.</p>
+ <p>Also, a new <c>unexpected_io.log</c> log file
+ (reachable from the summary page of each test suite) has
+ been introduced. All unexpected IO will be printed into
+ it(for example, IO to a group leader for a parallel test
+ case that has finished).</p>
+ <p>
+ Own Id: OTP-10101 Aux Id: OTP-10125 </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>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>It is now possible to let a test specification include
+ other test specifications. Included specs can either be
+ joined with the source spec (and all other joined specs),
+ resulting in one single test run, or they can be executed
+ in separate test runs. Also, a start flag/option,
+ <c>join_specs</c>, has been introduced, to be used in
+ combination with the <c>spec</c> option. With
+ <c>join_specs</c>, Common Test can be told to either join
+ multiple test specifications, or run them separately.
+ Without <c>join_specs</c>, the latter behaviour is
+ default. Note that this is a change compared to earlier
+ versions of Common Test, where specifications could only
+ be joined. More information can be found in the Running
+ Tests chapter in the User's Guide (see the Test
+ Specifications section).</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-9881 Aux Id: kunagi-350 [261] </p>
+ </item>
+ <item>
+ <p>
+ The <c>ct_slave:start/3</c> function now supports an
+ <c>{env,[{Var,Value}]}</c> option to extend environment
+ for the slave node.</p>
+ <p>
+ Own Id: OTP-10469 Aux Id: kunagi-317 [228] </p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Update common test modules to handle unicode <list>
+ <item> Use UTF-8 encoding for all HTML files, except the
+ HTML version of the test suite generated with
+ erl2html2:convert, which will have the same encoding as
+ the original test suite (.erl) file. </item> <item>
+ Encode link targets in HTML files with
+ test_server_ctrl:uri_encode/1. </item> <item> Use unicode
+ modifier 't' with ~s when appropriate. </item> <item> Use
+ unicode:characters_to_list and
+ unicode:characters_to_binary for conversion between
+ binaries and strings instead of binary_to_list and
+ list_to_binary. </item> </list></p>
+ <p>
+ Own Id: OTP-10783</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <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>
+ Event handler on a ct_master node causes hanging.</p>
+ <p>
+ Own Id: OTP-10634 Aux Id: kunagi-347 [258] </p>
+ </item>
+ <item>
+ <p>
+ CT fails to open telnet conn after a timetrap timeout.</p>
+ <p>
+ Own Id: OTP-10648 Aux Id: seq12212 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.6.3.1</title>
<section><title>Known Bugs and Problems</title>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index a0b2c96006..35f89153d3 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>
@@ -155,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
@@ -528,369 +530,469 @@
<marker id="test_specifications"></marker>
<section>
<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 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 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 (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
- specifies a set of test cases will "swallow" one that only
- specifies a subset of these cases. E.g. the result of merging
- one term that specifies that all cases in suite S should be
- executed, with another term specifying only test case X and Y in
- S, is a test of all cases in S. However, if a term specifying
- test case X and Y in S is merged with a term specifying case Z
- in S, the result is a test of X, Y and Z in S. To disable this
- behaviour, 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 log files as
- SKIPPED.</p>
- <p>When a test case group is specified, the resulting test
- executes the <c>init_per_group</c> function, followed by all test
- cases and sub groups (including their configuration functions), and
- finally the <c>end_per_group</c> function. Also if particular
- test cases in a group are specified, <c>init_per_group</c>
- and <c>end_per_group</c> for the group in question are
- called. If a group which is defined (in <c>Suite:group/0</c>) to
- be a sub group of another group, is specified (or 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>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. 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 <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 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> (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}.
- {init, [NodeAlias], InitOptions}.
-
- {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}.
-
- {scale_timetraps, Bool}.
- {scale_timetraps, NodeRefs, Bool}.
-
- {cover, CoverSpecFile}.
- {cover, NodeRefs, CoverSpecFile}.
-
- {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}}.
-
- {logdir, LogDir}.
- {logdir, NodeRefs, LogDir}.
-
- {logopts, LogOpts}.
- {logopts, NodeRefs, LogOpts}.
-
- {create_priv_dir, PrivDirOption}.
- {create_priv_dir, NodeRefs, PrivDirOption}.
-
- {event_handler, EventHandlers}.
- {event_handler, NodeRefs, EventHandlers}.
- {event_handler, EventHandlers, InitArgs}.
- {event_handler, NodeRefs, EventHandlers, InitArgs}.
-
- {ct_hooks, CTHModules}.
- {ct_hooks, NodeRefs, CTHModules}.
-
- {enable_builtin_hooks, Bool}.
-
- {basic_html, Bool}.
- {basic_html, NodeRefs, Bool}.
+ <section>
+ <title>General description</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 are normally declared in one or more text files
+ (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 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 (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
+ specifies a set of test cases will "swallow" one that only
+ specifies a subset of these cases. E.g. the result of merging
+ one term that specifies that all cases in suite S should be
+ executed, with another term specifying only test case X and Y in
+ S, is a test of all cases in S. However, if a term specifying
+ test case X and Y in S is merged with a term specifying case Z
+ in S, the result is a test of X, Y and Z in S. To disable this
+ behaviour, 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 log files as
+ SKIPPED.</p>
+ </section>
+ <section>
+ <title>Using multiple test specification files</title>
+
+ <p>When multiple test specification files are given at startup (either
+ with <c>ct_run -spec file1 file2 ...</c> or
+ <c>ct:run_test([{spec, [File1,File2,...]}])</c>),
+ Common Test will either execute one test run per specification file, or
+ join the files and perform all tests within one single test run. The first
+ behaviour is the default one. The latter requires that the start
+ flag/option <c>join_suites</c> is provided, e.g.
+ <c>run_test -spec ./my_tests1.ts ./my_tests2.ts -join_suites</c>.</p>
+
+ <p>Joining a number of specifications, or running them separately, can
+ also be accomplished with (and may be combined with) test specification
+ file inclusion, described next.</p>
+ </section>
+ <section>
+ <title>Test specification file inclusion</title>
+ <p>With the <c>specs</c> term (see syntax below), it's possible to have
+ a test specification include other specifications. An included
+ specification may either be joined with the source specification,
+ or used to produce a separate test run (like with the <c>join_specs</c>
+ start flag/option above). Example:</p>
+ <pre>
+ %% In specification file "a.spec"
+ {specs, join, ["b.spec", "c.spec"]}.
+ {specs, separate, ["d.spec", "e.spec"]}.
+ %% Config and test terms follow
+ ...</pre>
+ <p>In this example, the test terms defined in files "b.spec" and "c.spec"
+ will be joined with the terms in the source specification "a.spec"
+ (if any). The inclusion of specifications "d.spec" and
+ "e.spec" will result in two separate, and independent, test runs (i.e.
+ one for each included specification).</p>
+ <p>Note that the <c>join</c> option does not imply that the test terms
+ will be merged (see <c>merge_tests</c> above), only that all tests are
+ executed in one single test run.</p>
+ <p>Joined specifications share common configuration settings, such as
+ the list of <c>config</c> files or <c>include</c> directories.
+ For configuration that can not be combined, such as settings for <c>logdir</c>
+ or <c>verbosity</c>, it is up to the user to ensure there are no clashes
+ when the test specifications are joined. Specifications included with
+ the <c>separate</c> option, do not share configuration settings with the
+ source specification. This is useful e.g. if there are clashing
+ configuration settings in included specifications, making it impossible
+ to join them.</p>
+ <p>If <c>{merge_tests,true}</c> is set in the source specification
+ (which is the default setting), terms in joined specifications will be
+ merged with terms in the source specification (according to the
+ description of <c>merge_tests</c> above).</p>
+ <p>Note that it is always the <c>merge_tests</c> setting in the source
+ specification that is used when joined with other specifications.
+ Say e.g. that a source specification A, with tests TA1 and TA2, has
+ <c>{merge_tests,false}</c> set, and it includes another specification,
+ B, with tests TB1 and TB2, that has <c>{merge_tests,true}</c> set.
+ The result will be that the test series: <c>TA1,TA2,merge(TB1,TB2)</c>,
+ is executed. The opposite <c>merge_tests</c> settings would result in the
+ following the test series: <c>merge(merge(TA1,TA2),TB1,TB2)</c>.</p>
+ <p>The <c>specs</c> term may of course be used to nest specifications,
+ i.e. have one specification include other specifications, which in turn
+ include others, etc.</p>
+ </section>
+ <section>
+ <title>Test case groups</title>
+
+ <p>When a test case group is specified, the resulting test
+ executes the <c>init_per_group</c> function, followed by all test
+ cases and sub groups (including their configuration functions), and
+ finally the <c>end_per_group</c> function. Also if particular
+ test cases in a group are specified, <c>init_per_group</c>
+ and <c>end_per_group</c> for the group in question are
+ called. If a group which is defined (in <c>Suite:group/0</c>) to
+ be a sub group of another group, is specified (or 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>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. 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>
+ </section>
- {release_shell, Bool}.</pre>
+ <section>
+ <title>Test specification syntax</title>
+
+ <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 <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 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> (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>
+ </section>
+ <p>Config terms:</p>
+ <pre>
+ {merge_tests, Bool}.
+
+ {define, Constant, Value}.
+
+ {specs, InclSpecsOption, TestSpecs}.
+
+ {node, NodeAlias, Node}.
+
+ {init, InitOptions}.
+ {init, [NodeAlias], InitOptions}.
+
+ {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}.
+
+ {scale_timetraps, Bool}.
+ {scale_timetraps, NodeRefs, Bool}.
+
+ {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}}.
+
+ {logdir, LogDir}.
+ {logdir, NodeRefs, LogDir}.
+
+ {logopts, LogOpts}.
+ {logopts, NodeRefs, LogOpts}.
+
+ {create_priv_dir, PrivDirOption}.
+ {create_priv_dir, NodeRefs, PrivDirOption}.
+
+ {event_handler, EventHandlers}.
+ {event_handler, NodeRefs, EventHandlers}.
+ {event_handler, EventHandlers, InitArgs}.
+ {event_handler, NodeRefs, EventHandlers, InitArgs}.
+
+ {ct_hooks, CTHModules}.
+ {ct_hooks, NodeRefs, CTHModules}.
+
+ {enable_builtin_hooks, Bool}.
+
+ {basic_html, Bool}.
+ {basic_html, NodeRefs, Bool}.
+
+ {release_shell, Bool}.</pre>
+
<p>Test terms:</p>
- <pre>
- {suites, Dir, Suites}.
- {suites, NodeRefs, Dir, Suites}.
-
- {groups, Dir, Suite, Groups}.
- {groups, NodeRefs, Dir, Suite, Groups}.
-
- {groups, Dir, Suite, Groups, {cases,Cases}}.
- {groups, NodeRefs, Dir, Suite, Groups, {cases,Cases}}.
-
- {cases, Dir, Suite, Cases}.
- {cases, NodeRefs, Dir, Suite, Cases}.
-
- {skip_suites, Dir, Suites, Comment}.
- {skip_suites, NodeRefs, Dir, Suites, Comment}.
-
- {skip_groups, Dir, Suite, GroupNames, Comment}.
- {skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}.
-
- {skip_cases, Dir, Suite, Cases, Comment}.
- {skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.</pre>
-
+ <pre>
+ {suites, Dir, Suites}.
+ {suites, NodeRefs, Dir, Suites}.
+
+ {groups, Dir, Suite, Groups}.
+ {groups, NodeRefs, Dir, Suite, Groups}.
+
+ {groups, Dir, Suite, Groups, {cases,Cases}}.
+ {groups, NodeRefs, Dir, Suite, Groups, {cases,Cases}}.
+
+ {cases, Dir, Suite, Cases}.
+ {cases, NodeRefs, Dir, Suite, Cases}.
+
+ {skip_suites, Dir, Suites, Comment}.
+ {skip_suites, NodeRefs, Dir, Suites, Comment}.
+
+ {skip_groups, Dir, Suite, GroupNames, Comment}.
+ {skip_groups, NodeRefs, Dir, Suite, GroupNames, Comment}.
+
+ {skip_cases, Dir, Suite, Cases, Comment}.
+ {skip_cases, NodeRefs, Dir, Suite, Cases, Comment}.</pre>
+
<p>Types:</p>
- <pre>
- 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>
- {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>
-
- <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}.
-
- %% 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>
+ Bool = true | false
+ Constant = atom()
+ Value = term()
+ InclSpecsOption = join | separate
+ TestSpecs = string() | [string()]
+ 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>
+
+ <section>
+ <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>
+ {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>
+ </section>
- <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"}.
+ <section>
+ <title>Constants</title>
+
+ <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}.
+
+ %% 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>
+ </section>
- {logdir, "'Top'/logs"}.
-
- {config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}.
-
- {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"}.
-
- {suites, 'T2', [t2B_SUITE,t2C_SUITE]}.
- {cases, 'T2', t2A_SUITE, [test4,test1,test7]}.
-
- {skip_suites, 'T3', all, "Not implemented"}.</pre>
+ <section>
+ <title>Example</title>
+
+ <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"}.
+
+ {config, ["'T1'/'CfgFile'", "'T2'/'CfgFile'", "'T3'/'CfgFile'"]}.
+
+ {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"}.
+
+ {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
+ the HTML log files (in subdirectories tagged with node name,
+ date and time).</item>
+ <item>The variables in the specified test system config files will be
+ imported for the 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
+ the test.</item>
+ <item>Secondly, the test for system t2 should run. The included suites are
+ t2B and t2C. Included are also test cases test4, test1 and test7 in suite
+ t2A. Note that the test cases will be executed in the specified order.</item>
+ <item>Lastly, all suites for systems t3 are to be completely skipped and this
+ should be explicitly noted in the log files.</item>
+ </list>
+ </section>
- <p>The example specifies the following:</p>
- <list>
- <item>The specified logdir directory will be used for storing
- the HTML log files (in subdirectories tagged with node name,
- date and time).</item>
- <item>The variables in the specified test system config files will be
- imported for the 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
- the test.</item>
- <item>Secondly, the test for system t2 should run. The included suites are
- t2B and t2C. Included are also test cases test4, test1 and test7 in suite
- t2A. Note that the test cases will be executed in the specified order.</item>
- <item>Lastly, all suites for systems t3 are to be completely skipped and this
- should be explicitly noted in the log files.</item>
- </list>
- <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
- includes (for Common Test) unrecognizable terms. If this is desired,
- the <c>-allow_user_terms</c> flag should be used when starting tests with
- <c>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><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>
+ <title>The init term</title>
+ <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>
+ </section>
+ <section>
+ <title>User specific terms</title>
+ <p>It is possible for the user to provide a test specification that
+ includes (for Common Test) unrecognizable terms. If this is desired,
+ the <c>-allow_user_terms</c> flag should be used when starting tests with
+ <c>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><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>
<section>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 248d7de8b6..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>
@@ -982,38 +982,36 @@
<p>Example:</p>
<pre>
+ Some printouts during test case execution:
- 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]),
- 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:
- If starting the test without specifying any verbosity levels:
+ $ ct_run ...
- $ ct_run ...
+ the following gets printed:
- 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:
+ 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>
+ 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>
diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in
index 4372ab124e..5a9fabbe45 100644
--- a/lib/common_test/priv/Makefile.in
+++ b/lib/common_test/priv/Makefile.in
@@ -68,15 +68,15 @@ JS = jquery-latest.js jquery.tablesorter.min.js
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)
# ----------------------------------------------------
diff --git a/lib/common_test/priv/auxdir/config.guess b/lib/common_test/priv/auxdir/config.guess
index 38a833903b..f475ceb413 100755
--- a/lib/common_test/priv/auxdir/config.guess
+++ b/lib/common_test/priv/auxdir/config.guess
@@ -1,14 +1,12 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-05-17'
+timestamp='2013-02-12'
# 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
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@@ -17,26 +15,22 @@ timestamp='2007-05-17'
# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
#
-# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# Please send patches with a ChangeLog entry to [email protected].
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -56,8 +50,7 @@ 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.
+Copyright 1992-2013 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."
@@ -144,7 +137,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
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*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -170,7 +163,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 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
+ | grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -180,7 +173,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
;;
esac
# The OS release
@@ -201,6 +194,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -223,7 +220,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -269,7 +266,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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 ;;
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
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
@@ -295,12 +295,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-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:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -324,14 +324,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
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/[^.]*//'`
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
@@ -375,23 +394,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -461,8 +480,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ # 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 ] || \
@@ -475,7 +494,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -532,7 +551,7 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[45])
+ *:AIX:*:[4567])
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
@@ -575,52 +594,52 @@ EOF
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" ;;
+ 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
+ esac ;;
+ esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ sed 's/^ //' << EOF >$dummy.c
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
+ #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);
+ 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);
- }
+ 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
@@ -640,7 +659,7 @@ EOF
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
+ grep -q __LP64__
then
HP_ARCH="hppa2.0w"
else
@@ -711,22 +730,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -750,14 +769,14 @@ EOF
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 ;;
+ 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}"
+ 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}
@@ -769,40 +788,51 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
amd64)
echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
+ exit ;;
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
+ i*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ # 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)
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
- EM64T | authenticamd)
+ authenticamd | genuineintel | EM64T)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
+ IA64)
+ echo ia64-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 ;;
+ 8664:Windows_NT:*)
+ echo x86_64-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
@@ -832,20 +862,68 @@ EOF
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
- arm*:Linux:*:*)
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
echo ${UNAME_MACHINE}-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 -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -856,74 +934,36 @@ EOF
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:*:*)
+ mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
- #undef mips64
- #undef mips64el
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
+ CPU=${UNAME_MACHINE}el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
+ CPU=${UNAME_MACHINE}
#else
CPU=
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
+ or1k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
+ padre:Linux:*:*)
+ echo sparc-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}
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
@@ -933,14 +973,17 @@ EOF
*) echo hppa-unknown-linux-gnu ;;
esac
exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -948,81 +991,18 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- tile:Linux:*:*)
- echo tile-unknown-linux-gnu
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-gnu
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- xtensa:Linux:*:*)
- echo xtensa-unknown-linux-gnu
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-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
@@ -1030,11 +1010,11 @@ EOF
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,
+ # 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.
+ # Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1051,7 +1031,7 @@ EOF
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
@@ -1066,7 +1046,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1094,10 +1074,13 @@ EOF
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 ;;
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1132,8 +1115,18 @@ EOF
/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; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ 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; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
@@ -1146,7 +1139,7 @@ EOF
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
@@ -1166,10 +1159,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- echo i586-unisys-sysv4
- 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
@@ -1195,11 +1188,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv${UNAME_RELEASE}
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1209,6 +1202,12 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1236,6 +1235,16 @@ EOF
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
unknown) UNAME_PROCESSOR=powerpc ;;
esac
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1251,7 +1260,10 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
@@ -1296,13 +1308,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ 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`
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1317,11 +1329,14 @@ EOF
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ 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_
@@ -1339,11 +1354,11 @@ main ()
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
- "4"
+ "4"
#else
- ""
+ ""
#endif
- ); exit (0);
+ ); exit (0);
#endif
#endif
@@ -1477,9 +1492,9 @@ 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
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
If the version you run ($0) is already up to date, please
send the following data and any information you think might be
diff --git a/lib/common_test/priv/auxdir/config.sub b/lib/common_test/priv/auxdir/config.sub
index f43233b104..bb6edbdb47 100755
--- a/lib/common_test/priv/auxdir/config.sub
+++ b/lib/common_test/priv/auxdir/config.sub
@@ -1,44 +1,40 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-04-29'
+timestamp='2013-02-12'
-# 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
+# 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 3 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.
+# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Please send patches with a ChangeLog entry to [email protected].
#
# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
# 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.
@@ -72,8 +68,7 @@ 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.
+Copyright 1992-2013 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."
@@ -120,12 +115,18 @@ esac
# 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* | \
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -148,10 +149,13 @@ case $os in
-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)
+ -apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
+ -bluegene*)
+ os=-cnk
+ ;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
@@ -166,10 +170,10 @@ case $os in
os=-chorusos
basic_machine=$1
;;
- -chorusrdb)
- os=-chorusrdb
+ -chorusrdb)
+ os=-chorusrdb
basic_machine=$1
- ;;
+ ;;
-hiux*)
os=-hiuxwe2
;;
@@ -214,6 +218,12 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
-lynx*)
os=-lynxos
;;
@@ -238,24 +248,34 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 | aarch64_be \
| 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 \
+ | arc \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | be32 | be64 \
| bfin \
| c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
+ | epiphany \
| fido | fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
- | mips64vr | mips64vrel \
+ | mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
@@ -266,31 +286,45 @@ case $basic_machine in
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
+ | moxie \
| mt \
| msp430 \
- | nios | nios2 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | or32 \
+ | open8 \
+ | or1k | or32 \
| pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
+ | rl78 | rx \
| score \
- | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | 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 \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
- | z8k)
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
basic_machine=$basic_machine-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -300,6 +334,21 @@ case $basic_machine in
basic_machine=mt-unknown
;;
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-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.
@@ -314,29 +363,37 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* | aarch64_be-* \
| 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-* \
+ | be32-* | be64-* \
| bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
| 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-* \
+ | hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
+ | mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
@@ -347,31 +404,41 @@ case $basic_machine in
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
- | nios-* | nios2-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | 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-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
| tron-* \
- | v850-* | v850e-* | vax-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
| we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
- | xstormy16-* | xtensa-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
| ymp-* \
- | z8k-*)
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -389,7 +456,7 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
- abacus)
+ abacus)
basic_machine=abacus-unknown
;;
adobe68k)
@@ -435,6 +502,10 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-bsd
;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -443,10 +514,35 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -475,8 +571,8 @@ case $basic_machine in
basic_machine=craynv-cray
os=-unicosmp
;;
- cr16c)
- basic_machine=cr16c-unknown
+ cr16 | cr16-*)
+ basic_machine=cr16-unknown
os=-elf
;;
crds | unos)
@@ -514,6 +610,10 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
@@ -629,7 +729,6 @@ case $basic_machine in
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
@@ -668,6 +767,14 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
m88k-omron*)
basic_machine=m88k-omron
;;
@@ -679,6 +786,13 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
mingw32)
basic_machine=i386-pc
os=-mingw32
@@ -715,10 +829,18 @@ case $basic_machine in
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -783,6 +905,12 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@@ -813,6 +941,14 @@ case $basic_machine in
basic_machine=i860-intel
os=-osf
;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
pbd)
basic_machine=sparc-tti
;;
@@ -857,9 +993,10 @@ case $basic_machine in
;;
power) basic_machine=power-ibm
;;
- ppc) basic_machine=powerpc-unknown
+ ppc | ppcbe) basic_machine=powerpc-unknown
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
@@ -884,7 +1021,11 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
- rdos)
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
basic_machine=i386-pc
os=-rdos
;;
@@ -953,6 +1094,9 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sun2)
basic_machine=m68000-sun
;;
@@ -1009,17 +1153,9 @@ case $basic_machine in
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
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
;;
tx39)
basic_machine=mipstx39-unknown
@@ -1027,10 +1163,6 @@ case $basic_machine in
tx39el)
basic_machine=mipstx39el-unknown
;;
- tile*)
- basic_machine=tile-tilera
- os=-linux-gnu
- ;;
toad1)
basic_machine=pdp10-xkl
os=-tops20
@@ -1092,6 +1224,9 @@ case $basic_machine in
xps | xps100)
basic_machine=xps100-honeywell
;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1100,6 +1235,10 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
none)
basic_machine=none-none
os=-none
@@ -1138,7 +1277,7 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
- sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1185,9 +1324,12 @@ esac
if [ x"$os" != x"" ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
@@ -1208,21 +1350,23 @@ case $os in
# 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* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
+ | -aos* | -aros* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
+ | -bitrig* | -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* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
@@ -1230,7 +1374,7 @@ case $os in
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1269,7 +1413,7 @@ case $os in
-opened*)
os=-openedition
;;
- -os400*)
+ -os400*)
os=-os400
;;
-wince*)
@@ -1318,7 +1462,7 @@ case $os in
-sinix*)
os=-sysv4
;;
- -tpf*)
+ -tpf*)
os=-tpf
;;
-triton*)
@@ -1354,12 +1498,14 @@ case $os in
-aros*)
os=-aros
;;
- -kaos*)
- os=-kaos
- ;;
-zvmoe)
os=-zvmoe
;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
+ ;;
-none)
;;
*)
@@ -1382,10 +1528,10 @@ else
# system, and we'll never get to this point.
case $basic_machine in
- score-*)
+ score-*)
os=-elf
;;
- spu-*)
+ spu-*)
os=-elf
;;
*-acorn)
@@ -1397,8 +1543,20 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
- c4x-* | tic4x-*)
- os=-coff
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
@@ -1418,14 +1576,11 @@ case $basic_machine in
;;
m68000-sun)
os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
;;
m68*-cisco)
os=-aout
;;
- mep-*)
+ mep-*)
os=-elf
;;
mips*-cisco)
@@ -1434,6 +1589,9 @@ case $basic_machine in
mips*-*)
os=-elf
;;
+ or1k-*)
+ os=-elf
+ ;;
or32-*)
os=-coff
;;
@@ -1452,7 +1610,7 @@ case $basic_machine in
*-ibm)
os=-aix
;;
- *-knuth)
+ *-knuth)
os=-mmixware
;;
*-wec)
@@ -1557,7 +1715,7 @@ case $basic_machine in
-sunos*)
vendor=sun
;;
- -aix*)
+ -cnk*|-aix*)
vendor=ibm
;;
-beos*)
@@ -1628,3 +1786,4 @@ exit
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
+
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index dd2923ece9..4600c0ad78 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -1,7 +1,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
@@ -97,7 +97,7 @@ DTD_FILES = \
# ----------------------------------------------------
ERL_COMPILE_FLAGS += -pa ../ebin -I../include -I $(ERL_TOP)/lib/snmp/include/ \
-I../../test_server/include -I../../xmerl/inc/ \
- -I $(ERL_TOP)/lib/kernel/include
+ -I $(ERL_TOP)/lib/kernel/include -Werror
# ----------------------------------------------------
# Targets
@@ -127,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/ct.erl b/lib/common_test/src/ct.erl
index ad9bf4e2d6..04a95a53fa 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -1,7 +1,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
@@ -144,11 +144,11 @@ run(TestDirs) ->
%%% @spec run_test(Opts) -> Result
%%% Opts = [OptTuples]
%%% OptTuples = {dir,TestDirs} | {suite,Suites} | {group,Groups} |
-%%% {testcase,Cases} | {spec,TestSpecs} | {label,Label} |
-%%% {config,CfgFiles} | {userconfig, UserConfig} |
+%%% {testcase,Cases} | {spec,TestSpecs} | {join_specs,Bool} |
+%%% {label,Label} | {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} |
@@ -735,7 +735,7 @@ fail(Format, Args) ->
%%% overwrites the string set by this function.</p>
comment(Comment) when is_list(Comment) ->
Formatted =
- case (catch io_lib:format("~s",[Comment])) of
+ case (catch io_lib:format("~ts",[Comment])) of
{'EXIT',_} -> % it's a list not a string
io_lib:format("~p",[Comment]);
String ->
@@ -988,8 +988,9 @@ get_testdata(Key) ->
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
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index b1d709bc75..c35cbd3c08 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-2012. 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
@@ -266,7 +266,10 @@ read_config_files_int([{Callback, File}|Files], FunToSave) ->
read_config_files_int([], _FunToSave) ->
ok.
-store_config(Config, Callback, File) ->
+store_config(Config, Callback, File) when is_tuple(Config) ->
+ store_config([Config], Callback, File);
+
+store_config(Config, Callback, File) when is_list(Config) ->
[ets:insert(?attr_table,
#ct_conf{key=Key,
value=Val,
@@ -607,7 +610,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) ->
EncBin = crypto:des3_cbc_encrypt(K1, K2, K3, IVec, Bin2),
case file:write_file(EncryptFileName, EncBin) of
ok ->
- io:format("~s --(encrypt)--> ~s~n",
+ io:format("~ts --(encrypt)--> ~ts~n",
[SrcFileName,EncryptFileName]),
ok;
{error,Reason} ->
@@ -649,7 +652,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) ->
_ ->
case file:write_file(TargetFileName, SrcBin) of
ok ->
- io:format("~s --(decrypt)--> ~s~n",
+ io:format("~ts --(decrypt)--> ~ts~n",
[EncryptFileName,TargetFileName]),
ok;
{error,Reason} ->
@@ -700,7 +703,7 @@ get_crypt_key_from_file() ->
_ ->
case catch string:tokens(binary_to_list(Result), [$\n,$\r]) of
[Key] ->
- io:format("~nCrypt key file: ~s~n", [FullName]),
+ io:format("~nCrypt key file: ~ts~n", [FullName]),
Key;
_ ->
{error,{bad_crypt_file,FullName}}
diff --git a/lib/common_test/src/ct_config_plain.erl b/lib/common_test/src/ct_config_plain.erl
index 237df5c8f3..c6547f0a40 100644
--- a/lib/common_test/src/ct_config_plain.erl
+++ b/lib/common_test/src/ct_config_plain.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
@@ -47,7 +47,7 @@ read_config(ConfigFile) ->
{error,no_crypt_file} ->
{error,{config_file_error,
lists:flatten(
- io_lib:format("~s",[file:format_error(Reason)]))}};
+ io_lib:format("~ts",[file:format_error(Reason)]))}};
{error,CryptError} ->
{error,{decrypt_file_error,CryptError}};
_ when is_list(Key) ->
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
index d7bd18606b..ac08a3e0ad 100644
--- a/lib/common_test/src/ct_conn_log_h.erl
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -1,7 +1,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
@@ -52,7 +52,7 @@ open_files([],State) ->
do_open_files([{Tag,File}|Logs],Acc) ->
- case file:open(File, [write]) of
+ case file:open(File, [write,{encoding,utf8}]) of
{ok,Fd} ->
do_open_files(Logs,[{Tag,Fd}|Acc]);
{error,Reason} ->
@@ -98,9 +98,9 @@ terminate(_,#state{logs=Logs}) ->
%%% 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)]).
+ io:format(Fd,"~n~ts~ts~ts",[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
@@ -109,9 +109,10 @@ write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) ->
%% 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)])
+ io:format(Fd,"~n~ts~ts~ts",
+ [format_head(ConnMod,LogType,Time," ERROR"),
+ format_title(LogType,Info),
+ format_error(LogType,Report)])
end.
get_log(Info,State) ->
@@ -140,16 +141,16 @@ 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]);
+ io_lib:format("~n~w, ~w~ts, ",[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]).
+ io_lib:format("~n~ts",[Head]).
format_title(raw,#conn_log{client=Client}=Info) ->
- io_lib:format("Client ~p ~s ~s",[Client,actionstr(Info),serverstr(Info)]);
+ io_lib:format("Client ~w ~s ~ts",[Client,actionstr(Info),serverstr(Info)]);
format_title(_,Info) ->
Title = pad_char_end(?WIDTH,pretty_title(Info),$=),
- io_lib:format("~n~s", [Title]).
+ io_lib:format("~n~ts", [Title]).
format_data(_,_,NoData) when NoData == ""; NoData == <<>> ->
"";
@@ -162,8 +163,6 @@ format_error(pretty,Report) ->
[io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report].
-
-
%%%-----------------------------------------------------------------
%%% Helpers
conn_info(LoggingProc, #conn_log{client=undefined} = ConnInfo) ->
@@ -187,12 +186,12 @@ now_to_time({_,_,MicroS}=Now) ->
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 ",
+ io_lib:format("= ~s ==== ~s-~s-~w::~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 ",
+ io_lib:format("= Client ~w ~s Server ~ts ",
[Client,actionstr(Info),serverstr(Info)]).
actionstr(#conn_log{action=send}) -> "----->";
@@ -204,7 +203,7 @@ 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]).
+ io_lib:format("~w(~p)",[Alias,Address]).
month(1) -> "Jan";
month(2) -> "Feb";
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 49e0635d79..c1c1d943b9 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -150,7 +150,7 @@ init(RecvPids) ->
%%--------------------------------------------------------------------
handle_event(Event,State=#state{receivers=RecvPids}) ->
print("~n=== ~w ===~n", [?MODULE]),
- print("~p: ~p~n", [Event#event.name,Event#event.data]),
+ print("~w: ~w~n", [Event#event.name,Event#event.data]),
lists:foreach(fun(Recv) -> report_event(Recv,Event) end, RecvPids),
{ok,State}.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 403eab66cb..5fe4eaf511 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -750,12 +750,12 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
Descr1 = lists:flatten(io_lib:format("~P",[Descr,10])),
if length(Descr1) > 50 ->
Descr2 = string:substr(Descr1,1,50),
- io_lib:format("{badmatch,~s...}",[Descr2]);
+ io_lib:format("{badmatch,~ts...}",[Descr2]);
true ->
- io_lib:format("{badmatch,~s}",[Descr1])
+ io_lib:format("{badmatch,~ts}",[Descr1])
end;
{test_case_failed,Reason} ->
- case (catch io_lib:format("{test_case_failed,~s}", [Reason])) of
+ case (catch io_lib:format("{test_case_failed,~ts}", [Reason])) of
{'EXIT',_} ->
io_lib:format("{test_case_failed,~p}", [Reason]);
Result -> Result
@@ -788,7 +788,7 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
"<font color=\"green\">" ++ "(" ++ "</font>"
++ Comment ++
"<font color=\"green\">" ++ ")" ++ "</font>",
- Str = io_lib:format("~s ~s", [ErrorHtml,CommentHtml]),
+ Str = io_lib:format("~ts ~ts", [ErrorHtml,CommentHtml]),
test_server:comment(Str)
end
end,
@@ -803,24 +803,24 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
end,
case Loc of
[{?MODULE,error_in_suite}] ->
- PrintErr("Error in suite detected: ~s", [ErrStr]);
+ PrintErr("Error in suite detected: ~ts", [ErrStr]);
R when R == unknown; R == undefined ->
- PrintErr("Error detected: ~s", [ErrStr]);
+ PrintErr("Error detected: ~ts", [ErrStr]);
%% if a function specified by all/0 does not exist, we
%% pick up undef here
[{LastMod,LastFunc}|_] when ErrStr == "undef" ->
- PrintErr("~w:~w could not be executed~nReason: ~s",
+ PrintErr("~w:~w could not be executed~nReason: ~ts",
[LastMod,LastFunc,ErrStr]);
[{LastMod,LastFunc}|_] ->
- PrintErr("~w:~w failed~nReason: ~s", [LastMod,LastFunc,ErrStr]);
+ PrintErr("~w:~w failed~nReason: ~ts", [LastMod,LastFunc,ErrStr]);
[{LastMod,LastFunc,LastLine}|_] ->
%% print error to console, we are only
%% interested in the last executed expression
- PrintErr("~w:~w failed on line ~w~nReason: ~s",
+ PrintErr("~w:~w failed on line ~w~nReason: ~ts",
[LastMod,LastFunc,LastLine,ErrStr]),
case ct_util:read_suite_data({seq,Mod,Func}) of
@@ -1184,13 +1184,19 @@ report(What,Data) ->
ok;
{error,Reason} ->
ct_logs:log("COVER INFO",
- "Importing cover data from: ~s fails! "
+ "Importing cover data from: ~ts fails! "
"Reason: ~p", [Imp,Reason])
end
end, Imps)
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,
@@ -1343,4 +1349,7 @@ format_comment(Comment) ->
%%%-----------------------------------------------------------------
%%% @spec get_html_wrapper(TestName, PrintLabel, Cwd) -> Header
get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
- ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols).
+ get_html_wrapper(TestName, PrintLabel, Cwd, TableCols, utf8).
+
+get_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) ->
+ ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding).
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index 1f01d84601..2d4b1d1f52 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-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
@@ -272,7 +272,7 @@ call(Pid, Msg, Timeout) ->
after Timeout ->
erlang:demonitor(MRef, [flush]),
log("ct_gen_conn",
- "Connection process ~p not responding. Killing now!",
+ "Connection process ~w not responding. Killing now!",
[Pid]),
exit(Pid, kill),
{error,{process_down,Pid,forced_termination}}
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
index 74ab5e5439..14a8aab881 100644
--- a/lib/common_test/src/ct_groups.erl
+++ b/lib/common_test/src/ct_groups.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -462,7 +462,7 @@ make_conf(Mod, Name, Props, TestSpec) ->
false ->
ct_logs:log("TEST INFO", "init_per_group/2 and "
"end_per_group/2 missing for group "
- "~p in ~p, using default.",
+ "~w in ~w, using default.",
[Name,Mod]),
{{ct_framework,init_per_group},
{ct_framework,end_per_group},
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index 1bcc63738e..3d87a82e24 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -362,11 +362,11 @@ catch_apply(M,F,A, Default) ->
[{M,F,A,_}|_] when Reason == undef ->
Default;
Trace ->
- ct_logs:log("Suite Hook","Call to CTH failed: ~p:~p",
+ ct_logs:log("Suite Hook","Call to CTH failed: ~w:~p",
[error,{Reason,Trace}]),
throw({error_in_cth_call,
lists:flatten(
- io_lib:format("~p:~p/~p CTH call failed",
+ io_lib:format("~w:~w/~w CTH call failed",
[M,F,length(A)]))})
end
end.
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 0b7a8bb075..0b204a681a 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -1,7 +1,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
@@ -35,9 +35,10 @@
-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/4]).
+-export([get_ts_html_wrapper/5]).
-export([xhtml/2, locate_priv_file/1, make_relative/1]).
-export([insert_javascript/1]).
+-export([uri/1]).
%% Logging stuff directly from testcase
-export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4,
@@ -307,8 +308,8 @@ end_log() ->
%%% calling test suite.</p>
add_external_logs(Logs) ->
start_log("External Logs"),
- [cont_log("<a href=~p>~s</a>\n",
- [filename:join("log_private",Log),Log]) || Log <- Logs],
+ [cont_log("<a href=\"~ts\">~ts</a>\n",
+ [uri(filename:join("log_private",Log)),Log]) || Log <- Logs],
end_log().
%%%-----------------------------------------------------------------
@@ -320,8 +321,8 @@ add_external_logs(Logs) ->
%%% @doc Print a link to a given file stored in the priv_dir of the
%%% calling test suite.
add_link(Heading,File,Type) ->
- log(Heading,"<a href=~p type=~p>~s</a>\n",
- [filename:join("log_private",File),Type,File]).
+ log(Heading,"<a href=\"~ts\" type=~p>~ts</a>\n",
+ [uri(filename:join("log_private",File)),Type,File]).
%%%-----------------------------------------------------------------
@@ -396,9 +397,9 @@ tc_print(Category,Format,Args) ->
%%% <p>This function is called by <code>ct</code> when printing
%%% stuff from a testcase on the user console.</p>
tc_print(Category,Importance,Format,Args) ->
- VLvl = case ct_util:get_testdata({verbosity,Category}) of
+ VLvl = case ct_util:get_verbosity(Category) of
undefined ->
- ct_util:get_testdata({verbosity,'$unspecified'});
+ ct_util:get_verbosity('$unspecified');
{error,bad_invocation} ->
?MAX_VERBOSITY;
Val ->
@@ -469,7 +470,7 @@ ct_log(Category,Format,Args) ->
%%%=================================================================
%%% Internal functions
int_header() ->
- "<div class=\"ct_internal\"><b>*** CT ~s *** ~s</b>".
+ "<div class=\"ct_internal\"><b>*** CT ~s *** ~ts</b>".
int_footer() ->
"</div>".
@@ -692,7 +693,7 @@ logger_loop(State) ->
logger_loop(State);
{set_stylesheet,TC,SSFile} ->
Fd = State#logger_state.ct_log_fd,
- io:format(Fd, "~p loading external style sheet: ~s~n",
+ io:format(Fd, "~p loading external style sheet: ~ts~n",
[TC,SSFile]),
logger_loop(State#logger_state{stylesheet = SSFile});
{clear_stylesheet,_} when State#logger_state.stylesheet == undefined ->
@@ -752,7 +753,7 @@ print_to_log(sync, FromPid, TCGL, List, State) ->
IoProc = if FromPid /= TCGL -> TCGL;
true -> State#logger_state.ct_log_fd
end,
- io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]),
+ io:format(IoProc, "~ts", [lists:foldl(IoFun, [], List)]),
State;
print_to_log(async, FromPid, TCGL, List, State) ->
@@ -764,7 +765,7 @@ print_to_log(async, FromPid, TCGL, List, State) ->
end,
Printer = fun() ->
test_server:permit_io(IoProc, self()),
- io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)])
+ io:format(IoProc, "~ts", [lists:foldl(IoFun, [], List)])
end,
case State#logger_state.async_print_jobs of
[] ->
@@ -868,7 +869,7 @@ set_evmgr_gl(GL) ->
end.
open_ctlog() ->
- {ok,Fd} = file:open(?ct_log_name,[write]),
+ {ok,Fd} = file:open(?ct_log_name,[write,{encoding,utf8}]),
io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []),
case file:consult(ct_run:variables_file_name("../")) of
{ok,Vars} ->
@@ -878,7 +879,7 @@ open_ctlog() ->
Dir = filename:dirname(Cwd),
Variables = ct_run:variables_file_name(Dir),
io:format(Fd,
- "Can not read the file \'~s\' Reason: ~w\n"
+ "Can not read the file \'~ts\' Reason: ~w\n"
"No configuration found for test!!\n",
[Variables,Reason])
end,
@@ -904,7 +905,7 @@ print_style(Fd,undefined) ->
print_style(Fd,StyleSheet) ->
case file:read_file(StyleSheet) of
{ok,Bin} ->
- Str = binary_to_list(Bin),
+ Str = b2s(Bin,encoding(StyleSheet)),
Pos0 = case string:str(Str,"<style>") of
0 -> string:str(Str,"<STYLE>");
N0 -> N0
@@ -919,9 +920,9 @@ print_style(Fd,StyleSheet) ->
print_style_error(Fd,StyleSheet,missing_style_end_tag);
Pos0 /= 0 ->
Style = string:sub_string(Str,Pos0,Pos1+7),
- io:format(Fd,"~s\n",[Style]);
+ io:format(Fd,"~ts\n",[Style]);
Pos0 == 0 ->
- io:format(Fd,"<style>~s</style>\n",[Str])
+ io:format(Fd,"<style>~ts</style>\n",[Str])
end;
{error,Reason} ->
print_style_error(Fd,StyleSheet,Reason)
@@ -934,7 +935,7 @@ print_style(Fd,StyleSheet) ->
%% [StyleSheet]).
print_style_error(Fd,StyleSheet,Reason) ->
- io:format(Fd,"\n<!-- Failed to load stylesheet ~s: ~p -->\n",
+ io:format(Fd,"\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
[StyleSheet,Reason]),
print_style(Fd,undefined).
@@ -963,7 +964,7 @@ make_last_run_index(StartTime) ->
% io:put_chars("done\n"),
ok;
Err ->
- io:format("Unknown internal error while updating ~s. "
+ io:format("Unknown internal error while updating ~ts. "
"Please report.\n(Err: ~p, ID: 1)",
[AbsIndexName,Err]),
{error, Err}
@@ -1001,7 +1002,7 @@ make_last_run_index1(StartTime,IndexName) ->
%% write current Totals to file, later to be used in all_runs log
write_totals_file(?totals_name,Label,Logs1,Totals),
Index = [Index0|index_footer()],
- case force_write_file(IndexName, Index) of
+ case force_write_file(IndexName, unicode:characters_to_binary(Index)) of
ok ->
ok;
{error, Reason} ->
@@ -1085,7 +1086,7 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
CrashDumpName = SuiteName ++ "_erl_crash.dump",
case filelib:is_file(CrashDumpName) of
true ->
- ["&nbsp;<a href=\"", CrashDumpName,
+ ["&nbsp;<a href=\"", uri(CrashDumpName),
"\">(CrashDump)</a>"];
false ->
""
@@ -1115,10 +1116,10 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
[] -> "none";
_ -> "<a href=\""++?all_runs_name++"\">Old Runs</a>"
end,
- A = xhtml(["<td><font size=\"-1\"><a href=\"",CtLogFile,
+ A = xhtml(["<td><font size=\"-1\"><a href=\"",uri(CtLogFile),
"\">CT Log</a></font></td>\n",
"<td><font size=\"-1\">",OldRunsLink,"</font></td>\n"],
- ["<td><a href=\"",CtLogFile,"\">CT Log</a></td>\n",
+ ["<td><a href=\"",uri(CtLogFile),"\">CT Log</a></td>\n",
"<td>",OldRunsLink,"</td>\n"]),
{L,T,N,A};
false ->
@@ -1128,7 +1129,8 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
if NotBuilt == 0 ->
["<td align=right>",integer_to_list(NotBuilt),"</td>\n"];
true ->
- ["<td align=right><a href=\"",filename:join(CtRunDir,?ct_log_name),"\">",
+ ["<td align=right><a href=\"",
+ uri(filename:join(CtRunDir,?ct_log_name)),"\">",
integer_to_list(NotBuilt),"</a></td>\n"]
end,
FailStr =
@@ -1151,7 +1153,7 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
[xhtml("<tr valign=top>\n",
["<tr class=\"",odd_or_even(),"\">\n"]),
xhtml("<td><font size=\"-1\"><a href=\"", "<td><a href=\""),
- LogFile,"\">",SuiteName,"</a>", CrashDumpLink,
+ uri(LogFile),"\">",SuiteName,"</a>", CrashDumpLink,
xhtml("</font></td>\n", "</td>\n"),
Lbl, Timestamp,
"<td align=right>",integer_to_list(Success),"</td>\n",
@@ -1364,8 +1366,9 @@ header1(Title, SubTitle, TableCols) ->
"<head>\n",
"<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n",
xhtml("",
- ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">\n"]),
+ ["<link rel=\"stylesheet\" href=\"",uri(CSSFile),"\" type=\"text/css\">\n"]),
xhtml("",
["<script type=\"text/javascript\" src=\"",JQueryFile,
"\"></script>\n"]),
@@ -1462,7 +1465,7 @@ count_cases(Dir) ->
LogFile = filename:join(Dir, ?suitelog_name),
case file:read_file(LogFile) of
{ok, Bin} ->
- case count_cases1(binary_to_list(Bin),
+ case count_cases1(b2s(Bin),
{undefined,undefined,undefined,undefined}) of
{error,not_complete} ->
%% The test is not complete - dont write summary
@@ -1472,8 +1475,9 @@ count_cases(Dir) ->
write_summary(SumFile, Summary),
Summary
end;
- {error, _Reason} ->
- io:format("\nFailed to read ~p (skipped)\n", [LogFile]),
+ {error, Reason} ->
+ io:format("\nFailed to read ~p: ~p (skipped)\n",
+ [LogFile,Reason]),
error
end
end.
@@ -1557,7 +1561,7 @@ config_table1([{Key,Value}|Vars]) ->
"<td><pre>",io_lib:format("~p",[Value]),"</pre></td></tr>\n"],
["<tr class=\"", odd_or_even(), "\">\n",
"<td>", atom_to_list(Key), "</td>\n",
- "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) |
+ "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) |
config_table1(Vars)];
config_table1([]) ->
["</tbody>\n</table>\n"].
@@ -1574,8 +1578,9 @@ 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++
- all_runs_index_footer()),
+ Result = file:write_file(AbsName,
+ unicode:characters_to_binary(
+ Header++Index++all_runs_index_footer())),
if When == start -> ok;
true -> io:put_chars("done\n")
end,
@@ -1602,10 +1607,27 @@ sort_all_runs(Dirs) ->
interactive_link() ->
[Dir|_] = lists:reverse(filelib:wildcard(logdir_prefix()++"*.*")),
CtLog = filename:join(Dir,"ctlog.html"),
- Body = ["Log from last interactive run: <a href=\"",CtLog,"\">",
- timestamp(Dir),"</a>"],
- file:write_file("last_interactive.html",Body),
- io:format("~n~nUpdated ~s\n"
+ Body =
+ [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",
+ "\"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"]),
+ "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n",
+ "<head>\n",
+ "<title>Last interactive run</title>\n",
+ "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n",
+ "</head>\n",
+ "<body>\n",
+ "Log from last interactive run: <a href=\"",uri(CtLog),"\">",
+ timestamp(Dir),"</a>",
+ "</body>\n",
+ "</html>\n"
+ ],
+ file:write_file("last_interactive.html",unicode:characters_to_binary(Body)),
+ io:format("~n~nUpdated ~ts\n"
"Any CT activities will be logged here\n",
[?abs("last_interactive.html")]).
@@ -1655,7 +1677,7 @@ runentry(Dir) ->
TestNames;
true ->
Trunc = Polish(string:substr(TestNames,1,?testname_width-3)),
- lists:flatten(io_lib:format("~s...",[Trunc]))
+ lists:flatten(io_lib:format("~ts...",[Trunc]))
end,
Total = TotSucc+TotFail+AllSkip,
A = xhtml(["<td align=center><font size=\"-1\">",Node,
@@ -1695,7 +1717,7 @@ runentry(Dir) ->
"<td align=right>?</td>\n"],
A++B++C
end,
- Index = filename:join(Dir,?index_name),
+ Index = uri(filename:join(Dir,?index_name)),
[xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]),
xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">",timestamp(Dir),"</a>",
TotalsStr,"</font></td>\n"],
@@ -1836,7 +1858,7 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) ->
ok ->
ok;
Err ->
- io:format("Unknown internal error while updating ~s. "
+ io:format("Unknown internal error while updating ~ts. "
"Please report.\n(Err: ~p, ID: 1)",
[AbsIndexName,Err]),
{error, Err}
@@ -1900,7 +1922,7 @@ make_all_suites_index1(When, AbsIndexName, AllLogDirs) ->
ok
end;
Err ->
- io:format("Unknown internal error while updating ~s. "
+ io:format("Unknown internal error while updating ~ts. "
"Please report.\n(Err: ~p, ID: 1)",
[AbsIndexName,Err]),
{error, Err}
@@ -1912,7 +1934,7 @@ make_all_suites_index2(IndexName, AllTestLogDirs) ->
all_suites_index_header(),
0, 0, 0, 0, 0, [], []),
Index = [Index0|index_footer()],
- case force_write_file(IndexName, Index) of
+ case force_write_file(IndexName, unicode:characters_to_binary(Index)) of
ok ->
{ok,CacheData};
{error, Reason} ->
@@ -1970,7 +1992,7 @@ make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) ->
all_suites_index_header(IndexDir),
0, 0, 0, 0, 0),
Index = [Index0|index_footer()],
- case force_write_file(AbsIndexName, Index) of
+ case force_write_file(AbsIndexName, unicode:characters_to_binary(Index)) of
ok ->
ok;
{error, Reason} ->
@@ -2116,7 +2138,7 @@ simulate_logger_loop() ->
receive
{log,_,_,_,_,_,List} ->
S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List],
- io:format("~s",[S]),
+ io:format("~ts",[S]),
simulate_logger_loop();
stop ->
ok
@@ -2273,11 +2295,12 @@ make_relative1(DirTs, CwdTs) ->
Ups ++ DirTs.
%%%-----------------------------------------------------------------
-%%% @spec get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> {Mode,Header,Footer}
+%%% @spec get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding)
+%%% -> {Mode,Header,Footer}
%%%
%%% @doc
%%%
-get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
+get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) ->
TestName1 = if is_list(TestName) ->
lists:flatten(TestName);
true ->
@@ -2319,14 +2342,16 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
"<html>\n",
"<head><title>", TestName1, "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=",
+ html_encoding(Encoding),"\">\n",
"</head>\n",
"<body", Bgr, " bgcolor=\"white\" text=\"black\" ",
"link=\"blue\" vlink=\"purple\" alink=\"red\">\n",
LabelStr, "\n"],
["<center>\n<br><hr><p>\n",
- "<a href=\"", AllRuns,
+ "<a href=\"", uri(AllRuns),
"\">Test run history\n</a> | ",
- "<a href=\"", TestIndex,
+ "<a href=\"", uri(TestIndex),
"\">Top level test index\n</a>\n</p>\n",
Copyright,"</center>\n</body>\n</html>\n"]};
_ ->
@@ -2363,14 +2388,15 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
"<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\">\n",
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n",
+ "<link rel=\"stylesheet\" href=\"", uri(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,
+ "<a href=\"", uri(AllRuns),
"\">Test run history\n</a> | ",
- "<a href=\"", TestIndex,
+ "<a href=\"", uri(TestIndex),
"\">Top level test index\n</a>\n</p>\n",
Copyright,"</center>\n</body>\n</html>\n"]}
end.
@@ -2460,3 +2486,31 @@ insert_javascript({tablesorter,TableName,
" $(\"#",TableName,"\").trigger(\"update\");\n",
" $(\"#",TableName,"\").trigger(\"appendCache\");\n",
"});\n</script>\n"].
+
+uri("") ->
+ "";
+uri(Href) ->
+ test_server_ctrl:uri_encode(Href).
+
+%% Read magic comment to get encoding of text file.
+%% If no magic comment exists, assume default encoding
+encoding(File) ->
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end.
+
+%% Convert binary to string using default encoding
+b2s(Bin) ->
+ b2s(Bin,epp:default_encoding()).
+
+%% Convert binary to string using given encoding
+b2s(Bin,Encoding) ->
+ unicode:characters_to_list(Bin,Encoding).
+
+html_encoding(latin1) ->
+ "iso-8859-1";
+html_encoding(utf8) ->
+ "utf-8".
diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl
index 8ddb91d355..d4bd81e78d 100644
--- a/lib/common_test/src/ct_make.erl
+++ b/lib/common_test/src/ct_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -280,13 +280,13 @@ recompile(File, NoExec, Load, Opts) ->
do_recompile(_File, true, _Load, _Opts) ->
out_of_date;
do_recompile(File, false, noload, Opts) ->
- io:format("Recompile: ~s\n",[File]),
+ io:format("Recompile: ~ts\n",[File]),
compile:file(File, [report_errors, report_warnings, error_summary |Opts]);
do_recompile(File, false, load, Opts) ->
- io:format("Recompile: ~s\n",[File]),
+ io:format("Recompile: ~ts\n",[File]),
c:c(File, Opts);
do_recompile(File, false, netload, Opts) ->
- io:format("Recompile: ~s\n",[File]),
+ io:format("Recompile: ~ts\n",[File]),
c:nc(File, Opts).
exists(File) ->
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 042c5ba267..b42ff73846 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -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()]
@@ -82,39 +82,48 @@ run_test(NodeOptsList) when is_list(NodeOptsList) ->
%%% ExclNodes = [atom()]
%%%
%%% @doc Tests are spawned on the nodes as specified in <code>TestSpecs</code>.
-%%% Each specification in TestSpec will be handled separately. It is however possible
-%%% to also specify a list of specifications that should be merged into one before
-%%% the tests are executed. Any test without a particular node specification will
-%%% also be executed on the nodes in <code>InclNodes</code>. Nodes in the
-%%% <code>ExclNodes</code> list will be excluded from the test.
+%%% Each specification in TestSpec will be handled separately. It is however
+%%% possible to also specify a list of specifications that should be merged
+%%% into one before the tests are executed. Any test without a particular node
+%%% specification will also be executed on the nodes in <code>InclNodes</code>.
+%%% Nodes in the <code>ExclNodes</code> list will be excluded from the test.
run([TS|TestSpecs],AllowUserTerms,InclNodes,ExclNodes) when is_list(TS),
is_list(InclNodes),
is_list(ExclNodes) ->
- TS1 =
- case TS of
- List=[S|_] when is_list(S) -> List;
- Spec -> [Spec]
- end,
- Result =
- case catch ct_testspec:collect_tests_from_file(TS1,InclNodes,AllowUserTerms) of
- {error,Reason} ->
- {error,Reason};
- TSRec=#testspec{logdir=AllLogDirs,
- config=StdCfgFiles,
- userconfig=UserCfgFiles,
- include=AllIncludes,
- init=AllInitOpts,
- event_handler=AllEvHs} ->
- AllCfgFiles = {StdCfgFiles, UserCfgFiles},
- RunSkipPerNode = ct_testspec:prepare_tests(TSRec),
- RunSkipPerNode2 = exclude_nodes(ExclNodes,RunSkipPerNode),
- run_all(RunSkipPerNode2,AllLogDirs,AllCfgFiles,AllEvHs,
- AllIncludes,[],[],AllInitOpts,TS1)
- end,
- [{TS,Result} | run(TestSpecs,AllowUserTerms,InclNodes,ExclNodes)];
+ %% Note: [Spec] means run one test with Spec
+ %% [Spec1,Spec2] means run two tests separately
+ %% [[Spec1,Spec2]] means run one test, with the two specs merged
+ case catch ct_testspec:collect_tests_from_file([TS],InclNodes,
+ AllowUserTerms) of
+ {error,Reason} ->
+ [{error,Reason} | run(TestSpecs,AllowUserTerms,InclNodes,ExclNodes)];
+ Tests ->
+ RunResult =
+ lists:map(
+ fun({Specs,TSRec=#testspec{logdir=AllLogDirs,
+ config=StdCfgFiles,
+ userconfig=UserCfgFiles,
+ include=AllIncludes,
+ init=AllInitOpts,
+ event_handler=AllEvHs}}) ->
+ AllCfgFiles =
+ {StdCfgFiles,UserCfgFiles},
+ RunSkipPerNode =
+ ct_testspec:prepare_tests(TSRec),
+ RunSkipPerNode2 =
+ exclude_nodes(ExclNodes,RunSkipPerNode),
+ TSList = if is_integer(hd(TS)) -> [TS];
+ true -> TS end,
+ {Specs,run_all(RunSkipPerNode2,AllLogDirs,
+ AllCfgFiles,AllEvHs,
+ AllIncludes,[],[],AllInitOpts,TSList)}
+ end, Tests),
+ RunResult ++ run(TestSpecs,AllowUserTerms,InclNodes,ExclNodes)
+ end;
run([],_,_,_) ->
[];
-run(TS,AllowUserTerms,InclNodes,ExclNodes) when is_list(InclNodes), is_list(ExclNodes) ->
+run(TS,AllowUserTerms,InclNodes,ExclNodes) when is_list(InclNodes),
+ is_list(ExclNodes) ->
run([TS],AllowUserTerms,InclNodes,ExclNodes).
%%%-----------------------------------------------------------------
@@ -152,29 +161,32 @@ exclude_nodes([],RunSkipPerNode) ->
%%% AllowUserTerms = bool()
%%% Node = atom()
%%%
-%%% @doc Tests are spawned on <code>Node</code> according to <code>TestSpecs</code>.
+%%% @doc Tests are spawned on <code>Node</code> according to
+%%% <code>TestSpecs</code>.
run_on_node([TS|TestSpecs],AllowUserTerms,Node) when is_list(TS),is_atom(Node) ->
- TS1 =
- case TS of
- [List|_] when is_list(List) -> List;
- Spec -> [Spec]
- end,
- Result =
- case catch ct_testspec:collect_tests_from_file(TS1,[Node],AllowUserTerms) of
- {error,Reason} ->
- {error,Reason};
- TSRec=#testspec{logdir=AllLogDirs,
- config=StdCfgFiles,
- init=AllInitOpts,
- include=AllIncludes,
- userconfig=UserCfgFiles,
- event_handler=AllEvHs} ->
- AllCfgFiles = {StdCfgFiles, UserCfgFiles},
- {Run,Skip} = ct_testspec:prepare_tests(TSRec,Node),
- run_all([{Node,Run,Skip}],AllLogDirs,AllCfgFiles,AllEvHs,
- AllIncludes, [],[],AllInitOpts,TS1)
- end,
- [{TS,Result} | run_on_node(TestSpecs,AllowUserTerms,Node)];
+ case catch ct_testspec:collect_tests_from_file([TS],[Node],
+ AllowUserTerms) of
+ {error,Reason} ->
+ [{error,Reason} | run_on_node(TestSpecs,AllowUserTerms,Node)];
+ Tests ->
+ RunResult =
+ lists:map(
+ fun({Specs,TSRec=#testspec{logdir=AllLogDirs,
+ config=StdCfgFiles,
+ init=AllInitOpts,
+ include=AllIncludes,
+ userconfig=UserCfgFiles,
+ event_handler=AllEvHs}}) ->
+ AllCfgFiles = {StdCfgFiles,UserCfgFiles},
+ {Run,Skip} = ct_testspec:prepare_tests(TSRec,Node),
+ TSList = if is_integer(hd(TS)) -> [TS];
+ true -> TS end,
+ {Specs,run_all([{Node,Run,Skip}],AllLogDirs,
+ AllCfgFiles,AllEvHs,
+ AllIncludes, [],[],AllInitOpts,TSList)}
+ end, Tests),
+ RunResult ++ run_on_node(TestSpecs,AllowUserTerms,Node)
+ end;
run_on_node([],_,_) ->
[];
run_on_node(TS,AllowUserTerms,Node) when is_atom(Node) ->
@@ -244,8 +256,9 @@ run_all([],AllLogDirs,_,AllEvHs,_AllIncludes,
{value,{_,Dir}} -> Dir;
false -> "."
end,
- log(tty,"Master Logdir","~s",[MasterLogDir]),
- start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir,LogDirs,InitOptions,Specs),
+ log(tty,"Master Logdir","~ts",[MasterLogDir]),
+ start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir,
+ LogDirs,InitOptions,Specs),
ok.
@@ -297,13 +310,15 @@ start_master(NodeOptsList) ->
start_master(NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Specs) ->
Master = spawn_link(?MODULE,init_master,[self(),NodeOptsList,EvHandlers,
- MasterLogDir,LogDirs,InitOptions,Specs]),
+ MasterLogDir,LogDirs,
+ InitOptions,Specs]),
receive
{Master,Result} -> Result
end.
%%% @hidden
-init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Specs) ->
+init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,
+ InitOptions,Specs) ->
case whereis(ct_master) of
undefined ->
register(ct_master,self()),
@@ -325,13 +340,14 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Spec
{MLPid,_} = ct_master_logs:start(MasterLogDir,
[N || {N,_} <- NodeOptsList]),
log(all,"Master Logger process started","~w",[MLPid]),
+
case Specs of
[] -> ok;
_ ->
SpecsStr = lists:map(fun(Name) ->
Name ++ " "
end,Specs),
- ct_master_logs:log("Test Specification file(s)","~s",
+ ct_master_logs:log("Test Specification file(s)","~ts",
[lists:flatten(SpecsStr)])
end,
@@ -340,7 +356,7 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Spec
ct_master_event:add_handler(),
%% add user handlers for master event manager
Add = fun({H,Args}) ->
- log(all,"Adding Event Handler","~p",[H]),
+ log(all,"Adding Event Handler","~w",[H]),
case gen_event:add_handler(?CT_MEVMGR_REF,H,Args) of
ok -> ok;
{'EXIT',Why} -> exit(Why);
@@ -359,7 +375,8 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Spec
init_master1(Parent,NodeOptsList,InitOptions,LogDirs).
init_master1(Parent,NodeOptsList,InitOptions,LogDirs) ->
- {Inaccessible,NodeOptsList1,InitOptions1} = init_nodes(NodeOptsList,InitOptions),
+ {Inaccessible,NodeOptsList1,InitOptions1} = init_nodes(NodeOptsList,
+ InitOptions),
case Inaccessible of
[] ->
init_master2(Parent,NodeOptsList,LogDirs);
@@ -391,8 +408,9 @@ init_master2(Parent,NodeOptsList,LogDirs) ->
SpawnAndMon =
fun({Node,Opts}) ->
monitor_node(Node,true),
- log(all,"Test Info","Starting test(s) on ~p...",[Node]),
- {spawn_link(Node,?MODULE,init_node_ctrl,[self(),Cookie,Opts]),Node}
+ log(all,"Test Info","Starting test(s) on ~w...",[Node]),
+ {spawn_link(Node,?MODULE,init_node_ctrl,[self(),Cookie,Opts]),
+ Node}
end,
NodeCtrlPids = lists:map(SpawnAndMon,NodeOptsList),
Result = master_loop(#state{node_ctrl_pids=NodeCtrlPids,
@@ -404,12 +422,13 @@ master_loop(#state{node_ctrl_pids=[],
results=Finished}) ->
Str =
lists:map(fun({Node,Result}) ->
- io_lib:format("~-40.40.*s~p\n",[$_,atom_to_list(Node),Result])
+ io_lib:format("~-40.40.*ts~p\n",
+ [$_,atom_to_list(Node),Result])
end,lists:reverse(Finished)),
log(all,"TEST RESULTS",Str,[]),
log(all,"Info","Updating log files",[]),
refresh_logs(LogDirs,[]),
-
+
ct_master_event:stop(),
ct_master_logs:stop(),
ok;
@@ -437,18 +456,20 @@ master_loop(State=#state{node_ctrl_pids=NodeCtrlPids,
Bad
end,
log(all,"Test Info",
- "Test on node ~w failed! Reason: ~p",[Node,Error]),
+ "Test on node ~w failed! Reason: ~p",
+ [Node,Error]),
{Locks1,Blocked1} =
update_queue(exit,Node,Locks,Blocked),
master_loop(State#state{node_ctrl_pids=NodeCtrlPids1,
- results=[{Node,Error}|Results],
+ results=[{Node,
+ Error}|Results],
locks=Locks1,
blocked=Blocked1})
end;
undefined ->
%% ignore (but report) exit from master_logger etc
log(all,"Test Info",
- "Warning! Process ~p has terminated. Reason: ~p",
+ "Warning! Process ~w has terminated. Reason: ~p",
[Pid,Reason]),
master_loop(State)
end;
@@ -531,7 +552,7 @@ update_queue(take,Node,From,Lock={Op,Resource},Locks,Blocked) ->
%% Blocked: [{{Operation,Resource},Node,WaitingPid},...]
case lists:keysearch(Lock,1,Locks) of
{value,{_Lock,Owner}} -> % other node has lock
- log(html,"Lock Info","Node ~p blocked on ~w by ~w. Resource: ~p",
+ log(html,"Lock Info","Node ~w blocked on ~w by ~w. Resource: ~p",
[Node,Op,Owner,Resource]),
Blocked1 = Blocked ++ [{Lock,Node,From}],
{Locks,Blocked1};
@@ -546,7 +567,7 @@ update_queue(release,Node,_From,Lock={Op,Resource},Locks,Blocked) ->
case lists:keysearch(Lock,1,Blocked) of
{value,E={Lock,SomeNode,WaitingPid}} ->
Blocked1 = lists:delete(E,Blocked),
- log(html,"Lock Info","Node ~p proceeds with ~w. Resource: ~p",
+ log(html,"Lock Info","Node ~w proceeds with ~w. Resource: ~p",
[SomeNode,Op,Resource]),
reply(ok,WaitingPid), % waiting process may start
{Locks1,Blocked1};
@@ -625,7 +646,8 @@ refresh_logs([D|Dirs],Refreshed) ->
refresh_logs([],Refreshed) ->
Str =
lists:map(fun({D,Result}) ->
- io_lib:format("Refreshing logs in ~p... ~p",[D,Result])
+ io_lib:format("Refreshing logs in ~p... ~p",
+ [D,Result])
end,Refreshed),
log(all,"Info",Str,[]).
@@ -638,7 +660,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) ->
process_flag(trap_exit, true),
MasterNode = node(MasterPid),
group_leader(whereis(user),self()),
- io:format("~n********** node_ctrl process ~p started on ~p **********~n",
+ io:format("~n********** node_ctrl process ~w started on ~w **********~n",
[self(),node()]),
%% initially this node must have the same cookie as the master node
%% but now we set it explicitly for the connection so that test suites
@@ -671,7 +693,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) ->
pong ->
MasterPid ! {self(),{result,Result}};
pang ->
- io:format("Warning! Connection to master node ~p is lost. "
+ io:format("Warning! Connection to master node ~w is lost. "
"Can't report result!~n~n", [MasterNode])
end.
@@ -696,8 +718,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,
@@ -751,21 +774,25 @@ start_nodes(InitOptions)->
IsAlive = lists:member(NodeName, nodes()),
case {HasNodeStart, IsAlive} of
{false, false}->
- io:format("WARNING: Node ~p is not alive but has no node_start option~n", [NodeName]);
+ io:format("WARNING: Node ~w is not alive but has no "
+ "node_start option~n", [NodeName]);
{false, true}->
- io:format("Node ~p is alive~n", [NodeName]);
+ io:format("Node ~w is alive~n", [NodeName]);
{true, false}->
{node_start, NodeStart} = lists:keyfind(node_start, 1, Options),
{value, {callback_module, Callback}, NodeStart2}=
lists:keytake(callback_module, 1, NodeStart),
case Callback:start(Host, Node, NodeStart2) of
{ok, NodeName} ->
- io:format("Node ~p started successfully with callback ~p~n", [NodeName,Callback]);
+ io:format("Node ~w started successfully "
+ "with callback ~w~n", [NodeName,Callback]);
{error, Reason, _NodeName} ->
- io:format("Failed to start node ~p with callback ~p! Reason: ~p~n", [NodeName, Callback, Reason])
+ io:format("Failed to start node ~w with callback ~w! "
+ "Reason: ~p~n", [NodeName, Callback, Reason])
end;
{true, true}->
- io:format("WARNING: Node ~p is alive but has node_start option~n", [NodeName])
+ io:format("WARNING: Node ~w is alive but has node_start "
+ "option~n", [NodeName])
end
end,
InitOptions).
@@ -778,7 +805,8 @@ eval_on_nodes(InitOptions)->
{false,_}->
ok;
{true,false}->
- io:format("WARNING: Node ~p is not alive but has eval option ~n", [NodeName]);
+ io:format("WARNING: Node ~w is not alive but has eval "
+ "option~n", [NodeName]);
{true,true}->
{eval, MFAs} = lists:keyfind(eval, 1, Options),
evaluate(NodeName, MFAs)
@@ -789,9 +817,11 @@ eval_on_nodes(InitOptions)->
evaluate(Node, [{M,F,A}|MFAs])->
case rpc:call(Node, M, F, A) of
{badrpc,Reason}->
- io:format("WARNING: Failed to call ~p:~p/~p on node ~p due to ~p~n", [M,F,length(A),Node,Reason]);
+ io:format("WARNING: Failed to call ~w:~w/~w on node ~w "
+ "due to ~p~n", [M,F,length(A),Node,Reason]);
Result->
- io:format("Called ~p:~p/~p on node ~p, result: ~p~n", [M,F,length(A),Node,Result])
+ io:format("Called ~w:~w/~w on node ~w, result: ~p~n",
+ [M,F,length(A),Node,Result])
end,
evaluate(Node, MFAs);
evaluate(_Node, [])->
diff --git a/lib/common_test/src/ct_master_event.erl b/lib/common_test/src/ct_master_event.erl
index a70baefaaf..fd97ab16f7 100644
--- a/lib/common_test/src/ct_master_event.erl
+++ b/lib/common_test/src/ct_master_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -66,16 +66,30 @@ add_handler(Args) ->
%% Description: Stops the event manager
%%--------------------------------------------------------------------
stop() ->
- flush(),
- gen_event:stop(?CT_MEVMGR_REF).
+ case flush() of
+ {error,Reason} ->
+ ct_master_logs:log("Error",
+ "No response from CT Master Event.\n"
+ "Reason = ~p\n"
+ "Terminating now!\n",[Reason]),
+ %% communication with event manager fails, kill it
+ catch exit(whereis(?CT_MEVMGR_REF), kill);
+ _ ->
+ gen_event:stop(?CT_MEVMGR_REF)
+ end.
flush() ->
- case gen_event:call(?CT_MEVMGR_REF,?MODULE,flush) of
+ try gen_event:call(?CT_MEVMGR_REF,?MODULE,flush,1800000) of
flushing ->
timer:sleep(1),
flush();
done ->
- ok
+ ok;
+ Error = {error,_} ->
+ Error
+ catch
+ _:Reason ->
+ {error,Reason}
end.
%%--------------------------------------------------------------------
@@ -114,13 +128,13 @@ init(_) ->
%% each installed event handler to handle the event.
%%--------------------------------------------------------------------
handle_event(#event{name=start_logging,node=Node,data=RunDir},State) ->
- ct_master_logs:log("CT Master Event Handler","Got ~s from ~p",[RunDir,Node]),
+ ct_master_logs:log("CT Master Event Handler","Got ~ts from ~w",[RunDir,Node]),
ct_master_logs:nodedir(Node,RunDir),
{ok,State};
handle_event(#event{name=Name,node=Node,data=Data},State) ->
print("~n=== ~w ===~n", [?MODULE]),
- print("~p on ~p: ~p~n", [Name,Node,Data]),
+ print("~w on ~w: ~p~n", [Name,Node,Data]),
{ok,State}.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 9e61d5b16f..5393097f57 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -133,8 +133,8 @@ init(Parent,LogDir,Nodes) ->
end,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:format(CtLogFd,"~ts\n",[NodeStr]),
+ io:put_chars(CtLogFd,[int_footer(),"\n"]),
NodeDirIxFd = open_nodedir_index(RunDirAbs,Time),
Parent ! {started,self(),{Time,RunDirAbs}},
@@ -201,25 +201,22 @@ 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", {[],[1,2],[]}),[]),
+ {ok,Fd} = file:open(FullName,[write,{encoding,utf8}]),
+ 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) ->
@@ -238,7 +235,7 @@ config_table1([]) ->
["</tbody>\n</table>\n"].
int_header() ->
- "<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~s</b>".
+ "<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~ts</b>".
int_footer() ->
"</div>".
@@ -247,21 +244,22 @@ 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),[]),
+ {ok,Fd} = file:open(FullName,[write,{encoding,utf8}]),
+ 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=\"",ct_logs:uri(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) ->
@@ -286,7 +284,9 @@ make_all_runs_index(LogDir) ->
DirsSorted = (catch sort_all_runs(Dirs)),
Header = all_runs_header(),
Index = [runentry(Dir) || Dir <- DirsSorted],
- Result = file:write_file(FullName,Header++Index++index_footer()),
+ Result = file:write_file(FullName,
+ unicode:characters_to_binary(
+ Header++Index++index_footer())),
Result.
sort_all_runs(Dirs) ->
@@ -326,7 +326,8 @@ runentry(Dir) ->
end,
Index = filename:join(Dir,?nodedir_index_name),
["<tr>\n"
- "<td align=center><a href=\"",Index,"\">",timestamp(Dir),"</a></td>\n",
+ "<td align=center><a href=\"",ct_logs:uri(Index),"\">",
+ timestamp(Dir),"</a></td>\n",
"<td align=center>",MasterStr,"</td>\n",
"<td align=center>",NodesStr,"</td>\n",
"</tr>\n"].
@@ -384,8 +385,10 @@ header(Title, TableCols) ->
"<head>\n",
"<title>" ++ Title ++ "</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n",
xhtml("",
- ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ ["<link rel=\"stylesheet\" href=\"",ct_logs:uri(CSSFile),
+ "\" type=\"text/css\">"]),
xhtml("",
["<script type=\"text/javascript\" src=\"",JQueryFile,
"\"></script>\n"]),
diff --git a/lib/common_test/src/ct_master_status.erl b/lib/common_test/src/ct_master_status.erl
index 76060fb7bb..f9f511ecca 100644
--- a/lib/common_test/src/ct_master_status.erl
+++ b/lib/common_test/src/ct_master_status.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -70,7 +70,7 @@ init(_) ->
%%
handle_event(#event{name=Name,node=Node,data=Data},State) ->
print("~n=== ~w ===~n", [?MODULE]),
- print("~p on ~p: ~p~n", [Name,Node,Data]),
+ print("~w on ~w: ~p~n", [Name,Node,Data]),
{ok,State}.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 294b82bff6..1339e53780 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -1,7 +1,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
@@ -307,7 +307,7 @@
-type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} |
{password,string()} | {user_dir,string()} |
{timeout,timeout()}.
--type host() :: inet:host_name() | inet:ip_address().
+-type host() :: inet:hostname() | inet:ip_address().
-type notification() :: {notification, xml_attributes(), notification_content()}.
-type notification_content() :: [event_time()|simple_xml()].
@@ -1073,7 +1073,8 @@ handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
SimpleXml = encode_rpc_operation(get,[Filter]),
do_send_rpc(Op, SimpleXml, Timeout, From, State).
-handle_msg({ssh_cm, _CM, {data, _Ch, _Type, Data}}, 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
@@ -1280,10 +1281,11 @@ do_send(Connection, SimpleXml) ->
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 = unicode:characters_to_binary(
+ xmerl:export_simple([Simple],
+ xmerl_xml,
+ [#xmlAttribute{name=prolog,
+ value=Prolog}])),
<<Xml/binary,?END_TAG/binary>>.
%%%-----------------------------------------------------------------
@@ -1687,18 +1689,27 @@ log(#connection{host=Host,port=Port,name=Name},Action,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)]).
+format_data(How,Data) ->
+ %% Assuming that the data is encoded as UTF-8. If it is not, then
+ %% the printout might be wrong, but the format function will not
+ %% crash!
+ %% FIXME: should probably read encoding from the data and do
+ %% unicode:characters_to_binary(Data,InEncoding,utf8) when calling
+ %% log/3 instead of assuming utf8 in as done here!
+ do_format_data(How,unicode:characters_to_binary(Data)).
+
+do_format_data(raw,Data) ->
+ io_lib:format("~n~ts~n",[hide_password(Data)]);
+do_format_data(pretty,Data) ->
+ io_lib:format("~n~ts~n",[indent(Data)]);
+do_format_data(html,Data) ->
+ io_lib:format("~n~ts~n",[html_format(Data)]).
%%%-----------------------------------------------------------------
%%% Hide password elements from XML data
hide_password(Bin) ->
re:replace(Bin,<<"(<password[^>]*>)[^<]*(</password>)">>,<<"\\1*****\\2">>,
- [global,{return,binary}]).
+ [global,{return,binary},unicode]).
%%%-----------------------------------------------------------------
%%% HTML formatting
@@ -1716,13 +1727,13 @@ indent(Bin) ->
Part ->
indent1(lists:reverse(Part)++String,erase(indent))
end,
- list_to_binary(IndentedString).
+ unicode:characters_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}]).
+normalize(Bin) ->
+ re:replace(Bin,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list},unicode]).
indent1("<?"++Rest1,Indent1) ->
@@ -1805,7 +1816,8 @@ get_tag([]) ->
%%% SSH stuff
ssh_receive_data() ->
receive
- {ssh_cm, _CM, {data, _Ch, _Type, Data}} ->
+ {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};
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 96b2934382..49f00429ae 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -58,6 +58,7 @@
vts,
shell,
cover,
+ cover_stop,
coverspec,
step,
logdir,
@@ -147,7 +148,7 @@ script_start(Args) ->
_ -> ""
end
end,
- io:format("~nCommon Test~s starting (cwd is ~s)~n~n",
+ io:format("~nCommon Test~s starting (cwd is ~ts)~n~n",
[CTVsn,Cwd]),
Self = self(),
Pid = spawn_link(fun() -> script_start1(Self, Args) end),
@@ -245,6 +246,7 @@ 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),
@@ -328,32 +330,33 @@ script_start1(Parent, Args) ->
true
end,
- StartOpts = #opts{label = Label, profile = Profile,
- vts = Vts, shell = Shell, cover = Cover,
- 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,
- starter = script},
-
+ Opts = #opts{label = Label, profile = Profile,
+ 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,
+ starter = script},
+
%% check if log files should be refreshed or go on to run tests...
- Result = run_or_refresh(StartOpts, Args),
+ Result = run_or_refresh(Opts, Args),
%% send final results to starting process waiting in script_start/0
Parent ! {self(), Result}.
-run_or_refresh(StartOpts = #opts{logdir = LogDir}, Args) ->
+run_or_refresh(Opts = #opts{logdir = LogDir}, Args) ->
case proplists:get_value(refresh_logs, Args) of
undefined ->
- script_start2(StartOpts, Args);
+ script_start2(Opts, Args);
Refresh ->
LogDir1 = case Refresh of
[] -> which(logdir,LogDir);
@@ -375,167 +378,203 @@ run_or_refresh(StartOpts = #opts{logdir = LogDir}, Args) ->
{error,{all_suites_index,ASReason}};
_ ->
file:set_cwd(Cwd),
- io:format("Logs in ~s refreshed!~n~n", [LogDir1]),
+ io:format("Logs in ~ts refreshed!~n~n",
+ [LogDir1]),
timer:sleep(500), % time to flush io before quitting
ok
end
end
end.
-script_start2(StartOpts = #opts{vts = undefined,
- shell = undefined}, Args) ->
- TestSpec = proplists:get_value(spec, Args),
- {Terms,Opts} =
- case TestSpec of
- Specs when Specs =/= [], Specs =/= undefined ->
- %% using testspec as input for test
- Relaxed = get_start_opt(allow_user_terms, true, false, Args),
- case catch ct_testspec:collect_tests_from_file(Specs, Relaxed) of
- {E,Reason} when E == error ; E == 'EXIT' ->
- {{error,Reason},StartOpts};
- TS ->
- SpecStartOpts = get_data_for_node(TS, node()),
-
- Label = choose_val(StartOpts#opts.label,
- SpecStartOpts#opts.label),
-
- Profile = choose_val(StartOpts#opts.profile,
- SpecStartOpts#opts.profile),
-
- LogDir = choose_val(StartOpts#opts.logdir,
- SpecStartOpts#opts.logdir),
-
- AllLogOpts = merge_vals([StartOpts#opts.logopts,
- SpecStartOpts#opts.logopts]),
- 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),
- MultTT =
- choose_val(StartOpts#opts.multiply_timetraps,
- SpecStartOpts#opts.multiply_timetraps),
- ScaleTT =
- choose_val(StartOpts#opts.scale_timetraps,
- SpecStartOpts#opts.scale_timetraps),
-
- CreatePrivDir =
- choose_val(StartOpts#opts.create_priv_dir,
- SpecStartOpts#opts.create_priv_dir),
-
- AllEvHs =
- merge_vals([StartOpts#opts.event_handlers,
- SpecStartOpts#opts.event_handlers]),
-
- AllCTHooks = merge_vals(
- [StartOpts#opts.ct_hooks,
- SpecStartOpts#opts.ct_hooks]),
-
- EnableBuiltinHooks =
- choose_val(
- 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,
- 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,
- create_priv_dir = CreatePrivDir}}
- end;
- _ ->
- {undefined,StartOpts}
- end,
- %% read config/userconfig from start flags
- InitConfig = ct_config:prepare_config_list(Args),
- TheLogDir = which(logdir, Opts#opts.logdir),
- case {TestSpec,Terms} of
- {_,{error,_}=Error} ->
- Error;
- {[],_} ->
+script_start2(Opts = #opts{vts = undefined,
+ shell = undefined}, Args) ->
+ case proplists:get_value(spec, Args) of
+ Specs when Specs =/= [], Specs =/= undefined ->
+ Specs1 = get_start_opt(join_specs, [Specs], Specs, Args),
+ %% using testspec as input for test
+ Relaxed = get_start_opt(allow_user_terms, true, false, Args),
+ case catch ct_testspec:collect_tests_from_file(Specs1, Relaxed) of
+ {E,Reason} when E == error ; E == 'EXIT' ->
+ {error,Reason};
+ TestSpecData ->
+ execute_all_specs(TestSpecData, Opts, Args, [])
+ end;
+ [] ->
{error,no_testspec_specified};
- {undefined,_} -> % no testspec used
- case check_and_install_configfiles(InitConfig, TheLogDir, Opts) of
+ _ -> % no testspec used
+ %% read config/userconfig from start flags
+ InitConfig = ct_config:prepare_config_list(Args),
+ TheLogDir = which(logdir, Opts#opts.logdir),
+ case check_and_install_configfiles(InitConfig,
+ TheLogDir,
+ Opts) of
ok -> % go on read tests from start flags
script_start3(Opts#opts{config=InitConfig,
logdir=TheLogDir}, Args);
Error ->
Error
- end;
- {_,_} -> % testspec used
- %% merge config from start flags with config from testspec
- AllConfig = merge_vals([InitConfig, Opts#opts.config]),
- case check_and_install_configfiles(AllConfig, TheLogDir, Opts) of
- ok -> % read tests from spec
- {Run,Skip} = ct_testspec:prepare_tests(Terms, node()),
- do_run(Run, Skip, Opts#opts{config=AllConfig,
- logdir=TheLogDir}, Args);
- Error ->
- Error
end
end;
-script_start2(StartOpts, Args) ->
+script_start2(Opts, Args) ->
%% read config/userconfig from start flags
InitConfig = ct_config:prepare_config_list(Args),
- LogDir = which(logdir, StartOpts#opts.logdir),
- case check_and_install_configfiles(InitConfig, LogDir, StartOpts) of
+ LogDir = which(logdir, Opts#opts.logdir),
+ case check_and_install_configfiles(InitConfig, LogDir, Opts) of
ok -> % go on read tests from start flags
- script_start3(StartOpts#opts{config=InitConfig,
- logdir=LogDir}, Args);
+ script_start3(Opts#opts{config=InitConfig,
+ logdir=LogDir}, Args);
Error ->
Error
end.
+execute_all_specs([], _, _, Result) ->
+ Result1 = lists:reverse(Result),
+ case lists:keysearch('EXIT', 1, Result1) of
+ {value,{_,_,ExitReason}} ->
+ exit(ExitReason);
+ false ->
+ case lists:keysearch(error, 1, Result1) of
+ {value,Error} ->
+ Error;
+ false ->
+ lists:foldl(fun({Ok,Fail,{UserSkip,AutoSkip}},
+ {Ok1,Fail1,{UserSkip1,AutoSkip1}}) ->
+ {Ok1+Ok,Fail1+Fail,
+ {UserSkip1+UserSkip,
+ AutoSkip1+AutoSkip}}
+ end, {0,0,{0,0}}, Result1)
+ end
+ end;
+
+execute_all_specs([{Specs,TS} | TSs], Opts, Args, Result) ->
+ CombinedOpts = combine_test_opts(TS, Specs, Opts),
+ try execute_one_spec(TS, CombinedOpts, Args) of
+ ExecResult ->
+ execute_all_specs(TSs, Opts, Args, [ExecResult|Result])
+ catch
+ _ : ExitReason ->
+ execute_all_specs(TSs, Opts, Args,
+ [{'EXIT',self(),ExitReason}|Result])
+ end.
+
+execute_one_spec(TS, Opts, Args) ->
+ %% read config/userconfig from start flags
+ InitConfig = ct_config:prepare_config_list(Args),
+ TheLogDir = which(logdir, Opts#opts.logdir),
+ %% merge config from start flags with config from testspec
+ AllConfig = merge_vals([InitConfig, Opts#opts.config]),
+ case check_and_install_configfiles(AllConfig, TheLogDir, Opts) of
+ ok -> % read tests from spec
+ {Run,Skip} = ct_testspec:prepare_tests(TS, node()),
+ do_run(Run, Skip, Opts#opts{config=AllConfig,
+ logdir=TheLogDir}, Args);
+ Error ->
+ Error
+ end.
+
+combine_test_opts(TS, Specs, Opts) ->
+ TSOpts = get_data_for_node(TS, node()),
+
+ Label = choose_val(Opts#opts.label,
+ TSOpts#opts.label),
+
+ Profile = choose_val(Opts#opts.profile,
+ TSOpts#opts.profile),
+
+ LogDir = choose_val(Opts#opts.logdir,
+ TSOpts#opts.logdir),
+
+ AllLogOpts = merge_vals([Opts#opts.logopts,
+ TSOpts#opts.logopts]),
+ AllVerbosity =
+ merge_keyvals([Opts#opts.verbosity,
+ TSOpts#opts.verbosity]),
+ AllSilentConns =
+ merge_vals([Opts#opts.silent_connections,
+ TSOpts#opts.silent_connections]),
+ Cover =
+ choose_val(Opts#opts.cover,
+ TSOpts#opts.cover),
+ CoverStop =
+ choose_val(Opts#opts.cover_stop,
+ TSOpts#opts.cover_stop),
+ MultTT =
+ choose_val(Opts#opts.multiply_timetraps,
+ TSOpts#opts.multiply_timetraps),
+ ScaleTT =
+ choose_val(Opts#opts.scale_timetraps,
+ TSOpts#opts.scale_timetraps),
+
+ CreatePrivDir =
+ choose_val(Opts#opts.create_priv_dir,
+ TSOpts#opts.create_priv_dir),
+
+ AllEvHs =
+ merge_vals([Opts#opts.event_handlers,
+ TSOpts#opts.event_handlers]),
+
+ AllCTHooks = merge_vals(
+ [Opts#opts.ct_hooks,
+ TSOpts#opts.ct_hooks]),
+
+ EnableBuiltinHooks =
+ choose_val(
+ Opts#opts.enable_builtin_hooks,
+ TSOpts#opts.enable_builtin_hooks),
+
+ Stylesheet =
+ choose_val(Opts#opts.stylesheet,
+ TSOpts#opts.stylesheet),
+
+ AllInclude = merge_vals([Opts#opts.include,
+ TSOpts#opts.include]),
+ application:set_env(common_test, include, AllInclude),
+
+ AutoCompile =
+ case choose_val(Opts#opts.auto_compile,
+ TSOpts#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,
+ TSOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
+
+ Opts#opts{label = Label,
+ profile = Profile,
+ testspecs = Specs,
+ cover = Cover,
+ cover_stop = CoverStop,
+ logdir = which(logdir, LogDir),
+ logopts = AllLogOpts,
+ basic_html = BasicHtml,
+ verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
+ config = TSOpts#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,
+ create_priv_dir = CreatePrivDir}.
+
check_and_install_configfiles(
Configs, LogDir, #opts{
event_handlers = EvHandlers,
@@ -555,12 +594,12 @@ check_and_install_configfiles(
{error,{cant_load_callback_module,Info}}
end.
-script_start3(StartOpts, Args) ->
- StartOpts1 = get_start_opt(step,
- fun(Step) ->
- StartOpts#opts{step = Step,
- cover = undefined}
- end, StartOpts, Args),
+script_start3(Opts, Args) ->
+ Opts1 = get_start_opt(step,
+ fun(Step) ->
+ Opts#opts{step = Step,
+ cover = undefined}
+ end, Opts, Args),
case {proplists:get_value(dir, Args),
proplists:get_value(suite, Args),
groups_and_cases(proplists:get_value(group, Args),
@@ -574,17 +613,17 @@ script_start3(StartOpts, Args) ->
{error,no_dir_specified};
{Dirs,undefined,[]} when is_list(Dirs) ->
- script_start4(StartOpts#opts{tests = tests(Dirs)}, Args);
+ script_start4(Opts#opts{tests = tests(Dirs)}, Args);
{undefined,Suites,[]} when is_list(Suites) ->
Ts = tests([suite_to_test(S) || S <- Suites]),
- script_start4(StartOpts1#opts{tests = Ts}, Args);
+ script_start4(Opts1#opts{tests = Ts}, Args);
{undefined,Suite,GsAndCs} when is_list(Suite) ->
case [suite_to_test(S) || S <- Suite] of
DirMods = [_] ->
Ts = tests(DirMods, GsAndCs),
- script_start4(StartOpts1#opts{tests = Ts}, Args);
+ script_start4(Opts1#opts{tests = Ts}, Args);
[_,_|_] ->
{error,multiple_suites_and_cases};
_ ->
@@ -598,10 +637,10 @@ script_start3(StartOpts, Args) ->
case [suite_to_test(Dir,S) || S <- Suite] of
DirMods when GsAndCs == [] ->
Ts = tests(DirMods),
- script_start4(StartOpts1#opts{tests = Ts}, Args);
+ script_start4(Opts1#opts{tests = Ts}, Args);
DirMods = [_] when GsAndCs /= [] ->
Ts = tests(DirMods, GsAndCs),
- script_start4(StartOpts1#opts{tests = Ts}, Args);
+ script_start4(Opts1#opts{tests = Ts}, Args);
[_,_|_] when GsAndCs /= [] ->
{error,multiple_suites_and_cases};
_ ->
@@ -612,8 +651,8 @@ script_start3(StartOpts, Args) ->
{error,incorrect_start_options};
{undefined,undefined,_} ->
- if StartOpts#opts.vts ; StartOpts#opts.shell ->
- script_start4(StartOpts#opts{tests = []}, Args);
+ if Opts#opts.vts ; Opts#opts.shell ->
+ script_start4(Opts#opts{tests = []}, Args);
true ->
script_usage(),
{error,missing_start_options}
@@ -723,6 +762,7 @@ script_usage() ->
"\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[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
@@ -742,9 +782,11 @@ script_usage() ->
"\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]"
"\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]"
"\n\t[-allow_user_terms]"
+ "\n\t[-join_specs]"
"\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[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
@@ -839,7 +881,7 @@ run_test1(StartOpts) when is_list(StartOpts) ->
undefined ->
Tracing = start_trace(StartOpts),
{ok,Cwd} = file:get_cwd(),
- io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]),
+ io:format("~nCommon Test starting (cwd is ~ts)~n~n", [Cwd]),
Res =
case ct_repeat:loop_test(func, StartOpts) of
false ->
@@ -938,6 +980,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),
@@ -1000,7 +1043,8 @@ run_test2(StartOpts) ->
Step = get_start_opt(step, value, StartOpts),
Opts = #opts{label = Label, profile = Profile,
- cover = Cover, step = Step, logdir = LogDir,
+ cover = Cover, cover_stop = CoverStop,
+ step = Step, logdir = LogDir,
logopts = LogOpts, basic_html = BasicHtml,
config = CfgFiles,
verbosity = Verbosity,
@@ -1033,102 +1077,60 @@ run_test2(StartOpts) ->
end.
run_spec_file(Relaxed,
- Opts = #opts{testspecs = Specs, config = CfgFiles},
+ Opts = #opts{testspecs = Specs},
StartOpts) ->
Specs1 = case Specs of
[X|_] when is_integer(X) -> [Specs];
_ -> Specs
end,
AbsSpecs = lists:map(fun(SF) -> ?abs(SF) end, Specs1),
- log_ts_names(AbsSpecs),
- case catch ct_testspec:collect_tests_from_file(AbsSpecs, Relaxed) of
+ AbsSpecs1 = get_start_opt(join_specs, [AbsSpecs], AbsSpecs, StartOpts),
+ case catch ct_testspec:collect_tests_from_file(AbsSpecs1, Relaxed) of
{Error,CTReason} when Error == error ; Error == 'EXIT' ->
exit({error,CTReason});
- TS ->
- SpecOpts = get_data_for_node(TS, node()),
- Label = choose_val(Opts#opts.label,
- SpecOpts#opts.label),
- Profile = choose_val(Opts#opts.profile,
- SpecOpts#opts.profile),
- LogDir = choose_val(Opts#opts.logdir,
- 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),
- MultTT = choose_val(Opts#opts.multiply_timetraps,
- SpecOpts#opts.multiply_timetraps),
- ScaleTT = choose_val(Opts#opts.scale_timetraps,
- SpecOpts#opts.scale_timetraps),
- CreatePrivDir = choose_val(Opts#opts.create_priv_dir,
- SpecOpts#opts.create_priv_dir),
- AllEvHs = merge_vals([Opts#opts.event_handlers,
- SpecOpts#opts.event_handlers]),
- AllInclude = merge_vals([Opts#opts.include,
- SpecOpts#opts.include]),
- AllCTHooks = merge_vals([Opts#opts.ct_hooks,
- SpecOpts#opts.ct_hooks]),
- 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,
+ TestSpecData ->
+ run_all_specs(TestSpecData, Opts, StartOpts, [])
+ 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,
- 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,
- scale_timetraps = ScaleTT,
- create_priv_dir = CreatePrivDir,
- ct_hooks = AllCTHooks,
- enable_builtin_hooks = EnableBuiltinHooks
- },
-
- case check_and_install_configfiles(AllConfig,Opts1#opts.logdir,
- Opts1) of
- ok ->
- {Run,Skip} = ct_testspec:prepare_tests(TS, node()),
- reformat_result(catch do_run(Run, Skip, Opts1, StartOpts));
- {error,GCFReason} ->
- exit({error,GCFReason})
+run_all_specs([], _, _, TotResult) ->
+ TotResult1 = lists:reverse(TotResult),
+ case lists:keysearch('EXIT', 1, TotResult1) of
+ {value,{_,_,ExitReason}} ->
+ exit(ExitReason);
+ false ->
+ case lists:keysearch(error, 1, TotResult1) of
+ {value,Error} ->
+ Error;
+ false ->
+ lists:foldl(fun({Ok,Fail,{UserSkip,AutoSkip}},
+ {Ok1,Fail1,{UserSkip1,AutoSkip1}}) ->
+ {Ok1+Ok,Fail1+Fail,
+ {UserSkip1+UserSkip,
+ AutoSkip1+AutoSkip}}
+ end, {0,0,{0,0}}, TotResult1)
end
+ end;
+
+run_all_specs([{Specs,TS} | TSs], Opts, StartOpts, TotResult) ->
+ log_ts_names(Specs),
+ Combined = #opts{config = TSConfig} = combine_test_opts(TS, Specs, Opts),
+ AllConfig = merge_vals([Opts#opts.config, TSConfig]),
+ try run_one_spec(TS, Combined#opts{config = AllConfig}, StartOpts) of
+ Result ->
+ run_all_specs(TSs, Opts, StartOpts, [Result | TotResult])
+ catch
+ _ : Reason ->
+ run_all_specs(TSs, Opts, StartOpts, [{error,Reason} | TotResult])
+ end.
+
+run_one_spec(TS, CombinedOpts, StartOpts) ->
+ #opts{logdir = Logdir, config = Config} = CombinedOpts,
+ case check_and_install_configfiles(Config, Logdir, CombinedOpts) of
+ ok ->
+ {Run,Skip} = ct_testspec:prepare_tests(TS, node()),
+ reformat_result(catch do_run(Run, Skip, CombinedOpts, StartOpts));
+ Error ->
+ Error
end.
run_prepared(Run, Skip, Opts = #opts{logdir = LogDir,
@@ -1318,7 +1320,7 @@ run_testspec(TestSpec) ->
run_testspec1(TestSpec) ->
{ok,Cwd} = file:get_cwd(),
- io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]),
+ io:format("~nCommon Test starting (cwd is ~ts)~n~n", [Cwd]),
case catch run_testspec2(TestSpec) of
{'EXIT',Reason} ->
file:set_cwd(Cwd),
@@ -1376,6 +1378,7 @@ get_data_for_node(#testspec{label = Labels,
verbosity = VLvls,
silent_connections = SilentConnsList,
cover = CoverFs,
+ cover_stop = CoverStops,
config = Cfgs,
userconfig = UsrCfgs,
event_handler = EvHs,
@@ -1407,6 +1410,7 @@ get_data_for_node(#testspec{label = Labels,
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),
@@ -1425,6 +1429,7 @@ get_data_for_node(#testspec{label = Labels,
verbosity = Verbosity,
silent_connections = SilentConns,
cover = Cover,
+ cover_stop = CoverStop,
config = ConfigFiles,
event_handlers = EvHandlers,
ct_hooks = FiltCTHooks,
@@ -1452,7 +1457,7 @@ refresh_logs(LogDir) ->
{error,{all_runs_index,ARReason}};
_ ->
file:set_cwd(Cwd),
- io:format("Logs in ~s refreshed!~n",[LogDir]),
+ io:format("Logs in ~ts refreshed!~n",[LogDir]),
ok
end
end
@@ -1597,14 +1602,7 @@ 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,
@@ -1638,7 +1636,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
@@ -1776,7 +1780,7 @@ possibly_spawn(true, Tests, Skip, Opts) ->
end,
unlink(CTUtilSrv),
SupPid = spawn(Supervisor),
- io:format(user, "~nTest control handed over to process ~p~n~n",
+ io:format(user, "~nTest control handed over to process ~w~n~n",
[SupPid]),
SupPid.
@@ -1864,7 +1868,7 @@ verify_suites(TestSuites) ->
Suite)),
io:format(user,
"Suite ~w not found"
- "in directory ~s~n",
+ "in directory ~ts~n",
[Suite,TestDir]),
{Found,[{DS,[Name]}|NotFound]}
end
@@ -1879,7 +1883,7 @@ verify_suites(TestSuites) ->
ActualDir = filename:dirname(SuiteFile),
{[{ActualDir,Suite}|Found],NotFound};
false ->
- io:format(user, "Directory ~s is "
+ io:format(user, "Directory ~ts is "
"invalid~n", [Dir]),
Name = filename:join(Dir, atom_to_list(Suite)),
{Found,[{DS,[Name]}|NotFound]}
@@ -2119,7 +2123,7 @@ do_run_test(Tests, Skip, Opts) ->
cross = CovCross,
src = _CovSrc}} ->
ct_logs:log("COVER INFO",
- "Using cover specification file: ~s~n"
+ "Using cover specification file: ~ts~n"
"App: ~w~n"
"Cross cover: ~w~n"
"Including ~w modules~n"
@@ -2134,7 +2138,7 @@ do_run_test(Tests, Skip, Opts) ->
DelResult = file:delete(CovExport),
ct_logs:log("COVER INFO",
"Warning! "
- "Export file ~s already exists. "
+ "Export file ~ts already exists. "
"Deleting with result: ~p",
[CovExport,DelResult]);
false ->
@@ -2144,7 +2148,8 @@ 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
@@ -2216,6 +2221,15 @@ do_run_test(Tests, Skip, Opts) ->
end, CleanUp),
[code:del_path(Dir) || Dir <- AddedToPath],
+ %% 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;
@@ -2277,8 +2291,12 @@ add_jobs([{TestDir,all,_}|Tests], Skip, Opts, CleanUp) ->
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, CleanUp)
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, CleanUp);
+ _ ->
+ CleanUp
+ end
end;
add_jobs([{TestDir,[Suite],all}|Tests], Skip,
Opts, CleanUp) when is_atom(Suite) ->
@@ -2291,8 +2309,12 @@ add_jobs([{TestDir,Suites,all}|Tests], Skip,
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, CleanUp)
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, CleanUp);
+ _ ->
+ CleanUp
+ end
end;
add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) ->
case maybe_interpret(Suite, all, Opts) of
@@ -2304,8 +2326,12 @@ add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) ->
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2344,8 +2370,12 @@ add_jobs([{TestDir,Suite,Confs}|Tests], Skip, Opts, CleanUp) when
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2370,8 +2400,12 @@ add_jobs([{TestDir,Suite,Cases}|Tests],
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2387,8 +2421,12 @@ add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opts, CleanUp) when is_atom(Case) -
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2398,7 +2436,13 @@ add_jobs([], _, _, CleanUp) ->
wait_for_idle() ->
ct_util:update_last_run_index(),
- Notify = fun(Me) -> Me ! idle end,
+ Notify = fun(Me,IdleState) -> Me ! {idle,IdleState},
+ receive
+ {Me,proceed} -> ok
+ after
+ 30000 -> ok
+ end
+ end,
case catch test_server_ctrl:idle_notify(Notify) of
{'EXIT',_} ->
error;
@@ -2406,11 +2450,14 @@ wait_for_idle() ->
%% so we don't hang forever if test_server dies
Ref = erlang:monitor(process, TSPid),
Result = receive
- idle -> ok;
+ {idle,abort} -> aborted;
+ {idle,_} -> ok;
{'DOWN', Ref, _, _, _} -> error
end,
erlang:demonitor(Ref, [flush]),
ct_util:update_last_run_index(),
+ %% let test_server_ctrl proceed (and possibly shut down) now
+ TSPid ! {self(),proceed},
Result
end.
@@ -2606,7 +2653,7 @@ log_ts_names(Specs) ->
List = lists:map(fun(Name) ->
Name ++ " "
end, Specs),
- ct_logs:log("Test Specification file(s)", "~s",
+ ct_logs:log("Test Specification file(s)", "~ts",
[lists:flatten(List)]).
merge_arguments(Args) ->
@@ -2618,6 +2665,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));
@@ -2714,10 +2764,10 @@ event_handler_args2opts(Default, Args) ->
event_handler_init_args2opts(EHs)
end.
event_handler_init_args2opts([EH, Arg, "and" | EHs]) ->
- [{list_to_atom(EH),lists:flatten(io_lib:format("~s",[Arg]))} |
+ [{list_to_atom(EH),lists:flatten(io_lib:format("~ts",[Arg]))} |
event_handler_init_args2opts(EHs)];
event_handler_init_args2opts([EH, Arg]) ->
- [{list_to_atom(EH),lists:flatten(io_lib:format("~s",[Arg]))}];
+ [{list_to_atom(EH),lists:flatten(io_lib:format("~ts",[Arg]))}];
event_handler_init_args2opts([]) ->
[].
@@ -2867,6 +2917,10 @@ opts2args(EnvStartOpts) ->
[{allow_user_terms,[]}];
({allow_user_terms,false}) ->
[];
+ ({join_specs,true}) ->
+ [{join_specs,[]}];
+ ({join_specs,false}) ->
+ [];
({auto_compile,false}) ->
[{no_auto_compile,[]}];
({auto_compile,true}) ->
@@ -2900,11 +2954,11 @@ opts2args(EnvStartOpts) ->
[{event_handler_init,[atom_to_list(EH),ArgStr]}];
({event_handler,{EHs,Arg}}) when is_list(EHs) ->
ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
- Strs = lists:map(fun(EH) ->
- [atom_to_list(EH),
- ArgStr,"and"]
- end, EHs),
- [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
+ Strs = lists:flatmap(fun(EH) ->
+ [atom_to_list(EH),
+ ArgStr,"and"]
+ end, EHs),
+ [_LastAnd | StrsR] = lists:reverse(Strs),
[{event_handler_init,lists:reverse(StrsR)}];
({logopts,LOs}) when is_list(LOs) ->
[{logopts,[atom_to_list(LO) || LO <- LOs]}];
@@ -3035,7 +3089,7 @@ start_trace(Args) ->
false
end;
{_,Error} ->
- io:format("Warning! Tracing not started. Reason: ~s~n~n",
+ io:format("Warning! Tracing not started. Reason: ~ts~n~n",
[file:format_error(Error)]),
false
end;
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_telnet.erl b/lib/common_test/src/ct_telnet.erl
index b13c050e32..02186864a5 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-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
@@ -201,7 +201,7 @@ open(KeyOrName,ConnType,TargetMod,Extra) ->
close(Connection) ->
case get_handle(Connection) of
{ok,Pid} ->
- log("ct_telnet:close","Handle: ~p",[Pid]),
+ log("ct_telnet:close","Handle: ~w",[Pid]),
case ct_gen_conn:stop(Pid) of
{error,{process_down,Pid,noproc}} ->
{error,already_closed};
@@ -558,7 +558,7 @@ reconnect(Ip,Port,N,State=#state{target_mod=TargetMod,
Error when N==0 ->
Error;
_Error ->
- log("Reconnect failed!","Retries left: ~p",[N]),
+ log("Reconnect failed!","Retries left: ~w",[N]),
timer:sleep(ReconnInt),
reconnect(Ip,Port,N-1,State)
end.
@@ -567,7 +567,7 @@ reconnect(Ip,Port,N,State=#state{target_mod=TargetMod,
%% @hidden
terminate(TelnPid,State) ->
log(heading(terminate,State#state.name),
- "Closing telnet connection.\nId: ~p",
+ "Closing telnet connection.\nId: ~w",
[TelnPid]),
ct_telnet_client:close(TelnPid).
@@ -899,7 +899,7 @@ one_expect(Data,Pattern,EO) ->
[Prompt] when Prompt==prompt; Prompt=={prompt,PromptType} ->
%% Only searching for prompt
log_lines(UptoPrompt),
- try_cont_log("<b>PROMPT:</b> ~s", [PromptType]),
+ try_cont_log("<b>PROMPT:</b> ~ts", [PromptType]),
{match,{prompt,PromptType},Rest};
[{prompt,_OtherPromptType}] ->
%% Only searching for one specific prompt, not thisone
@@ -969,7 +969,7 @@ seq_expect1(Data,[prompt|Patterns],Acc,Rest,EO) ->
{continue,[prompt|Patterns],Acc,LastLine};
PromptType ->
log_lines(Data),
- try_cont_log("<b>PROMPT:</b> ~s", [PromptType]),
+ try_cont_log("<b>PROMPT:</b> ~ts", [PromptType]),
seq_expect(Rest,Patterns,[{prompt,PromptType}|Acc],EO)
end;
seq_expect1(Data,[{prompt,PromptType}|Patterns],Acc,Rest,EO) ->
@@ -980,7 +980,7 @@ seq_expect1(Data,[{prompt,PromptType}|Patterns],Acc,Rest,EO) ->
{continue,[{prompt,PromptType}|Patterns],Acc,LastLine};
PromptType ->
log_lines(Data),
- try_cont_log("<b>PROMPT:</b> ~s", [PromptType]),
+ try_cont_log("<b>PROMPT:</b> ~ts", [PromptType]),
seq_expect(Rest,Patterns,[{prompt,PromptType}|Acc],EO);
_OtherPromptType ->
log_lines(Data),
@@ -1032,13 +1032,13 @@ match_line(Line,Patterns,FoundPrompt,EO) ->
match_line(Line,[prompt|Patterns],false,EO,RetTag) ->
match_line(Line,Patterns,false,EO,RetTag);
match_line(Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) ->
- try_cont_log(" ~s", [Line]),
- try_cont_log("<b>PROMPT:</b> ~s", [FoundPrompt]),
+ try_cont_log(" ~ts", [Line]),
+ try_cont_log("<b>PROMPT:</b> ~ts", [FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
match_line(Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag)
when PromptType==FoundPrompt ->
- try_cont_log(" ~s", [Line]),
- try_cont_log("<b>PROMPT:</b> ~s", [FoundPrompt]),
+ try_cont_log(" ~ts", [Line]),
+ try_cont_log("<b>PROMPT:</b> ~ts", [FoundPrompt]),
{RetTag,{prompt,FoundPrompt}};
match_line(Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag)
when PromptType=/=FoundPrompt ->
@@ -1048,7 +1048,7 @@ match_line(Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) ->
nomatch ->
match_line(Line,Patterns,FoundPrompt,EO,RetTag);
{match,Match} ->
- try_cont_log("<b>MATCH:</b> ~s", [Line]),
+ try_cont_log("<b>MATCH:</b> ~ts", [Line]),
{RetTag,{Tag,Match}}
end;
match_line(Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) ->
@@ -1056,13 +1056,13 @@ match_line(Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) ->
nomatch ->
match_line(Line,Patterns,FoundPrompt,EO,RetTag);
{match,Match} ->
- try_cont_log("<b>MATCH:</b> ~s", [Line]),
+ try_cont_log("<b>MATCH:</b> ~ts", [Line]),
{RetTag,Match}
end;
match_line(Line,[],FoundPrompt,EO,match) ->
match_line(Line,EO#eo.haltpatterns,FoundPrompt,EO,halt);
match_line(Line,[],_FoundPrompt,_EO,halt) ->
- try_cont_log(" ~s", [Line]),
+ try_cont_log(" ~ts", [Line]),
nomatch.
one_line([$\n|Rest],Line) ->
@@ -1086,7 +1086,7 @@ log_lines(String) ->
[] ->
ok;
LastLine ->
- try_cont_log(" ~s", [LastLine])
+ try_cont_log(" ~ts", [LastLine])
end.
log_lines_not_last(String) ->
@@ -1094,7 +1094,7 @@ log_lines_not_last(String) ->
{[],LastLine} ->
LastLine;
{String1,LastLine} ->
- try_cont_log("~s",[String1]),
+ try_cont_log("~ts",[String1]),
LastLine
end.
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index d703b39ac5..7329498ed6 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. 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
@@ -308,7 +308,7 @@ cmd_dbg(_Cmd) ->
[Opt] -> Opt;
_ -> Opts
end,
- io:format("~s(~w): ~w\n", [CtrlStr,Ctrl,Opts1]);
+ io:format("~ts(~w): ~w\n", [CtrlStr,Ctrl,Opts1]);
Any ->
io:format("Unexpected in cmd_dbg:~n~w~n",[Any])
end.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 5ce095e38e..71b03c0ea6 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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,6 @@
collect_tests_from_file/2, collect_tests_from_file/3]).
-include("ct_util.hrl").
-
-define(testspec_fields, record_info(fields, testspec)).
%%%------------------------------------------------------------------
@@ -93,7 +92,7 @@ prepare_tests(TestSpec) when is_record(TestSpec,testspec) ->
%% run_per_node/2 takes the Run list as input and returns a list
%% of {Node,RunPerNode,[]} tuples where the tests have been sorted
%% on a per node basis.
-run_per_node([{{Node,Dir},Test}|Ts],Result, MergeTests) ->
+run_per_node([{{Node,Dir},Test}|Ts],Result,MergeTests) ->
{value,{Node,{Run,Skip}}} = lists:keysearch(Node,1,Result),
Run1 = case MergeTests of
false ->
@@ -190,7 +189,7 @@ prepare_suites(_Node,_Dir,[],Run,Skip) ->
prepare_cases(Node,Dir,Suite,Cases) ->
case get_skipped_cases(Node,Dir,Suite,Cases) of
- SkipAll=[{{Node,Dir},{Suite,_Cmt}}] -> % all cases to be skipped
+ SkipAll=[{{Node,Dir},{Suite,_Cmt}}] -> % all cases to be skipped
%% note: this adds an 'all' test even if only skip is specified
{[{{Node,Dir},{Suite,all}}],SkipAll};
Skipped ->
@@ -241,34 +240,147 @@ get_skipped_cases1(_,_,_,[]) ->
%%% collect_tests_from_file reads a testspec file and returns a record
%%% containing the data found.
-collect_tests_from_file(Specs, Relaxed) ->
+collect_tests_from_file(Specs,Relaxed) ->
collect_tests_from_file(Specs,[node()],Relaxed).
collect_tests_from_file(Specs,Nodes,Relaxed) when is_list(Nodes) ->
NodeRefs = lists:map(fun(N) -> {undefined,N} end, Nodes),
- catch collect_tests_from_file1(Specs,#testspec{nodes=NodeRefs},Relaxed).
+ %% [Spec1,Spec2,...] means create one testpec record per Spec file
+ %% [[Spec1,Spec2,...]] means merge all specs into one testspec record
+ {Join,Specs1} = if is_list(hd(hd(Specs))) -> {true,hd(Specs)};
+ true -> {false,Specs}
+ end,
+ Specs2 = [filename:absname(S) || S <- Specs1],
+ TS0 = #testspec{nodes=NodeRefs},
+
+ try create_specs(Specs2,TS0,Relaxed,Join) of
+ {{[],_},SeparateTestSpecs} ->
+ filter_and_convert(SeparateTestSpecs);
+ {{_,#testspec{tests=[]}},SeparateTestSpecs} ->
+ filter_and_convert(SeparateTestSpecs);
+ {Joined,SeparateTestSpecs} ->
+ [filter_and_convert(Joined) |
+ filter_and_convert(SeparateTestSpecs)]
+ catch
+ _:Error ->
+ Error
+ end.
+
+filter_and_convert(Joined) when is_tuple(Joined) ->
+ hd(filter_and_convert([Joined]));
+filter_and_convert([{_,#testspec{tests=[]}}|TSs]) ->
+ filter_and_convert(TSs);
+filter_and_convert([{[{SpecFile,MergeTests}|SMs],TestSpec}|TSs]) ->
+ #testspec{config = CfgFiles} = TestSpec,
+ TestSpec1 = TestSpec#testspec{config = delete_dups(CfgFiles),
+ merge_tests = MergeTests},
+ %% set the merge_tests value for the testspec to the value
+ %% of the first test spec in the set
+ [{[SpecFile | [SF || {SF,_} <- SMs]], TestSpec1} | filter_and_convert(TSs)];
+filter_and_convert([]) ->
+ [].
+
+delete_dups(Elems) ->
+ delete_dups1(lists:reverse(Elems),[]).
-collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) ->
+delete_dups1([E|Es],Keep) ->
+ case lists:member(E,Es) of
+ true ->
+ delete_dups1(Es,Keep);
+ false ->
+ delete_dups1(Es,[E|Keep])
+ end;
+delete_dups1([],Keep) ->
+ Keep.
+
+create_specs(Specs,TestSpec,Relaxed,Join) ->
+ SpecsTree = create_spec_tree(Specs,TestSpec,Join,[]),
+ create_specs(SpecsTree,TestSpec,Relaxed).
+
+create_spec_tree([Spec|Specs],TS,JoinWithNext,Known) ->
SpecDir = filename:dirname(filename:absname(Spec)),
- case file:consult(Spec) of
- {ok,Terms} ->
- 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.
+ TS1 = TS#testspec{spec_dir=SpecDir},
+ SpecAbsName = get_absfile(Spec,TS1),
+ case lists:member(SpecAbsName,Known) of
+ true ->
+ throw({error,{cyclic_reference,SpecAbsName}});
+ false ->
+ case file:consult(SpecAbsName) of
+ {ok,Terms} ->
+ Terms1 = replace_names(Terms),
+ {InclJoin,InclSep} = get_included_specs(Terms1,TS1),
+ {SpecAbsName,Terms1,
+ create_spec_tree(InclJoin,TS,true,[SpecAbsName|Known]),
+ create_spec_tree(InclSep,TS,false,[SpecAbsName|Known]),
+ JoinWithNext,
+ create_spec_tree(Specs,TS,JoinWithNext,Known)};
+ {error,Reason} ->
+ ReasonStr =
+ lists:flatten(io_lib:format("~s",
+ [file:format_error(Reason)])),
+ throw({error,{SpecAbsName,ReasonStr}})
+ end
+ end;
+create_spec_tree([],_TS,_JoinWithNext,_Known) ->
+ [].
+
+create_specs({Spec,Terms,InclJoin,InclSep,JoinWithNext,NextSpec},
+ TestSpec,Relaxed) ->
+ SpecDir = filename:dirname(filename:absname(Spec)),
+ TestSpec1 = create_spec(Terms,TestSpec#testspec{spec_dir=SpecDir},
+ JoinWithNext,Relaxed),
+
+ {{JoinSpecs1,JoinTS1},Separate1} = create_specs(InclJoin,TestSpec1,Relaxed),
+
+ {{JoinSpecs2,JoinTS2},Separate2} =
+ case JoinWithNext of
+ true ->
+ create_specs(NextSpec,JoinTS1,Relaxed);
+ false ->
+ {{[],JoinTS1},[]}
+ end,
+ {SepJoinSpecs,Separate3} = create_specs(InclSep,TestSpec,Relaxed),
+ {SepJoinSpecs1,Separate4} =
+ case JoinWithNext of
+ true ->
+ {{[],TestSpec},[]};
+ false ->
+ create_specs(NextSpec,TestSpec,Relaxed)
+ end,
+
+ SpecInfo = {Spec,TestSpec1#testspec.merge_tests},
+ AllSeparate =
+ [TSData || TSData = {Ss,_TS} <- Separate3++Separate1++
+ [SepJoinSpecs]++Separate2++
+ [SepJoinSpecs1]++Separate4,
+ Ss /= []],
+
+ case {JoinWithNext,JoinSpecs1} of
+ {true,_} ->
+ {{[SpecInfo|(JoinSpecs1++JoinSpecs2)],JoinTS2},
+ AllSeparate};
+ {false,[]} ->
+ {{[],TestSpec},
+ [{[SpecInfo],TestSpec1}|AllSeparate]};
+ {false,_} ->
+ {{[SpecInfo|(JoinSpecs1++JoinSpecs2)],JoinTS2},
+ AllSeparate}
+ end;
+create_specs([],TestSpec,_Relaxed) ->
+ {{[],TestSpec},[]}.
+
+create_spec(Terms,TestSpec,JoinedByPrev,Relaxed) ->
+ %% it's the "includer" that decides the value of merge_tests
+ Terms1 = if not JoinedByPrev ->
+ [{set_merge_tests,true}|Terms];
+ true ->
+ [{set_merge_tests,false}|Terms]
+ end,
+ TS = #testspec{tests=Tests, logdir=LogDirs} =
+ collect_tests({false,Terms1},TestSpec,Relaxed),
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests),
+ logdir=LogDirs1}.
collect_tests_from_list(Terms,Relaxed) ->
collect_tests_from_list(Terms,[node()],Relaxed).
@@ -276,8 +388,8 @@ collect_tests_from_list(Terms,Relaxed) ->
collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) ->
{ok,Cwd} = file:get_cwd(),
NodeRefs = lists:map(fun(N) -> {undefined,N} end, Nodes),
- case catch collect_tests(Terms,#testspec{nodes=NodeRefs,
- spec_dir=Cwd},
+ case catch collect_tests({true,Terms},#testspec{nodes=NodeRefs,
+ spec_dir=Cwd},
Relaxed) of
E = {error,_} ->
E;
@@ -287,13 +399,28 @@ collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) ->
TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1}
end.
-collect_tests(Terms,TestSpec,Relaxed) ->
+collect_tests({Replace,Terms},TestSpec=#testspec{alias=As,nodes=Ns},Relaxed) ->
put(relaxed,Relaxed),
- 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).
+ Terms1 = if Replace -> replace_names(Terms);
+ true -> Terms
+ end,
+ {MergeTestsDef,Terms2} =
+ case proplists:get_value(set_merge_tests,Terms1,true) of
+ false ->
+ %% disable merge_tests
+ {TestSpec#testspec.merge_tests,
+ proplists:delete(merge_tests,Terms1)};
+ true ->
+ {true,Terms1}
+ end,
+ %% reverse nodes and aliases initially to get the order of them right
+ %% in case this spec is being joined with a previous one
+ TestSpec1 = get_global(Terms2,TestSpec#testspec{alias = lists:reverse(As),
+ nodes = lists:reverse(Ns),
+ merge_tests = MergeTestsDef}),
+ TestSpec2 = get_all_nodes(Terms2,TestSpec1),
+ {Terms3, TestSpec3} = filter_init_terms(Terms2, [], TestSpec2),
+ add_tests(Terms3,TestSpec3).
%% replace names (atoms) in the testspec matching those in 'define' terms by
%% searching recursively through tuples and lists
@@ -420,9 +547,30 @@ replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) ->
replace_names_in_node1(NodeStr,[]) ->
NodeStr.
+%% look for other specification files, either to join with the
+%% current spec, or execute as separate test runs
+get_included_specs(Terms,TestSpec) ->
+ get_included_specs(Terms,TestSpec,[],[]).
+
+get_included_specs([{specs,How,SpecOrSpecs}|Ts],TestSpec,Join,Sep) ->
+ Specs = case SpecOrSpecs of
+ [File|_] when is_list(File) ->
+ [get_absfile(Spec,TestSpec) || Spec <- SpecOrSpecs];
+ [Ch|_] when is_integer(Ch) ->
+ [get_absfile(SpecOrSpecs,TestSpec)]
+ end,
+ if How == join ->
+ get_included_specs(Ts,TestSpec,Join++Specs,Sep);
+ true ->
+ get_included_specs(Ts,TestSpec,Join,Sep++Specs)
+ end;
+get_included_specs([_|Ts],TestSpec,Join,Sep) ->
+ get_included_specs(Ts,TestSpec,Join,Sep);
+get_included_specs([],_,Join,Sep) ->
+ {Join,Sep}.
%% global terms that will be used for analysing all other terms in the spec
-get_global([{merge_tests,Bool} | Ts], 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
@@ -588,7 +736,7 @@ add_option({Key,Value},Node,List,WarnIfExists) when is_list(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",
+ "defined for node ~w, skipping new ~w~n",
[Key,OldOption,Node,Value]),
OldOption;
{Key,OldOption}->
@@ -637,7 +785,7 @@ add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec) ->
add_tests([{suites,Dir,Ss}|Ts],Spec) ->
add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec);
add_tests([{suites,Nodes,Dir,Ss}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,suites,[Dir,Ss],Ts,Spec#testspec.nodes),
+ Ts1 = per_node(Nodes,suites,[Dir,Ss],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{suites,Node,Dir,Ss}|Ts],Spec) ->
Tests = Spec#testspec.tests,
@@ -660,11 +808,11 @@ add_tests([{groups,Dir,Suite,Gs}|Ts],Spec) ->
add_tests([{groups,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
add_tests([{groups,all_nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec);
add_tests([{groups,Nodes,Dir,Suite,Gs}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,groups,[Dir,Suite,Gs],Ts,Spec#testspec.nodes),
+ Ts1 = per_node(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,
+ Ts1 = per_node(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,
Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{groups,Node,Dir,Suite,Gs}|Ts],Spec) ->
@@ -688,7 +836,7 @@ add_tests([{cases,all_nodes,Dir,Suite,Cs}|Ts],Spec) ->
add_tests([{cases,Dir,Suite,Cs}|Ts],Spec) ->
add_tests([{cases,all_nodes,Dir,Suite,Cs}|Ts],Spec);
add_tests([{cases,Nodes,Dir,Suite,Cs}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,cases,[Dir,Suite,Cs],Ts,Spec#testspec.nodes),
+ Ts1 = per_node(Nodes,cases,[Dir,Suite,Cs],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
@@ -703,7 +851,7 @@ add_tests([{skip_suites,all_nodes,Dir,Ss,Cmt}|Ts],Spec) ->
add_tests([{skip_suites,Dir,Ss,Cmt}|Ts],Spec) ->
add_tests([{skip_suites,all_nodes,Dir,Ss,Cmt}|Ts],Spec);
add_tests([{skip_suites,Nodes,Dir,Ss,Cmt}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,skip_suites,[Dir,Ss,Cmt],Ts,Spec#testspec.nodes),
+ Ts1 = per_node(Nodes,skip_suites,[Dir,Ss,Cmt],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
@@ -724,11 +872,11 @@ add_tests([{skip_groups,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec);
add_tests([{skip_groups,Nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,Cmt],Ts,Spec#testspec.nodes),
+ Ts1 = per_node(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,
+ Ts1 = per_node(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) ->
@@ -752,7 +900,7 @@ add_tests([{skip_cases,all_nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) ->
add_tests([{skip_cases,Dir,Suite,Cs,Cmt}|Ts],Spec) ->
add_tests([{skip_cases,all_nodes,Dir,Suite,Cs,Cmt}|Ts],Spec);
add_tests([{skip_cases,Nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,skip_cases,[Dir,Suite,Cs,Cmt],Ts,Spec#testspec.nodes),
+ Ts1 = per_node(Nodes,skip_cases,[Dir,Suite,Cs,Cmt],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{skip_cases,Node,Dir,Suite,Cs,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
@@ -783,6 +931,9 @@ add_tests([{release_shell,Bool}|Ts],Spec) ->
add_tests(Ts, Spec#testspec{release_shell = Bool});
%% --- handled/errors ---
+add_tests([{set_merge_tests,_}|Ts],Spec) -> % internal
+ add_tests(Ts,Spec);
+
add_tests([{define,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
@@ -792,7 +943,10 @@ add_tests([{alias,_,_}|Ts],Spec) -> % handled
add_tests([{node,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
-add_tests([{merge_tests, _} | Ts], Spec) -> % handled
+add_tests([{merge_tests,_} | Ts], Spec) -> % handled
+ add_tests(Ts,Spec);
+
+add_tests([{specs,_,_} | Ts], Spec) -> % handled
add_tests(Ts,Spec);
%% --------------------------------------------------
@@ -821,7 +975,7 @@ 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),
+ Ts1 = per_node(NodesOrOther,Tag,[Data],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
false ->
add_tests([{Tag,all_nodes,{NodesOrOther,Data}}|Ts],Spec)
@@ -866,7 +1020,7 @@ add_tests([],Spec) -> % done
%% 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) ->
+check_term(Term) when is_tuple(Term) ->
Size = size(Term),
[Name|_] = tuple_to_list(Term),
Valid = valid_terms(),
@@ -903,6 +1057,8 @@ 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) ->
@@ -975,12 +1131,12 @@ update_recorded(Tag,Node,Spec) ->
end.
%% create one test term per node
-separate(Nodes,Tag,Data,Tests,Refs) ->
- Separated = separate(Nodes,Tag,Data,Refs),
+per_node(Nodes,Tag,Data,Tests,Refs) ->
+ Separated = per_node(Nodes,Tag,Data,Refs),
Separated ++ Tests.
-separate([N|Ns],Tag,Data,Refs) ->
- [list_to_tuple([Tag,ref2node(N,Refs)|Data])|separate(Ns,Tag,Data,Refs)];
-separate([],_,_,_) ->
+per_node([N|Ns],Tag,Data,Refs) ->
+ [list_to_tuple([Tag,ref2node(N,Refs)|Data])|per_node(Ns,Tag,Data,Refs)];
+per_node([],_,_,_) ->
[].
%% read the value for FieldName in record Rec#testspec
@@ -1037,14 +1193,21 @@ insert_groups(Node,Dir,Suite,Groups,Cases,Tests,true) when
{[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,Groups1,Suites0),
- insert_in_order({{Node,Dir},Suites1},Tests);
- false ->
- insert_in_order({{Node,Dir},[{Suite,Groups1}]},Tests)
+ {Tests1,Done} =
+ lists:foldr(fun(All={{N,D},[{all,_}]},{Replaced,_}) when N == Node,
+ D == Dir ->
+ {[All|Replaced],true};
+ ({{N,D},Suites0},{Replaced,_}) when N == Node,
+ D == Dir ->
+ Suites1 = insert_groups1(Suite,Groups1,Suites0),
+ {[{{N,D},Suites1}|Replaced],true};
+ (T,{Replaced,Match}) ->
+ {[T|Replaced],Match}
+ end, {[],false}, Tests),
+ if not Done ->
+ Tests ++ [{{Node,Dir},[{Suite,Groups1}]}];
+ true ->
+ Tests1
end;
insert_groups(Node,Dir,Suite,Groups,Case,Tests, MergeTests)
when is_atom(Case) ->
@@ -1082,14 +1245,21 @@ insert_groups2([],GrAndCases) ->
insert_cases(Node,Dir,Suite,Cases,Tests,false) when is_list(Cases) ->
append({{Node,Dir},[{Suite,Cases}]},Tests);
insert_cases(Node,Dir,Suite,Cases,Tests,true) when is_list(Cases) ->
- case lists:keysearch({Node,Dir},1,Tests) of
- {value,{{Node,Dir},[{all,_}]}} ->
- Tests;
- {value,{{Node,Dir},Suites0}} ->
- Suites1 = insert_cases1(Suite,Cases,Suites0),
- insert_in_order({{Node,Dir},Suites1},Tests);
- false ->
- insert_in_order({{Node,Dir},[{Suite,Cases}]},Tests)
+ {Tests1,Done} =
+ lists:foldr(fun(All={{N,D},[{all,_}]},{Replaced,_}) when N == Node,
+ D == Dir ->
+ {[All|Replaced],true};
+ ({{N,D},Suites0},{Replaced,_}) when N == Node,
+ D == Dir ->
+ Suites1 = insert_cases1(Suite,Cases,Suites0),
+ {[{{N,D},Suites1}|Replaced],true};
+ (T,{Replaced,Match}) ->
+ {[T|Replaced],Match}
+ end, {[],false}, Tests),
+ if not Done ->
+ Tests ++ [{{Node,Dir},[{Suite,Cases}]}];
+ true ->
+ Tests1
end;
insert_cases(Node,Dir,Suite,Case,Tests,MergeTests) when is_atom(Case) ->
insert_cases(Node,Dir,Suite,[Case],Tests,MergeTests).
@@ -1130,15 +1300,23 @@ skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,false) when
append({{Node,Dir},Suites1},Tests);
skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,true) when
((Cases == all) or is_list(Cases)) and is_list(Groups) ->
- Suites =
- case lists:keysearch({Node,Dir},1,Tests) of
- {value,{{Node,Dir},Suites0}} ->
- Suites0;
- false ->
- []
- end,
- Suites1 = skip_groups1(Suite,[{Gr,Cases} || Gr <- Groups],Cmt,Suites),
- insert_in_order({{Node,Dir},Suites1},Tests);
+ {Tests1,Done} =
+ lists:foldr(fun({{N,D},Suites0},{Replaced,_}) when N == Node,
+ D == Dir ->
+ Suites1 = skip_groups1(Suite,
+ [{Gr,Cases} || Gr <- Groups],
+ Cmt,Suites0),
+ {[{{N,D},Suites1}|Replaced],true};
+ (T,{Replaced,Match}) ->
+ {[T|Replaced],Match}
+ end, {[],false}, Tests),
+ if not Done ->
+ Tests ++ [{{Node,Dir},skip_groups1(Suite,
+ [{Gr,Cases} || Gr <- Groups],
+ Cmt,[])}];
+ true ->
+ Tests1
+ end;
skip_groups(Node,Dir,Suite,Groups,Case,Cmt,Tests,MergeTests)
when is_atom(Case) ->
Cases = if Case == all -> all; true -> [Case] end,
@@ -1160,15 +1338,19 @@ skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,false) when is_list(Cases) ->
Suites1 = skip_cases1(Suite,Cases,Cmt,[]),
append({{Node,Dir},Suites1},Tests);
skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,true) when is_list(Cases) ->
- Suites =
- case lists:keysearch({Node,Dir},1,Tests) of
- {value,{{Node,Dir},Suites0}} ->
- Suites0;
- false ->
- []
- end,
- Suites1 = skip_cases1(Suite,Cases,Cmt,Suites),
- insert_in_order({{Node,Dir},Suites1},Tests);
+ {Tests1,Done} =
+ lists:foldr(fun({{N,D},Suites0},{Replaced,_}) when N == Node,
+ D == Dir ->
+ Suites1 = skip_cases1(Suite,Cases,Cmt,Suites0),
+ {[{{N,D},Suites1}|Replaced],true};
+ (T,{Replaced,Match}) ->
+ {[T|Replaced],Match}
+ end, {[],false}, Tests),
+ if not Done ->
+ Tests ++ [{{Node,Dir},skip_cases1(Suite,Cases,Cmt,[])}];
+ true ->
+ Tests1
+ end;
skip_cases(Node,Dir,Suite,Case,Cmt,Tests,MergeTests) when is_atom(Case) ->
skip_cases(Node,Dir,Suite,[Case],Cmt,Tests,MergeTests).
@@ -1258,10 +1440,14 @@ is_node([],_) ->
valid_terms() ->
[
+ {set_merge_tests,2},
{define,3},
+ {specs,3},
{node,3},
{cover,2},
{cover,3},
+ {cover_stop,2},
+ {cover_stop,3},
{config,2},
{config,3},
{config,4},
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index cf891ed043..2e7e731595 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -1,7 +1,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
@@ -39,7 +39,8 @@
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, get_testdata/2,
- set_testdata_async/1, update_testdata/2, update_testdata/3]).
+ set_testdata_async/1, update_testdata/2, update_testdata/3,
+ set_verbosity/1, get_verbosity/1]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
@@ -128,6 +129,10 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
create_table(?conn_table,#conn.handle),
create_table(?board_table,2),
create_table(?suite_table,#suite_data.key),
+
+ create_table(?verbosity_table,1),
+ [ets:insert(?verbosity_table,{Cat,Lvl}) || {Cat,Lvl} <- Verbosity],
+
{ok,StartDir} = file:get_cwd(),
case file:set_cwd(LogDir) of
ok -> ok;
@@ -202,7 +207,7 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
self() ! {{stop,{self(),{user_error,CTHReason}}},
{Parent,make_ref()}}
end,
- loop(Mode, [{{verbosity,Cat},Lvl} || {Cat,Lvl} <- Verbosity], StartDir).
+ loop(Mode, [], StartDir).
create_table(TableName,KeyPos) ->
create_table(TableName,set,KeyPos).
@@ -278,6 +283,19 @@ reset_cwd() ->
get_start_dir() ->
call(get_start_dir).
+%% handle verbosity outside ct_util_server (let the client read
+%% the verbosity table) to avoid possible deadlock situations
+set_verbosity(Elem = {_Category,_Level}) ->
+ ets:insert(?verbosity_table, Elem),
+ ok.
+get_verbosity(Category) ->
+ case ets:lookup(?verbosity_table, Category) of
+ [{Category,Level}] ->
+ Level;
+ _ ->
+ undefined
+ end.
+
loop(Mode,TestData,StartDir) ->
receive
{update_last_run_index,From} ->
@@ -377,6 +395,7 @@ loop(Mode,TestData,StartDir) ->
ets:delete(?conn_table),
ets:delete(?board_table),
ets:delete(?suite_table),
+ ets:delete(?verbosity_table),
ct_logs:close(Info, StartDir),
ct_event:stop(),
ct_config:stop(),
@@ -396,14 +415,14 @@ loop(Mode,TestData,StartDir) ->
%% 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"
+ "Pid: ~w, Address: ~p, Callback: ~w\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",
+ io:format("\n\nct_util_server got EXIT from ~w: ~p\n\n",
[Pid,Reason]),
file:set_cwd(StartDir),
exit(Reason)
@@ -956,7 +975,7 @@ open_url(iexplore, Args, URL) ->
Path = proplists:get_value(default, Paths),
[Cmd | _] = string:tokens(Path, "%"),
Cmd1 = Cmd ++ " " ++ Args ++ " " ++ URL,
- io:format(user, "~nOpening ~s with command:~n ~s~n", [URL,Cmd1]),
+ io:format(user, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd1]),
open_port({spawn,Cmd1}, []);
_ ->
io:format("~nNo path to iexplore.exe~n",[])
@@ -969,6 +988,6 @@ open_url(Prog, Args, URL) ->
is_list(Prog) -> Prog
end,
Cmd = ProgStr ++ " " ++ Args ++ " " ++ URL,
- io:format(user, "~nOpening ~s with command:~n ~s~n", [URL,Cmd]),
+ io:format(user, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd]),
open_port({spawn,Cmd},[]),
ok.
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 196b5e46d0..7c01e17c36 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-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
@@ -21,6 +21,7 @@
-define(conn_table,ct_connections).
-define(board_table,ct_boards).
-define(suite_table,ct_suite_data).
+-define(verbosity_table,ct_verbosity_table).
-record(conn, {handle,
targetref,
@@ -38,6 +39,7 @@
verbosity=[],
silent_connections=[],
cover=[],
+ cover_stop=[],
config=[],
userconfig=[],
event_handler=[],
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
index 3af89db3a5..644594e34d 100644
--- a/lib/common_test/src/cth_conn_log.erl
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -1,7 +1,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
@@ -58,7 +58,7 @@
-spec init(Id, HookOpts) -> Result when
Id :: term(),
- HookOpts :: ct:hook_options(),
+ HookOpts :: ct_netconfc:hook_options(),
Result :: {ok,[{ct_netconfc:conn_mod(),
{ct_netconfc:log_type(),[ct_netconfc:key_or_name()]}}]}.
init(_Id, HookOpts) ->
@@ -105,8 +105,9 @@ pre_init_per_testcase(TestCase,Config,CthState) ->
"<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)])
+ "<tr><td>~p</td><td><a href=\"~ts\">~ts</a>"
+ "</td></tr>",
+ [S,ct_logs:uri(L),filename:basename(L)])
|| {S,L} <- Ls] ++
"</table>",
io:format(Str,[]),
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 e6eaad8d48..1a38b6584b 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -1,7 +1,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
@@ -297,7 +297,7 @@ 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]).
+ io_lib:format("~w-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]).
make_url(undefined,_) ->
undefined;
diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl
index 25b9d4d5d2..99ce92e9f1 100644
--- a/lib/common_test/src/unix_telnet.erl
+++ b/lib/common_test/src/unix_telnet.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -110,7 +110,7 @@ connect1(Ip,Port,Timeout,KeepAlive,Username,Password) ->
case ct_telnet:silent_teln_expect(Pid,[],[prompt],?prx,[]) of
{ok,{prompt,?username},_} ->
ok = ct_telnet_client:send_data(Pid,Username),
- cont_log("Username: ~s",[Username]),
+ cont_log("Username: ~ts",[Username]),
case ct_telnet:silent_teln_expect(Pid,[],prompt,?prx,[]) of
{ok,{prompt,?password},_} ->
ok = ct_telnet_client:send_data(Pid,Password),
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 8cdc4ebed7..bd746f87a7 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -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
@@ -40,6 +40,7 @@ MODULES= \
ct_repeat_1_SUITE \
ct_testspec_1_SUITE \
ct_testspec_2_SUITE \
+ ct_testspec_3_SUITE \
ct_skip_SUITE \
ct_error_SUITE \
ct_test_server_if_1_SUITE \
@@ -52,6 +53,10 @@ MODULES= \
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
@@ -107,7 +112,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/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/inviso/doc/html/.gitignore b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore
index e69de29bb2..e69de29bb2 100644
--- a/lib/inviso/doc/html/.gitignore
+++ 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 338e76264e..3881ced17d 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -44,8 +44,12 @@
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- Config1 = ct_test_support:init_per_suite(Config),
- Config1.
+ DataDir = ?config(data_dir, Config),
+ TestDir = filename:join(DataDir, "error/test/"),
+ CTH = filename:join(TestDir, "verify_config.erl"),
+ ct:pal("Compiling ~p: ~p",
+ [CTH,compile:file(CTH,[{outdir,TestDir},debug_info])]),
+ ct_test_support:init_per_suite([{path_dirs,[TestDir]} | Config]).
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
@@ -61,7 +65,8 @@ 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,
+ config_restored].
groups() ->
[].
@@ -251,6 +256,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,
@@ -267,6 +290,24 @@ misc_errors(Config) when is_list(Config) ->
TestEvents = events_to_check(misc_errors),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
+%%%-----------------------------------------------------------------
+%%%
+config_restored(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "error/test/config_restored_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},
+ {ct_hooks,[verify_config]}],
+ Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(config_restored,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(config_restored),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
@@ -429,8 +470,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 +540,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}},
@@ -971,11 +1011,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 +1454,48 @@ 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,[]}
+ ];
+
+test_events(config_restored) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,4}},
+ {?eh,tc_start,{config_restored_SUITE,init_per_suite}},
+ {?eh,tc_done,{config_restored_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{config_restored_SUITE,to_tc}},
+ {?eh,cth,{verify_config,post_end_per_testcase,{to_tc,diff_ok}}},
+ {?eh,tc_done,
+ {config_restored_SUITE,to_tc,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{config_restored_SUITE,exit_tc}},
+ {?eh,cth,{verify_config,post_end_per_testcase,{exit_tc,diff_ok}}},
+ {?eh,tc_done,{config_restored_SUITE,exit_tc,
+ {failed,{error,{test_case_failed,"Goodbye!"}}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ [{?eh,tc_start,{config_restored_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_start,{config_restored_SUITE,to_tc}},
+ {?eh,cth,{verify_config,post_end_per_testcase,{to_tc,diff_ok}}},
+ {?eh,tc_done,
+ {config_restored_SUITE,to_tc,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{config_restored_SUITE,exit_tc}},
+ {?eh,cth,{verify_config,post_end_per_testcase,{exit_tc,diff_ok}}},
+ {?eh,tc_done,{config_restored_SUITE,exit_tc,
+ {failed,{error,{test_case_failed,"Goodbye!"}}}}},
+ {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{config_restored_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{config_restored_SUITE,{end_per_group,g1,[]},ok}}],
+ {?eh,tc_start,{config_restored_SUITE,end_per_suite}},
+ {?eh,tc_done,{config_restored_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl
new file mode 100644
index 0000000000..bcbf972a36
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl
@@ -0,0 +1,175 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(config_restored_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,1000}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = [{init_per_suite,?MODULE} | Config],
+ TabPid = spawn(fun() ->
+ ets:new(?MODULE, [named_table, set, public]),
+ receive _ -> ok end
+ end),
+ [{tab,TabPid} | Config1].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ ets:delete(?MODULE),
+ exit(?config(tab, Config), kill),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(GroupName, Config) ->
+ [{init_per_group,GroupName} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_testcase(TC, Config) ->
+ Config1 = [{init_per_testcase,TC} | Config],
+ ets:insert(?MODULE, {config,Config}),
+ %% ct:pal("Config after init_per_testcase(~w) = ~p", [TC,Config1]),
+ Config1.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(TC, Config) ->
+ ct:pal("Config in end_per_testcase(~w) = ~p", [TC,Config]),
+ [{_,MemConfig}] = ets:lookup(?MODULE, config),
+ diff_config(Config, MemConfig, [tc_status]),
+ ?MODULE = proplists:get_value(init_per_suite, Config),
+ TC = proplists:get_value(init_per_testcase, Config),
+ case ?config(tc_group_properties, Config) of
+ undefined ->
+ ok;
+ Props ->
+ GName = proplists:get_value(name, Props),
+ GName = proplists:get_value(init_per_group, Config)
+ end,
+ 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() ->
+ [{g1,[],[to_tc, exit_tc]}].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [to_tc, exit_tc,
+ {group,g1}].
+
+
+to_tc(Config) ->
+ %% ct:pal("Config for to_tc = ~p", [Config]),
+ [{_,MemConfig}] = ets:lookup(?MODULE, config),
+ diff_config(Config, MemConfig, []),
+ ct:sleep(2000).
+
+exit_tc(Config) ->
+ %% ct:pal("Config for exit_tc = ~p", [Config]),
+ [{_,MemConfig}] = ets:lookup(?MODULE, config),
+ diff_config(Config, MemConfig, []),
+ ct:fail("Goodbye!").
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+
+diff_config(Cfg1, Cfg2, DiffKeys) ->
+ diff_config(Cfg1, Cfg2, DiffKeys, []).
+
+diff_config([{K,V} | Cfg1], Cfg2, DiffKeys, RemKeys) ->
+ case proplists:get_value(K, Cfg2) of
+ undefined ->
+ diff_config(Cfg1, Cfg2, proplists:delete(K, DiffKeys), RemKeys);
+ V ->
+ diff_config(Cfg1, proplists:delete(K, Cfg2), DiffKeys, RemKeys);
+ _Other ->
+ case proplists:is_defined(K, DiffKeys) of
+ true ->
+ diff_config(Cfg1, Cfg2, proplists:delete(K, DiffKeys), RemKeys);
+ false ->
+ diff_config(Cfg1, Cfg2, DiffKeys, [K | RemKeys])
+ end
+ end;
+diff_config([], [], [], []) ->
+ ct:pal("Diff ok!", []),
+ ok;
+diff_config([], Cfg2, DiffKeys, RemKeys) ->
+ Result = {diff_failed, {cfg2,Cfg2}, {diffkeys,DiffKeys}, {remkeys,RemKeys}},
+ ct:pal("Diff failed! Result = ~p", [Result]),
+ exit(Result).
+
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_error_SUITE_data/error/test/verify_config.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/verify_config.erl
new file mode 100644
index 0000000000..446dd8bfdf
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/verify_config.erl
@@ -0,0 +1,239 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Example Suite Callback module.
+%%%
+%%% <p>This module gives an example of a common test CTH (Common Test Hook).
+%%% There are many ways to add a CTH to a test run, you can do it either in
+%%% the command line using -ct_hook, in a test spec using
+%%% {ct_hook,M} or in the suite it self by returning ct_hook
+%%% from either suite/0, init_per_suite/1, init_per_group/2 and
+%%% init_per_testcase/2. The scope of the CTH is determined by where is it
+%%% started. If it is started in the command line or test spec then it will
+%%% be stopped at the end of all tests. If it is started in init_per_suite,
+%%% it will be stopped after end_per_suite and so on. See terminate
+%%% documentation for a table describing the scoping machanics.
+%%%
+%%% All of callbacks except init/1 in a CTH are optional.</p>
+
+-module(verify_config).
+
+%% CT Hooks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-type config() :: proplists:proplist().
+-type reason() :: term().
+-type skip_or_fail() :: {skip, reason()} |
+ {auto_skip, reason()} |
+ {fail, reason()} |
+ {'EXIT',reason()}.
+
+-record(state, { id = ?MODULE :: term()}).
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state. It should return an state for this CTH.
+-spec init(Id :: term(), Opts :: proplists:proplist()) ->
+ {ok, State :: #state{}}.
+init(Id, Opts) ->
+ {ok,Opts}.
+
+%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
+%% return the same ID the seconds CTH is ignored. This function should NOT
+%% have any side effects as it might be called multiple times by common test.
+-spec id(Opts :: proplists:proplist()) ->
+ Id :: term().
+id(Opts) ->
+ now().
+
+%% @doc Called before init_per_suite is called. Note that this callback is
+%% only called if the CTH is added before init_per_suite is run (eg. in a test
+%% specification, suite/0 function etc).
+%% You can change the config in the this function.
+-spec pre_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_suite(Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after init_per_suite.
+%% you can change the return value in this function.
+-spec post_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_suite(Suite,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before end_per_suite. The config/state can be changed here,
+%% though it will only affect the *end_per_suite function.
+-spec pre_end_per_suite(Suite :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_suite(Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after end_per_suite. Note that the config cannot be
+%% changed here, only the status of the suite.
+-spec post_end_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_suite(Suite,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each init_per_group.
+%% You can change the config in this function.
+-spec pre_init_per_group(Group :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_group(Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+%% You can change the return value in this function.
+-spec post_init_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_group(Group,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called after each end_per_group. The config/state can be changed here,
+%% though it will only affect the *end_per_group functions.
+-spec pre_end_per_group(Group :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_group(Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each end_per_group. Note that the config cannot be
+%% changed here, only the status of the group.
+-spec post_end_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_group(Group,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each test case.
+%% You can change the config in this function.
+-spec pre_init_per_testcase(TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_testcase(TC,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each test case. Note that the config cannot be
+%% changed here, only the status of the test case.
+-spec post_end_per_testcase(TC :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_testcase(TC,Config,Return,State) ->
+ %% check that config has been restored
+ ct:pal("Config in verify_config:post_end_per_testcase(~w) = ~p",
+ [TC,Config]),
+ [{_,MemConfig}] = ets:lookup(config_restored_SUITE, config),
+ try config_restored_SUITE:diff_config(Config, MemConfig, [tc_status]) of
+ ok ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_testcase,
+ {TC,diff_ok}}})
+ catch
+ _:_ ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_testcase,
+ {TC,diff_failed}}})
+ end,
+ {Return, State}.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
+%% This function should be used for extra cleanup which might be needed.
+%% It is not possible to modify the config or the status of the test run.
+-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
+ init_per_group | end_per_group | atom(),
+ Reason :: term(), State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_fail(TC, Reason, State) ->
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing. Test case can be
+%% end_per_suite, init_per_group, end_per_group and the actual test cases.
+-spec on_tc_skip(TC :: end_per_suite |
+ init_per_group | end_per_group | atom(),
+ {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_skip(TC, Reason, State) ->
+ State.
+
+%% @doc Called when the scope of the CTH is done, this depends on
+%% when the CTH was specified. This translation table describes when this
+%% function is called.
+%%
+%% | Started in | terminate called |
+%% |---------------------|-------------------------|
+%% | command_line | after all tests are run |
+%% | test spec | after all tests are run |
+%% | suite/0 | after SUITE is done |
+%% | init_per_suite/1 | after SUITE is done |
+%% | init_per_group/2 | after group is done |
+%% |-----------------------------------------------|
+%%
+-spec terminate(State :: #state{}) ->
+ term().
+terminate(State) ->
+ 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_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..7408cbe376 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -109,7 +109,7 @@ ct_master_test(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- [{TSFile,ok}] = run_test(ct_master_test, FileName, Config),
+ [{[TSFile],ok}] = run_test(ct_master_test, FileName, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -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),
@@ -196,13 +195,12 @@ get_log_dir(_,PrivDir,NodeName) ->
run_test(_Name, FileName, Config) ->
%% run the test twice, using different html versions
- [{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).
+ [{[FileName],ok}] = ct_test_support:run({ct_master,run,[FileName]},
+ [{ct_master,basic_html,[true]}],
+ Config),
+ [{[FileName],ok}] = ct_test_support:run({ct_master,run,[FileName]},
+ [{ct_master,basic_html,[false]}],
+ Config).
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
@@ -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..df54c4419c 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
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. 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
@@ -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_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl
index 3042a924fe..c89a4cdabe 100644
--- a/lib/common_test/test/ct_netconfc_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE.erl
@@ -43,12 +43,11 @@
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- Config1 = ct_test_support:init_per_suite(Config),
case application:load(crypto) of
- {error,Reason} ->
+ {error,Reason} when Reason=/={already_loaded,crypto} ->
{skip, Reason};
_ ->
- Config1
+ ct_test_support:init_per_suite(Config)
end.
end_per_suite(Config) ->
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
index d337158bce..54526e8e83 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -1044,12 +1044,9 @@ gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
Key = gen_dsa2(LSize, NSize),
{Key, encode_key(Key)}.
-encode_key(Key = #'RSAPrivateKey'{}) ->
- {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
- {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
encode_key(Key = #'DSAPrivateKey'{}) ->
- {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
- {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+ Der = public_key:der_encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', Der, not_encrypted}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
index 2427f37f52..09217f60a3 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl
@@ -1,7 +1,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
@@ -426,7 +426,7 @@ reply(ConnRef,Reply) ->
send(ConnRef, make_msg(Reply)).
from_simple(Simple) ->
- list_to_binary(xmerl:export_simple_element(Simple,xmerl_xml)).
+ unicode_c2b(xmerl:export_simple_element(Simple,xmerl_xml)).
xml(Content) ->
<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
@@ -435,30 +435,30 @@ xml(Content) ->
rpc_reply(Content) when is_binary(Content) ->
MsgId = case erase(msg_id) of
undefined -> <<>>;
- Id -> list_to_binary([" message-id=\"",Id,"\""])
+ Id -> unicode_c2b([" 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)).
+ rpc_reply(unicode_c2b(Content)).
session_id(no_session_id) ->
<<>>;
session_id(SessionId0) ->
- SessionId = list_to_binary(integer_to_list(SessionId0)),
+ SessionId = unicode_c2b(integer_to_list(SessionId0)),
<<"<session-id>",SessionId/binary,"</session-id>\n">>.
capabilities(undefined) ->
- CapsXml = list_to_binary([["<capability>",C,"</capability>\n"]
+ CapsXml = unicode_c2b([["<capability>",C,"</capability>\n"]
|| C <- ?CAPABILITIES]),
<<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
capabilities({base,Vsn}) ->
- CapsXml = list_to_binary([["<capability>",C,"</capability>\n"]
+ CapsXml = unicode_c2b([["<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]),
+ CapsXml = unicode_c2b([["<capability>",C,"</capability>\n"] || C <- Caps]),
<<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>;
capabilities(no_caps) ->
<<>>.
@@ -553,3 +553,8 @@ make_msg(Xml) when is_binary(Xml) ->
xml(Xml);
make_msg(Simple) when is_tuple(Simple) ->
xml(from_simple(Simple)).
+
+%%%-----------------------------------------------------------------
+%%% Convert to unicode binary, since we use UTF-8 encoding in XML
+unicode_c2b(Characters) ->
+ unicode:characters_to_binary(Characters).
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
index 69e98cef48..b86b47f0a2 100644
--- a/lib/common_test/test/ct_surefire_SUITE.erl
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -1,7 +1,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
@@ -32,6 +32,7 @@
-include_lib("common_test/include/ct_event.hrl").
-include_lib("xmerl/include/xmerl.hrl").
+-include_lib("kernel/include/file.hrl").
-define(eh, ct_test_support_eh).
@@ -77,53 +78,52 @@ all() ->
%%%-----------------------------------------------------------------
%%%
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).
+ run(default,[cth_surefire],"junit_report.xml",Config).
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).
+ run(absolute_path,[{cth_surefire,[{path,Path}]}],Path,Config).
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).
+ run(relative_path,[{cth_surefire,[{path,Path}]}],Path,Config).
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).
+ run(url,[{cth_surefire,[{url_base,?url_base},{path,Path}]}],
+ Path,Config).
logdir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir,Config),
- LogDir = filename:join(PrivDir,"specific_logdir"),
- file:make_dir(LogDir),
+ Opts = ct_test_support:get_opts(Config),
+ LogDir =
+ case lists:keyfind(logdir,1,Opts) of
+ {logdir,LD} -> LD;
+ false -> ?config(priv_dir,Config)
+ end,
+ MyLogDir = filename:join(LogDir,"specific_logdir"),
+ ensure_exists_empty(MyLogDir),
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).
+ run(logdir,[{cth_surefire,[{path,Path}]}],Path,Config,[{logdir,MyLogDir}]).
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
-run(Case,CTHs,Config) ->
- run(Case,CTHs,Config,[]).
-run(Case,CTHs,Config,ExtraOpts) ->
+run(Case,CTHs,Report,Config) ->
+ run(Case,CTHs,Report,Config,[]).
+run(Case,CTHs,Report,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).
+ ok = execute(Case, Opts, ERPid, Config),
+ LogDir =
+ case lists:keyfind(logdir,1,Opts) of
+ {logdir,LD} -> LD;
+ false -> ?config(priv_dir,Config)
+ end,
+ Re = filename:join([LogDir,"*",Report]),
+ check_xml(Case,Re).
setup(Test, Config) ->
Opts0 = ct_test_support:get_opts(Config),
@@ -349,3 +349,26 @@ 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}.
+
+ensure_exists_empty(Dir) ->
+ case file:list_dir(Dir) of
+ {error,enoent} ->
+ file:make_dir(Dir);
+ {ok,Files} ->
+ del_files(Dir,Files)
+ end.
+
+del_files(Dir,[F0|Fs] ) ->
+ F = filename:join(Dir,F0),
+ case file:read_file_info(F) of
+ {ok,#file_info{type=directory}} ->
+ {ok,Files} = file:list_dir(F),
+ del_files(F,Files),
+ file:del_dir(F),
+ del_files(Dir,Fs);
+ _ ->
+ file:delete(F),
+ del_files(Dir,Fs)
+ end;
+del_files(_,[]) ->
+ ok.
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 80cca4a1cc..5e109e98e9 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.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
@@ -32,7 +32,7 @@
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]).
@@ -117,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.
%%%-----------------------------------------------------------------
@@ -149,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 ->
@@ -314,8 +312,10 @@ wait_for_ct_stop(Retries, CTNode) ->
undefined ->
true;
Pid ->
+ Info = (catch process_info(Pid)),
test_server:format(0, "Waiting for CT (~p) to finish (~p)...",
[Pid,Retries]),
+ test_server:format(0, "Process info for ~p:~n~p", [Info]),
timer:sleep(5000),
wait_for_ct_stop(Retries-1, CTNode)
end.
@@ -330,12 +330,17 @@ handle_event(EH, Event) ->
start_event_receiver(Config) ->
CTNode = proplists:get_value(ct_node, Config),
- spawn_link(CTNode, fun() -> er() end).
+ Level = proplists:get_value(trace_level, Config),
+ ER = spawn_link(CTNode, fun() -> er() end),
+ test_server:format(Level, "~nEvent receiver ~w started!~n", [ER]),
+ ER.
get_events(_, Config) ->
CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
{event_receiver,CTNode} ! {self(),get_events},
Events = receive {event_receiver,Evs} -> Evs end,
+ test_server:format(Level, "Stopping event receiver!~n", []),
{event_receiver,CTNode} ! stop,
Events.
@@ -364,6 +369,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]),
@@ -612,8 +625,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 ->
@@ -627,23 +643,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});
_ ->
@@ -1002,33 +1007,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,'_'}}},
@@ -1039,10 +1050,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(_, _) ->
@@ -1125,6 +1139,8 @@ reformat([{_EH,#event{name=test_start,data=_}} | Events], EH) ->
[{EH,test_start,{'DEF',{'START_TIME','LOGDIR'}}} | reformat(Events, EH)];
reformat([{_EH,#event{name=test_done,data=_}} | Events], EH) ->
[{EH,test_done,{'DEF','STOP_TIME'}} | reformat(Events, EH)];
+reformat([{_EH,#event{name=tc_logfile,data=_}} | Events], EH) ->
+ reformat(Events, EH);
reformat([{_EH,#event{name=test_stats,data=Data}} | Events], EH) ->
[{EH,test_stats,Data} | reformat(Events, EH)];
%% use this to only print the last test_stats event:
@@ -1259,3 +1275,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 b7e19f25dd..6a4a4acd80 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -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).
%%%-----------------------------------------------------------------
@@ -1204,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,
@@ -1219,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,'_'}},
@@ -1229,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,'_'}},
@@ -1244,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,'_'}}},
@@ -1257,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,'_'}}},
@@ -1270,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,'_'}},
@@ -1282,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,
@@ -1296,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,'_'}},
@@ -1308,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,'_'}},
@@ -1325,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,'_'}}},
@@ -1338,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,'_'}}},
@@ -1352,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
index 9d2dc84ad3..518352e87c 100644
--- a/lib/common_test/test/ct_testspec_2_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_2_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -479,7 +479,7 @@ multiple_specs(_Config) ->
"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],
+ FileResult = ct_testspec:collect_tests_from_file([[SpecFile1,SpecFile2]],
false),
ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]),
@@ -490,7 +490,7 @@ multiple_specs(_Config) ->
[{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]],
LogDirV = get_absdir(filename:join(SpecDir,"../logs")),
- Verify = #testspec{merge_tests = false,
+ Verify = #testspec{merge_tests = true,
spec_dir = SpecDir,
nodes = [{undefined,Node},{n1,Node1},{n2,Node2}],
alias = [{to1,TO1V},{to2,TO2V}],
@@ -500,22 +500,13 @@ multiple_specs(_Config) ->
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"}}]}]},
+ [{x_SUITE,[all]},
+ {y_SUITE,[{g1,all},{g2,all},tc1,tc2]},
+ {z_SUITE,[{all,{skip,"skipped"}}]}]},
{{Node2,TO2V},
- [{y_SUITE,[{tc1,{skip,"skipped"}},
+ [{x_SUITE,[all,{{g1,all},{skip,"skipped"}},
+ {{g2,all},{skip,"skipped"}}]},
+ {y_SUITE,[all,{tc1,{skip,"skipped"}},
{tc2,{skip,"skipped"}}]}]}]},
verify_result(Verify,FileResult,FileResult).
@@ -524,7 +515,7 @@ multiple_specs(_Config) ->
%%%
misc_config_terms(_Config) ->
CfgDir = "../cfgs/to1",
-
+ TODir = "../tests/to1",
Spec =
[{node,x,n1@h1},{node,y,n2@h2},
@@ -554,7 +545,9 @@ misc_config_terms(_Config) ->
{create_priv_dir,[auto_per_tc]},
{create_priv_dir,n1@h1,[manual_per_tc]},
- {create_priv_dir,n2@h2,[auto_per_run]}
+ {create_priv_dir,n2@h2,[auto_per_run]},
+
+ {suites,n1@h1,TODir,[x_SUITE]}
],
{ok,SpecDir} = file:get_cwd(),
@@ -599,7 +592,9 @@ misc_config_terms(_Config) ->
{n2@h2,CSS2}],
create_priv_dir = [{Node,[auto_per_tc]},
{n1@h1,[manual_per_tc]},
- {n2@h2,[auto_per_run]}]
+ {n2@h2,[auto_per_run]}],
+ tests = [{{n1@h1,get_absdir(filename:join(SpecDir,TODir))},
+ [{x_SUITE,[all]}]}]
},
verify_result(Verify,ListResult,FileResult).
@@ -688,10 +683,10 @@ define_names_1(_Config) ->
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
-verify_result(Verify,ListResult,FileResult) ->
+verify_result(VerificationRec,ListResult,FileResult) ->
{_,TSLTuples} = rec2proplist(ListResult),
{_,TSFTuples} = rec2proplist(FileResult),
- {_,VTuples} = rec2proplist(Verify),
+ {_,VTuples} = rec2proplist(VerificationRec),
VResult =
(catch lists:foldl(fun({Tag,Val},{[{Tag,Val}|TSL],[{Tag,Val}|TSF]}) ->
{TSL,TSF};
@@ -720,6 +715,8 @@ read_config(S) ->
rec2proplist(E={error,_What}) ->
exit({invalid_testspec_record,E});
+rec2proplist([{Specs,Rec}]) when is_list(Specs) ->
+ rec2proplist(Rec);
rec2proplist(Rec) ->
[RecName|RecList] = tuple_to_list(Rec),
FieldNames =
diff --git a/lib/common_test/test/ct_testspec_3_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE.erl
new file mode 100644
index 0000000000..6b4b729552
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE.erl
@@ -0,0 +1,745 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_testspec_1_SUITE
+%%%
+%%% Description:
+%%% Test test specifications
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_testspec_3_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Config1 = ct_test_support:init_per_suite(Config),
+ SpecsDir1 = filename:join(DataDir, "specs1"),
+ SpecsDir2 = filename:join(DataDir, "specs2"),
+ [{specs_dir1,SpecsDir1},{specs_dir2,SpecsDir2} | 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_separate,
+ start_join,
+ incl_separate1,
+ incl_separate2,
+ incl_join1,
+ incl_join2,
+ incl_both1,
+ incl_both2,
+ incl_both_and_join1,
+ incl_both_and_join2,
+ rec_incl_separate1,
+ rec_incl_separate2,
+ rec_incl_join1,
+ rec_incl_join2,
+ rec_incl_separate_join1,
+ rec_incl_separate_join2,
+ rec_incl_join_separate1,
+ rec_incl_join_separate2
+ ].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+
+start_separate(Config) ->
+ Specs = [fname(specs_dir1, "flat_spec1", Config),
+ fname(specs_dir2, "flat_spec2", Config)],
+ setup_and_execute(start_separate, Specs, [], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+start_join(Config) ->
+ Specs = [fname(specs_dir1, "flat_spec1", Config),
+ fname(specs_dir2, "flat_spec2", Config)],
+ setup_and_execute(start_join, Specs, [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+incl_separate1(Config) ->
+ Specs = [fname(specs_dir1, "spec_sep1", Config),
+ fname(specs_dir2, "spec_sep2", Config)],
+ setup_and_execute(incl_separate1, Specs, [], Config).
+
+incl_separate2(Config) ->
+ Specs = [fname(specs_dir1, "spec_sep1", Config),
+ fname(specs_dir2, "spec_sep2", Config)],
+ setup_and_execute(incl_separate2, Specs, [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+incl_join1(Config) ->
+ Specs = [fname(specs_dir1, "spec_join1", Config),
+ fname(specs_dir2, "spec_join2", Config)],
+ setup_and_execute(incl_join1, Specs, [], Config).
+
+incl_join2(Config) ->
+ Specs = [fname(specs_dir1, "spec_join1", Config),
+ fname(specs_dir2, "spec_join2", Config)],
+ setup_and_execute(incl_join2, Specs, [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+incl_both1(Config) ->
+ Specs = [fname(specs_dir1, "spec_both1", Config),
+ fname(specs_dir2, "spec_both2", Config)],
+ setup_and_execute(incl_both1, Specs, [], Config).
+
+incl_both2(Config) ->
+ Specs = [fname(specs_dir1, "spec_both1", Config),
+ fname(specs_dir2, "spec_both2", Config)],
+ setup_and_execute(incl_both2, Specs, [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+incl_both_and_join1(Config) ->
+ Specs = [fname(specs_dir1, "spec_both_join1", Config),
+ fname(specs_dir2, "spec_both_join2", Config)],
+ setup_and_execute(incl_both_and_join1, Specs, [], Config).
+
+incl_both_and_join2(Config) ->
+ Specs = [fname(specs_dir1, "spec_both_join1", Config),
+ fname(specs_dir2, "spec_both_join2", Config)],
+ setup_and_execute(incl_both_and_join2, Specs, [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+rec_incl_separate1(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_sep1", Config),
+ fname(specs_dir2, "rec_spec_sep2", Config)],
+ setup_and_execute(rec_incl_separate1, Specs, [], Config).
+
+rec_incl_separate2(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_sep1", Config),
+ fname(specs_dir2, "rec_spec_sep2", Config)],
+ setup_and_execute(rec_incl_separate2, Specs, [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+rec_incl_join1(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_join1", Config),
+ fname(specs_dir2, "rec_spec_join2", Config)],
+ setup_and_execute(rec_incl_join1, Specs, [], Config).
+
+rec_incl_join2(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_join1", Config),
+ fname(specs_dir2, "rec_spec_join2", Config)],
+ setup_and_execute(rec_incl_join2, Specs, [{join_specs,true}], Config).
+
+
+%%%-----------------------------------------------------------------
+%%%
+
+rec_incl_separate_join1(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_sep_join1", Config),
+ fname(specs_dir2, "rec_spec_sep_join2", Config)],
+ setup_and_execute(rec_incl_separate_join1, Specs, [], Config).
+
+rec_incl_separate_join2(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_sep_join1", Config),
+ fname(specs_dir2, "rec_spec_sep_join2", Config)],
+ setup_and_execute(rec_incl_separate_join2, Specs,
+ [{join_specs,true}], Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+rec_incl_join_separate1(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_join_sep1", Config),
+ fname(specs_dir2, "rec_spec_join_sep2", Config)],
+ setup_and_execute(rec_incl_join_separate1, Specs, [], Config).
+
+rec_incl_join_separate2(Config) ->
+ Specs = [fname(specs_dir1, "rec_spec_join_sep1", Config),
+ fname(specs_dir2, "rec_spec_join_sep2", Config)],
+ setup_and_execute(rec_incl_join_separate2, Specs,
+ [{join_specs,true}], Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+fname(Tag, File, Config) ->
+ filename:join(?config(Tag, Config), File).
+
+check_parameter(TCID) ->
+ {ok,{config,TCID}}.
+
+read_config(TCID) ->
+ {ok,[{tcname,list_to_atom(TCID)}]}.
+
+setup_and_execute(TCName, Specs, TestOpts, Config) ->
+
+ TestID = {userconfig,{?MODULE,atom_to_list(TCName)}},
+ TestTerms = [TestID,{spec,Specs},{label,TCName}] ++ TestOpts,
+
+ {Opts,ERPid} = setup(TestTerms, Config),
+
+ case ct_test_support:run(Opts, Config) of
+ ok ->
+ ok;
+ Error ->
+ ct:pal("Error executing with opts: ~p", [Opts]),
+ exit(Error)
+ end,
+
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(TCName,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(TCName),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+setup(Test, Config) when is_tuple(Test) ->
+ setup([Test], Config);
+setup(Tests, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ Tests ++ [{event_handler,{?eh,EvHArgs}}],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+%reformat(Events, _EH) ->
+% Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(start_separate) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(start_join) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_separate1) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_separate2) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_join1) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_join2) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_both1) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_both2) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,20}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,ok_tc}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{4,8,{4,4}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{3,2,15}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t12_SUITE,init_per_suite}},
+ {?eh,tc_done,{t12_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t12_SUITE,end_per_suite}},
+ {?eh,tc_done,{t12_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t22_SUITE,init_per_suite}},
+ {?eh,tc_done,{t22_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{3,6,{3,3}}},
+ {?eh,tc_start,{t22_SUITE,end_per_suite}},
+ {?eh,tc_done,{t22_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]},
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,10}},
+ {?eh,tc_start,{t11_SUITE,init_per_suite}},
+ {?eh,tc_done,{t11_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{1,2,{1,1}}},
+ {?eh,tc_start,{t11_SUITE,end_per_suite}},
+ {?eh,tc_done,{t11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{t21_SUITE,init_per_suite}},
+ {?eh,tc_done,{t21_SUITE,init_per_suite,ok}},
+ {?eh,test_stats,{2,4,{2,2}}},
+ {?eh,tc_start,{t21_SUITE,end_per_suite}},
+ {?eh,tc_done,{t21_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(incl_both_and_join1) -> [];
+test_events(incl_both_and_join2) -> [];
+test_events(rec_incl_separate1) -> [];
+test_events(rec_incl_separate2) -> [];
+test_events(rec_incl_join1) -> [];
+test_events(rec_incl_join2) -> [];
+test_events(rec_incl_separate_join1) -> [];
+test_events(rec_incl_separate_join2) -> [];
+test_events(rec_incl_join_separate1) -> [];
+test_events(rec_incl_join_separate2) -> [];
+
+test_events(_) ->
+ [].
+
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg11 b/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg11
new file mode 100644
index 0000000000..bc672568da
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg11
@@ -0,0 +1 @@
+{file, cfg11}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg12 b/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg12
new file mode 100644
index 0000000000..30f2cf6857
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg12
@@ -0,0 +1 @@
+{file, cfg12}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg13 b/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg13
new file mode 100644
index 0000000000..1860ec78e5
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/config1/cfg13
@@ -0,0 +1 @@
+{file, cfg13}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/config2/cfg21 b/lib/common_test/test/ct_testspec_3_SUITE_data/config2/cfg21
new file mode 100644
index 0000000000..b18d35443e
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/config2/cfg21
@@ -0,0 +1 @@
+{file, cfg21}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/flat_spec1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/flat_spec1
new file mode 100644
index 0000000000..eff87222ea
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/flat_spec1
@@ -0,0 +1,4 @@
+{config, "../config1/cfg11"}.
+{suites, "../tests1", t11_SUITE}.
+{suites, "../tests1", t11_SUITE}.
+{suites, "../tests2", t21_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join1
new file mode 100644
index 0000000000..a3387f48a3
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join1
@@ -0,0 +1,2 @@
+{specs,join,"spec_join1"}.
+{specs,join,"../specs2/spec_join2"}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join_sep1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join_sep1
new file mode 100644
index 0000000000..fe127eb4b9
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_join_sep1
@@ -0,0 +1,5 @@
+{specs,join,"spec_sep1"}.
+{specs,join,"../specs2/spec_sep2"}.
+
+{config, "../config1/cfg13"}.
+{suites, "../tests2", t23_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep1
new file mode 100644
index 0000000000..c778aa68a6
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep1
@@ -0,0 +1,2 @@
+{specs,separate,"spec_sep1"}.
+{specs,separate,"../specs2/spec_sep2"}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep_join1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep_join1
new file mode 100644
index 0000000000..7cb5a05fff
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/rec_spec_sep_join1
@@ -0,0 +1,2 @@
+{specs,separate,"spec_join1"}.
+{specs,separate,"../specs2/spec_join2"}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both1
new file mode 100644
index 0000000000..46111614dc
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both1
@@ -0,0 +1,2 @@
+{specs, join, "../specs1/flat_spec1"}.
+{specs, separate, "../specs2/flat_spec2"}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both_join1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both_join1
new file mode 100644
index 0000000000..f52b3ed030
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_both_join1
@@ -0,0 +1,6 @@
+{specs, join, "../specs1/flat_spec1"}.
+{specs, separate, "../specs2/flat_spec2"}.
+{merge_tests,false}.
+{config, "../config1/cfg12"}.
+{suites, "../tests1", t11_SUITE}.
+{suites, "../tests2", t21_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_join1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_join1
new file mode 100644
index 0000000000..baaaf35be4
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_join1
@@ -0,0 +1 @@
+{specs, join, ["../specs2/flat_spec2", "flat_spec1"]}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_sep1 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_sep1
new file mode 100644
index 0000000000..89456c35e0
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs1/spec_sep1
@@ -0,0 +1,3 @@
+{specs, separate, "../specs2/flat_spec2"}.
+{specs, separate, "flat_spec1"}.
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/flat_spec2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/flat_spec2
new file mode 100644
index 0000000000..758d1e2514
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/flat_spec2
@@ -0,0 +1,5 @@
+{merge_tests, false}.
+{config, "../config2/cfg21"}.
+{suites, "../tests1", t12_SUITE}.
+{suites, "../tests1", t12_SUITE}.
+{suites, "../tests2", t22_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join2
new file mode 100644
index 0000000000..19d3a3d8e2
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join2
@@ -0,0 +1,5 @@
+{specs,join,"spec_join2"}.
+{specs,join,"../specs1/spec_join1"}.
+
+{config, "../config1/cfg13"}.
+{suites, "../tests2", t23_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join_sep2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join_sep2
new file mode 100644
index 0000000000..930e68c847
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_join_sep2
@@ -0,0 +1,5 @@
+{specs,join,"spec_sep2"}.
+{specs,join,"../specs1/spec_sep1"}.
+
+{config, "../config1/cfg13"}.
+{suites, "../tests2", t23_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep2
new file mode 100644
index 0000000000..5026f329a7
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep2
@@ -0,0 +1,5 @@
+{specs,separate,"spec_sep2"}.
+{specs,separate,"../specs1/spec_sep1"}.
+
+{config, "../config1/cfg13"}.
+{suites, "../tests2", t23_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep_join2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep_join2
new file mode 100644
index 0000000000..17057088b4
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/rec_spec_sep_join2
@@ -0,0 +1,5 @@
+{specs,separate,"spec_join2"}.
+{specs,separate,"../specs1/spec_join1"}.
+
+{config, "../config1/cfg13"}.
+{suites, "../tests2", t23_SUITE}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both2
new file mode 100644
index 0000000000..4c83115d23
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both2
@@ -0,0 +1,4 @@
+{specs, separate, "../specs1/flat_spec1"}.
+{specs, join, "../specs2/flat_spec2"}.
+{config, "../config1/cfg12"}.
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both_join2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both_join2
new file mode 100644
index 0000000000..ad81bfb4cc
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_both_join2
@@ -0,0 +1,9 @@
+{merge_tests,true}.
+{config, "../config1/cfg12"}.
+{suites, "../tests1", t11_SUITE}.
+{suites, "../tests2", t21_SUITE}.
+{suites, "../tests1", t12_SUITE}.
+{suites, "../tests2", t22_SUITE}.
+
+{specs, separate, "../specs1/flat_spec1"}.
+{specs, join, "../specs2/flat_spec2"}.
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_join2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_join2
new file mode 100644
index 0000000000..d652dbd78f
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_join2
@@ -0,0 +1,5 @@
+{specs, join, ["../specs1/flat_spec1"]}.
+{specs, join, ["flat_spec2"]}.
+{config, "../config1/cfg12"}.
+{suites, "../tests2", t22_SUITE}.
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_sep2 b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_sep2
new file mode 100644
index 0000000000..8d37f508b8
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/specs2/spec_sep2
@@ -0,0 +1,5 @@
+{specs, separate, "../specs1/flat_spec1"}.
+{specs, separate, "flat_spec2"}.
+{config, "../config1/cfg12"}.
+{suites, "../tests2", t22_SUITE}.
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t11_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t11_SUITE.erl
new file mode 100644
index 0000000000..b8216c3596
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t11_SUITE.erl
@@ -0,0 +1,175 @@
+%%
+%% %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(t11_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{require,file},
+ {require,tcname},
+ {timetrap,{seconds,1}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ %% verify that expected config file can be read
+ case {ct:get_config(tcname),ct:get_config(file,undefined,[all])} of
+ {start_separate,[cfg11]} -> ok;
+ {start_join,[cfg11,cfg21]} -> ok;
+ {incl_separate1,[cfg11]} -> ok;
+ {incl_separate2,[cfg11]} -> ok;
+ {incl_join1,[cfg21,cfg11]} -> ok;
+ {incl_join1,[cfg12,cfg11,cfg21]} -> ok;
+ {incl_join2,[cfg21,cfg11,cfg12]} -> ok;
+ {incl_both1,[cfg11]} -> ok;
+ {incl_both2,[cfg11,cfg12,cfg21]} -> ok;
+ {incl_both2,[cfg11]} -> ok;
+ _ -> ok
+
+ end,
+ 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(autoskip_tc, Config) ->
+ exit(kaboom),
+ Config;
+
+init_per_testcase(userskip_tc, Config) ->
+ {skip,"user skipped"};
+
+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_tc, exit_tc, to_tc, autoskip_tc, userskip_tc].
+
+%%--------------------------------------------------------------------
+%% @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
+%%--------------------------------------------------------------------
+ok_tc(_) ->
+ ok.
+
+exit_tc(_) ->
+ exit(kaboom),
+ ok.
+
+to_tc(_) ->
+ ct:timetrap(1),
+ ct:sleep(100),
+ ok.
+
+autoskip_tc(_) ->
+ ok.
+
+userskip_tc(_) ->
+ ok.
+
+
+
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t12_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t12_SUITE.erl
new file mode 100644
index 0000000000..7c51aca246
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/tests1/t12_SUITE.erl
@@ -0,0 +1,175 @@
+%%
+%% %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(t12_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{require,file},
+ {require,tcname},
+ {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) ->
+ %% verify that expected config file can be read
+ case {ct:get_config(tcname),ct:get_config(file,undefined,[all])} of
+ {start_separate,[cfg21]} -> ok;
+ {start_join,[cfg11,cfg21]} -> ok;
+ {incl_separate1,[cfg21]} -> ok;
+ {incl_separate2,[cfg21]} -> ok;
+ {incl_join1,[cfg21,cfg11]} -> ok;
+ {incl_join1,[cfg12,cfg11,cfg21]} -> ok;
+ {incl_join2,[cfg21,cfg11,cfg12]} -> ok;
+ {incl_both1,[cfg21]} -> ok;
+ {incl_both1,[cfg12,cfg21]} -> ok;
+ {incl_both2,[cfg11,cfg12,cfg21]} -> ok;
+ {incl_both2,[cfg21]} -> ok;
+ _ -> ok
+ end,
+ 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(autoskip_tc, Config) ->
+ exit(kaboom),
+ Config;
+
+init_per_testcase(userskip_tc, Config) ->
+ {skip,"user skipped"};
+
+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_tc, exit_tc, to_tc, autoskip_tc, userskip_tc].
+
+%%--------------------------------------------------------------------
+%% @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
+%%--------------------------------------------------------------------
+ok_tc(_) ->
+ ok.
+
+exit_tc(_) ->
+ exit(kaboom),
+ ok.
+
+to_tc(_) ->
+ ct:timetrap(1),
+ ct:sleep(100),
+ ok.
+
+autoskip_tc(_) ->
+ ok.
+
+userskip_tc(_) ->
+ ok.
+
+
+
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t21_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t21_SUITE.erl
new file mode 100644
index 0000000000..36c1b4279b
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t21_SUITE.erl
@@ -0,0 +1,174 @@
+%%
+%% %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(t21_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{require,file},
+ {require,tcname},
+ {timetrap,{seconds,1}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ %% verify that expected config file can be read
+ case {ct:get_config(tcname),ct:get_config(file,undefined,[all])} of
+ {start_separate,[cfg11]} -> ok;
+ {start_join,[cfg11,cfg21]} -> ok;
+ {incl_separate1,[cfg11]} -> ok;
+ {incl_separate2,[cfg11]} -> ok;
+ {incl_join1,[cfg21,cfg11]} -> ok;
+ {incl_join1,[cfg12,cfg11,cfg21]} -> ok;
+ {incl_join2,[cfg21,cfg11,cfg12]} -> ok;
+ {incl_both1,[cfg11]} -> ok;
+ {incl_both2,[cfg11,cfg12,cfg21]} -> ok;
+ {incl_both2,[cfg11]} -> ok;
+ _ -> ok
+ end,
+ 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(autoskip_tc, Config) ->
+ exit(kaboom),
+ Config;
+
+init_per_testcase(userskip_tc, Config) ->
+ {skip,"user skipped"};
+
+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_tc, exit_tc, to_tc, autoskip_tc, userskip_tc].
+
+%%--------------------------------------------------------------------
+%% @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
+%%--------------------------------------------------------------------
+ok_tc(_) ->
+ ok.
+
+exit_tc(_) ->
+ exit(kaboom),
+ ok.
+
+to_tc(_) ->
+ ct:timetrap(1),
+ ct:sleep(100),
+ ok.
+
+autoskip_tc(_) ->
+ ok.
+
+userskip_tc(_) ->
+ ok.
+
+
+
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t22_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t22_SUITE.erl
new file mode 100644
index 0000000000..3f6336c7e2
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t22_SUITE.erl
@@ -0,0 +1,177 @@
+%%
+%% %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(t22_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{require,file},
+ {require,tcname},
+ {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) ->
+ %% verify that expected config file can be read
+ case {ct:get_config(tcname),ct:get_config(file,undefined,[all])} of
+ {start_separate,[cfg21]} -> ok;
+ {start_join,[cfg11,cfg21]} -> ok;
+ {incl_separate1,[cfg12]} -> ok;
+ {incl_separate1,[cfg21]} -> ok;
+ {incl_separate2,[cfg12]} -> ok;
+ {incl_separate2,[cfg21]} -> ok;
+ {incl_join1,[cfg21,cfg11]} -> ok;
+ {incl_join1,[cfg12,cfg11,cfg21]} -> ok;
+ {incl_join2,[cfg21,cfg11,cfg12]} -> ok;
+ {incl_both1,[cfg21]} -> ok;
+ {incl_both1,[cfg12,cfg21]} -> ok;
+ {incl_both2,[cfg11,cfg12,cfg21]} -> ok;
+ {incl_both2,[cfg21]} -> ok;
+ _ -> ok
+ end,
+ 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(autoskip_tc, Config) ->
+ exit(kaboom),
+ Config;
+
+init_per_testcase(userskip_tc, Config) ->
+ {skip,"user skipped"};
+
+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_tc, exit_tc, to_tc, autoskip_tc, userskip_tc].
+
+%%--------------------------------------------------------------------
+%% @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
+%%--------------------------------------------------------------------
+ok_tc(_) ->
+ ok.
+
+exit_tc(_) ->
+ exit(kaboom),
+ ok.
+
+to_tc(_) ->
+ ct:timetrap(1),
+ ct:sleep(100),
+ ok.
+
+autoskip_tc(_) ->
+ ok.
+
+userskip_tc(_) ->
+ ok.
+
+
+
+
diff --git a/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t23_SUITE.erl b/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t23_SUITE.erl
new file mode 100644
index 0000000000..d836ab57c1
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_3_SUITE_data/tests2/t23_SUITE.erl
@@ -0,0 +1,158 @@
+%%
+%% %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(t23_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(autoskip_tc, Config) ->
+ exit(kaboom),
+ Config;
+
+init_per_testcase(userskip_tc, Config) ->
+ {skip,"user skipped"};
+
+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_tc, exit_tc, to_tc, autoskip_tc, userskip_tc].
+
+%%--------------------------------------------------------------------
+%% @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
+%%--------------------------------------------------------------------
+ok_tc(_) ->
+ ok.
+
+exit_tc(_) ->
+ exit(kaboom),
+ ok.
+
+to_tc(_) ->
+ ct:timetrap(1),
+ ct:sleep(100),
+ ok.
+
+autoskip_tc(_) ->
+ ok.
+
+userskip_tc(_) ->
+ ok.
+
+
+
+
diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl
index 349319de94..32488b1db9 100644
--- a/lib/common_test/test/ct_verbosity_SUITE.erl
+++ b/lib/common_test/test/ct_verbosity_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -44,8 +44,11 @@
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- Config1 = ct_test_support:init_per_suite(Config),
- Config1.
+ DataDir = ?config(data_dir, Config),
+ EvH = filename:join(DataDir,"simple_evh.erl"),
+ ct:pal("Compiling ~s: ~p", [EvH,compile:file(EvH,[{outdir,DataDir},
+ debug_info])]),
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]} | Config]).
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
@@ -56,7 +59,8 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{timetrap,{minutes,2}},
+ {ct_hooks,[ts_install_cth]}].
all() ->
[
@@ -67,7 +71,8 @@ all() ->
change_default,
combine_categories,
testspec_only,
- merge_with_testspec
+ merge_with_testspec,
+ possible_deadlock
].
%%--------------------------------------------------------------------
@@ -173,6 +178,17 @@ merge_with_testspec(Config) ->
ok = execute(TC, Opts, ERPid, Config).
%%%-----------------------------------------------------------------
+%%%
+possible_deadlock(Config) ->
+ TC = possible_deadlock,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {event_handler,[simple_evh]}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -180,7 +196,14 @@ 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],
+ Opts =
+ case proplists:get_value(event_handler, Test) of
+ undefined ->
+ Opts0 ++ [{event_handler,{?eh,EvHArgs}} | Test];
+ EvHs ->
+ Opts0 ++ [{event_handler,{[?eh|EvHs],EvHArgs}} |
+ proplists:delete(event_handler, Test)]
+ end,
ERPid = ct_test_support:start_event_receiver(Config),
{Opts,ERPid}.
diff --git a/lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl b/lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl
new file mode 100644
index 0000000000..3e744f2596
--- /dev/null
+++ b/lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl
@@ -0,0 +1,171 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-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%
+%%
+
+%%% @doc Common Test Framework Event Handler
+%%%
+%%% <p>This module implements an event handler that CT uses to
+%%% handle status and progress notifications during test runs.
+%%% The notifications are handled locally (per node) and passed
+%%% on to ct_master when CT runs in distributed mode. This
+%%% module may be used as a template for other event handlers
+%%% that can be plugged in to handle local logging and reporting.</p>
+-module(simple_evh).
+
+-behaviour(gen_event).
+
+%% gen_event callbacks
+-export([init/1, handle_event/2, handle_call/2,
+ handle_info/2, terminate/2, code_change/3]).
+
+-include_lib("common_test/include/ct_event.hrl").
+-include_lib("common_test/src/ct_util.hrl").
+
+%%====================================================================
+%% gen_event callbacks
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State}
+%% Description: Whenever a new event handler is added to an event manager,
+%% this function is called to initialize the event handler.
+%%--------------------------------------------------------------------
+init(_) ->
+ io:format("Event handler ~w started!~n", [?MODULE]),
+ {ok,[]}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_event(Event, State) -> {ok, State} |
+%% {swap_handler, Args1, State1, Mod2, Args2} |
+%% remove_handler
+%% Description:Whenever an event manager receives an event sent using
+%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
+%% each installed event handler to handle the event.
+%%--------------------------------------------------------------------
+handle_event(Event = #event{name = test_stats},State) ->
+ %% this could cause a deadlock
+ ct:pal("~p: ~p~n", [Event#event.name,Event#event.data]),
+ {ok,State};
+handle_event(_Event,State) ->
+ {ok,State}.
+
+%%============================== EVENTS ==============================
+%%
+%% Name = test_start
+%% Data = {StartTime,LogDir}
+%%
+%% Name = start_info
+%% Data = {Tests,Suites,Cases}
+%% Tests = Suites = Cases = integer()
+%%
+%% Name = test_done
+%% Data = EndTime
+%%
+%% Name = start_make
+%% Data = Dir
+%%
+%% Name = finished_make
+%% Data = Dir
+%%
+%% Name = tc_start
+%% Data = {Suite,CaseOrGroup}
+%% CaseOrGroup = atom() | {Conf,GroupName,GroupProperties}
+%% Conf = init_per_group | end_per_group
+%% GroupName = atom()
+%% GroupProperties = list()
+%%
+%% Name = tc_done
+%% Data = {Suite,CaseOrGroup,Result}
+%% CaseOrGroup = atom() | {Conf,GroupName,GroupProperties}
+%% Conf = init_per_group | end_per_group
+%% GroupName = atom()
+%% GroupProperties = list()
+%% Result = ok | {skipped,Reason} | {failed,Reason}
+%%
+%% Name = tc_user_skip
+%% Data = {Suite,Case,Comment}
+%% Comment = string()
+%%
+%% Name = tc_auto_skip
+%% Data = {Suite,Case,Comment}
+%% Comment = string()
+%%
+%% Name = test_stats
+%% Data = {Ok,Failed,Skipped}
+%% Ok = Failed = integer()
+%% Skipped = {UserSkipped,AutoSkipped}
+%% UserSkipped = AutoSkipped = integer()
+%%
+%% Name = start_logging
+%% Data = CtRunDir
+%%
+%% Name = stop_logging
+%% Data = []
+%%
+%% Name = start_write_file
+%% Data = FullNameFile
+%%
+%% Name = finished_write_file
+%% Data = FullNameFile
+%%
+%% Name =
+%% Data =
+%%
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_call(Request, State) -> {ok, Reply, State} |
+%% {swap_handler, Reply, Args1, State1,
+%% Mod2, Args2} |
+%% {remove_handler, Reply}
+%% Description: Whenever an event manager receives a request sent using
+%% gen_event:call/3,4, this function is called for the specified event
+%% handler to handle the request.
+%%--------------------------------------------------------------------
+handle_call(_Req, State) ->
+ Reply = ok,
+ {ok, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_info(Info, State) -> {ok, State} |
+%% {swap_handler, Args1, State1, Mod2, Args2} |
+%% remove_handler
+%% Description: This function is called for each installed event handler when
+%% an event manager receives any other message than an event or a synchronous
+%% request (or a system message).
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description:Whenever an event handler is deleted from an event manager,
+%% this function is called. It should be the opposite of Module:init/1 and
+%% do any necessary cleaning up.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index f9bb22867e..87d762b697 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.6.3
+COMMON_TEST_VSN = 1.7.1
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 27d750f929..ddaae2655d 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -816,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 6d51074d4a..94fea84557 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,104 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 4.9.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The compiler would crash attempting to compile
+ expressions such as "element(2, not_tuple)".</p>
+ <p>
+ Own Id: OTP-10794</p>
+ </item>
+ <item>
+ <p>
+ Forbid multiple values in Core Erlang sequence arguments.
+ Thanks to Jos� Valim and Anthony Ramine.</p>
+ <p>
+ Own Id: OTP-10818</p>
+ </item>
+ <item>
+ <p>
+ An unsafe optimization would cause the compiler to crash
+ with an internal error for certain complex code
+ sequences.</p>
+ <p>
+ Own Id: OTP-10825 Aux Id: seq12247 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Integers in expression that will give a floating point
+ result (such as "<c>X / 2</c>" will now be converted to
+ floating point at compile-time. (Suggested by Richard
+ O'Keefe.)</p>
+ <p>Identical floating points constans in a module will
+ now be coalesced to one entry in the constant pool.</p>
+ <p>
+ Own Id: OTP-10788</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 4.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The compiler optimizations have been polished, so that
+ the code quality will be slightly better in some cases.</p>
+ <p>
+ Own Id: OTP-10193</p>
+ </item>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Fix some wrong warnings triggered by the option
+ inline_list_funcs. Thanks to Anthony Ramine.</p>
+ <p>
+ Own Id: OTP-10690</p>
+ </item>
+ <item>
+ <p>
+ Forbid local fun variables in Core Erlang guards. Thanks
+ to Anthony Ramine.</p>
+ <p>
+ Own Id: OTP-10706</p>
+ </item>
+ <item>
+ <p>
+ Binary syntax matches could cause an internal consistency
+ error in in the compiler. (Thanks to Viktor Sovietov for
+ reporting this bug.)</p>
+ <p>
+ Own Id: OTP-10724</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 4.8.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 958d3501c7..c6d09d85eb 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -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
@@ -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 \
@@ -121,7 +122,7 @@ ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
ERL_COMPILE_FLAGS += +inline +warn_unused_import \
- +warnings_as_errors \
+ -Werror \
-I../../stdlib/include -I$(EGEN) -W
# ----------------------------------------------------
@@ -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_asm.erl b/lib/compiler/src/beam_asm.erl
index a7c8508321..112b087f3c 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.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
@@ -387,7 +387,7 @@ encode_arg({list, List}, Dict0) ->
{L, Dict} = encode_list(List, Dict0, []),
{[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
encode_arg({float, Float}, Dict) when is_float(Float) ->
- {[encode(?tag_z, 0),<<Float:64/float>>], Dict};
+ encode_arg({literal,Float}, Dict);
encode_arg({fr,Fr}, Dict) ->
{[encode(?tag_z, 2),encode(?tag_u, Fr)], Dict};
encode_arg({field_flags,Flags0}, Dict) ->
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index cd568097fa..cf5244e1ce 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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..cf5455dfde 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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..fdfcb08125 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.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
@@ -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..e208ffec1f 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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..b15adfa889 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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_dict.erl b/lib/compiler/src/beam_dict.erl
index 531968b3c8..212b9fb03a 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.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
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 62bdc74cc8..1a8bbcee22 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -512,7 +512,12 @@ decode_z_tagged(Tag,B,Bs,Literals) when (B band 16#08) =:= 0 ->
decode_alloc_list(Bs, Literals);
4 -> % literal
{{u,LitIndex},RestBs} = decode_arg(Bs),
- {{literal,gb_trees:get(LitIndex, Literals)},RestBs};
+ case gb_trees:get(LitIndex, Literals) of
+ Float when is_float(Float) ->
+ {{float,Float},RestBs};
+ Literal ->
+ {{literal,Literal},RestBs}
+ end;
_ ->
?exit({decode_z_tagged,{invalid_extended_tag,N}})
end;
diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl
index fb1a43cd9e..e5ec1bd904 100644
--- a/lib/compiler/src/beam_except.erl
+++ b/lib/compiler/src/beam_except.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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,13 +49,14 @@ function({function,Name,Arity,CLabel,Is0}) ->
-record(st,
{lbl, %func_info label
- loc %location for func_info
+ loc, %location for func_info
+ arity %arity for function
}).
function_1(Is0) ->
case Is0 of
- [{label,Lbl},{line,Loc}|_] ->
- St = #st{lbl=Lbl,loc=Loc},
+ [{label,Lbl},{line,Loc},{func_info,_,_,Arity}|_] ->
+ St = #st{lbl=Lbl,loc=Loc,arity=Arity},
translate(Is0, St, []);
[{label,_}|_] ->
%% No line numbers. The source must be a .S file.
@@ -65,10 +66,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) ->
@@ -78,14 +75,14 @@ translate_1(Ar, I, Is, St, [{line,_}=Line|Acc1]=Acc0) ->
case dig_out(Ar, Acc1) of
no ->
translate(Is, St, [I|Acc0]);
- {yes,function_clause,Acc2} ->
+ {yes,{function_clause,Arity},Acc2} ->
case {Line,St} of
- {{line,Loc},#st{lbl=Fi,loc=Loc}} ->
+ {{line,Loc},#st{lbl=Fi,loc=Loc,arity=Arity}} ->
Instr = {jump,{f,Fi}},
translate(Is, St, [Instr|Acc2]);
{_,_} ->
%% This must be "error(function_clause, Args)" in
- %% the Erlang source code. Don't translate.
+ %% the Erlang source code or a fun. Don't translate.
translate(Is, St, [I|Acc0])
end;
{yes,Instr,Acc2} ->
@@ -139,11 +136,16 @@ fix_block(Is0, Words) ->
[{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is].
dig_out_block_fc([{set,[],[],{alloc,Live,_}}|Bl]) ->
- dig_out_fc(Bl, Live-1, nil);
+ case dig_out_fc(Bl, Live-1, nil) of
+ no ->
+ no;
+ yes ->
+ {yes,{function_clause,Live}}
+ end;
dig_out_block_fc(_) -> no.
dig_out_fc([{set,[Dst],[{x,Reg},Dst0],put_list}|Is], Reg, Dst0) ->
dig_out_fc(Is, Reg-1, Dst);
dig_out_fc([{set,[{x,0}],[{atom,function_clause}],move}], -1, {x,1}) ->
- {yes,function_clause};
+ yes;
dig_out_fc(_, _, _) -> no.
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 6c7cb849aa..25428c0c10 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. 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
@@ -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..b29a3565e4 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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..97a8c7ba70 100644
--- a/lib/compiler/src/beam_peep.erl
+++ b/lib/compiler/src/beam_peep.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
@@ -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..3dd5ed182e 100644
--- a/lib/compiler/src/beam_receive.erl
+++ b/lib/compiler/src/beam_receive.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
@@ -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) ->
@@ -220,7 +249,7 @@ opt_ref_used(Is, RefReg, Fail, D) ->
Done = gb_sets:singleton(Fail),
Regs = regs_init_x0(),
try
- opt_ref_used_1(Is, RefReg, D, Done, Regs),
+ _ = opt_ref_used_1(Is, RefReg, D, Done, Regs),
true
catch
throw:not_used ->
@@ -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..fad9c42584 100644
--- a/lib/compiler/src/beam_trim.erl
+++ b/lib/compiler/src/beam_trim.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
@@ -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 7392f99fb6..3b51216a6c 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.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
@@ -142,9 +142,11 @@ simplify_float(Is0, Ts0) ->
throw:not_possible -> not_possible
end.
-simplify_float_1([{set,[D0],[A],{alloc,_,{gc_bif,'-',{f,0}}}}=I|Is]=Is0, Ts0, Rs0, Acc0) ->
- case tdb_find(A, Ts0) of
+simplify_float_1([{set,[D0],[A0],{alloc,_,{gc_bif,'-',{f,0}}}}=I|Is]=Is0,
+ Ts0, Rs0, Acc0) ->
+ case tdb_find(A0, Ts0) of
float ->
+ A = coerce_to_float(A0),
{Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
{D,Rs} = find_dest(D0, Rs1),
Areg = fetch_reg(A, Rs),
@@ -156,13 +158,16 @@ simplify_float_1([{set,[D0],[A],{alloc,_,{gc_bif,'-',{f,0}}}}=I|Is]=Is0, Ts0, Rs
{Rs,Acc} = flush(Rs0, Is0, Acc0),
simplify_float_1(Is, Ts, Rs, [I|checkerror(Acc)])
end;
-simplify_float_1([{set,[D0],[A,B],{alloc,_,{gc_bif,Op0,{f,0}}}}=I|Is]=Is0, Ts0, Rs0, Acc0) ->
- case float_op(Op0, A, B, Ts0) of
+simplify_float_1([{set,[D0],[A0,B0],{alloc,_,{gc_bif,Op0,{f,0}}}}=I|Is]=Is0,
+ Ts0, Rs0, Acc0) ->
+ case float_op(Op0, A0, B0, Ts0) of
no ->
Ts = update(I, Ts0),
{Rs,Acc} = flush(Rs0, Is0, Acc0),
simplify_float_1(Is, Ts, Rs, [I|checkerror(Acc)]);
{yes,Op} ->
+ A = coerce_to_float(A0),
+ B = coerce_to_float(B0),
{Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
{Rs2,Acc2} = load_reg(B, Ts0, Rs1, Acc1),
{D,Rs} = find_dest(D0, Rs2),
@@ -187,6 +192,16 @@ simplify_float_1([], Ts, Rs, Acc0) ->
Is = opt_fmoves(Is0, []),
{Is,Ts}.
+coerce_to_float({integer,I}=Int) ->
+ try float(I) of
+ F ->
+ {float,F}
+ catch _:_ ->
+ %% Let the overflow happen at run-time.
+ Int
+ end;
+coerce_to_float(Other) -> Other.
+
opt_fmoves([{set,[{x,_}=R],[{fr,_}]=Src,fmove}=I1,
{set,[_]=Dst,[{x,_}=R],move}=I2|Is], Acc) ->
case beam_utils:is_killed_block(R, Is) of
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 194f089ba1..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),
- 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(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),
+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_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([{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_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),
@@ -747,30 +696,16 @@ live_opt([{try_case_end,Src}=I|Is], _, D, 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),
@@ -780,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]);
@@ -799,8 +732,6 @@ 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) ->
@@ -827,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 29758b8fb4..eb72290306 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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 @@ files([F|Fs]) ->
case file(F) of
ok -> ok;
{error,Es} ->
- io:format("~p:~n~s~n", [F,format_error(Es)])
+ io:format("~tp:~n~ts~n", [F,format_error(Es)])
end,
files(Fs);
files([]) -> ok.
@@ -649,7 +649,8 @@ valfun_4(send, Vst) ->
call(send, 2, Vst);
valfun_4({set_tuple_element,Src,Tuple,I}, Vst) ->
assert_term(Src, Vst),
- assert_type({tuple_element,I+1}, Tuple, Vst);
+ assert_type({tuple_element,I+1}, Tuple, Vst),
+ Vst;
%% Match instructions.
valfun_4({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
assert_term(Src, Vst),
@@ -1044,7 +1045,7 @@ float_op(Src, Dst, Vst0) ->
assert_fls(Fls, Vst) ->
case get_fls(Vst) of
- Fls -> Vst;
+ Fls -> ok;
OtherFls -> error({bad_floating_point_state,OtherFls})
end.
@@ -1120,7 +1121,7 @@ bsm_match_state(Slots) ->
{match_context,0,Slots}.
bsm_validate_context(Reg, Vst) ->
- bsm_get_context(Reg, Vst),
+ _ = bsm_get_context(Reg, Vst),
ok.
bsm_get_context({x,X}=Reg, #vst{current=#st{x=Xs}}=_Vst) when is_integer(X) ->
@@ -1133,7 +1134,7 @@ bsm_get_context(Reg, _) -> error({bad_source,Reg}).
bsm_save(Reg, {atom,start}, Vst) ->
%% Save point refering to where the match started.
%% It is always valid. But don't forget to validate the context register.
- bsm_get_context(Reg, Vst),
+ bsm_validate_context(Reg, Vst),
Vst;
bsm_save(Reg, SavePoint, Vst) ->
case bsm_get_context(Reg, Vst) of
@@ -1146,7 +1147,7 @@ bsm_save(Reg, SavePoint, Vst) ->
bsm_restore(Reg, {atom,start}, Vst) ->
%% (Mostly) automatic save point refering to where the match started.
%% It is always valid. But don't forget to validate the context register.
- bsm_get_context(Reg, Vst),
+ bsm_validate_context(Reg, Vst),
Vst;
bsm_restore(Reg, SavePoint, Vst) ->
case bsm_get_context(Reg, Vst) of
@@ -1312,8 +1313,7 @@ assert_term(Src, Vst) ->
%%
assert_type(WantedType, Term, Vst) ->
- assert_type(WantedType, get_term_type(Term, Vst)),
- Vst.
+ assert_type(WantedType, get_term_type(Term, Vst)).
assert_type(Correct, Correct) -> ok;
assert_type(float, {float,_}) -> ok;
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 0a368df5d6..497af2b52c 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.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
@@ -202,36 +202,38 @@ format_error(bad_crypto_key) ->
format_error(no_crypto_key) ->
"no crypto key supplied.";
format_error({native, E}) ->
- io_lib:fwrite("native-code compilation failed with reason: ~P.",
+ io_lib:fwrite("native-code compilation failed with reason: ~tP.",
[E, 25]);
format_error({native_crash,E,Stk}) ->
- io_lib:fwrite("native-code compilation crashed with reason: ~P.\n~P\n",
+ io_lib:fwrite("native-code compilation crashed with reason: ~tP.\n~tP\n",
[E,25,Stk,25]);
format_error({open,E}) ->
- io_lib:format("open error '~s'", [file:format_error(E)]);
+ io_lib:format("open error '~ts'", [file:format_error(E)]);
format_error({epp,E}) ->
epp:format_error(E);
format_error(write_error) ->
"error writing file";
format_error({rename,From,To,Error}) ->
- io_lib:format("failed to rename ~s to ~s: ~s",
+ io_lib:format("failed to rename ~ts to ~ts: ~ts",
[From,To,file:format_error(Error)]);
format_error({delete,File,Error}) ->
- io_lib:format("failed to delete file ~s: ~s",
+ io_lib:format("failed to delete file ~ts: ~ts",
[File,file:format_error(Error)]);
format_error({delete_temp,File,Error}) ->
- io_lib:format("failed to delete temporary file ~s: ~s",
+ io_lib:format("failed to delete temporary file ~ts: ~ts",
[File,file:format_error(Error)]);
format_error({parse_transform,M,R}) ->
- io_lib:format("error in parse transform '~s': ~p", [M, R]);
+ io_lib:format("error in parse transform '~s': ~tp", [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]);
+ io_lib:format("error in core transform '~s': ~tp", [M, R]);
format_error({crash,Pass,Reason}) ->
- io_lib:format("internal error in ~p;\ncrash reason: ~p", [Pass,Reason]);
+ io_lib:format("internal error in ~p;\ncrash reason: ~tp", [Pass,Reason]);
format_error({bad_return,Pass,Reason}) ->
- io_lib:format("internal error in ~p;\nbad return value: ~p", [Pass,Reason]);
+ io_lib:format("internal error in ~p;\nbad return value: ~tp", [Pass,Reason]);
format_error({module_name,Mod,Filename}) ->
- io_lib:format("Module name '~s' does not match file name '~s'",
+ io_lib:format("Module name '~s' does not match file name '~ts'",
[Mod,Filename]).
%% The compile state record.
@@ -246,6 +248,7 @@ 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_encoding(),
errors=[],
warnings=[]}).
@@ -268,7 +271,7 @@ internal_comp(Passes, File, Suffix, St0) ->
ofile=objfile(Base, St0)},
Run = case member(time, St1#compile.options) of
true ->
- io:format("Compiling ~p\n", [File]),
+ io:format("Compiling ~tp\n", [File]),
fun run_tc/2;
false -> fun({_Name,Fun}, St) -> catch Fun(St) end
end,
@@ -551,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) ->
@@ -630,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}},
@@ -657,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"}}]},
@@ -681,7 +683,7 @@ binary_passes() ->
%% Remove the target file so we don't have an old one if the compilation fail.
remove_file(St) ->
- file:delete(St#compile.ofile),
+ _ = file:delete(St#compile.ofile),
{ok,St}.
-record(asm_module, {module,
@@ -733,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}}
@@ -776,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}}
@@ -850,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}};
@@ -887,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).
@@ -899,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.
@@ -1085,10 +1089,10 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) ->
{ok,Output1,CloseOutput} ->
try
%% Write the Makefile.
- io:fwrite(Output1, "~s", [Code]),
+ io:fwrite(Output1, "~ts", [Code]),
%% Close the file if relevant.
if
- CloseOutput -> file:close(Output1);
+ CloseOutput -> ok = file:close(Output1);
true -> ok
end,
{ok,St}
@@ -1227,7 +1231,7 @@ encrypt(des3_cbc=Mode, {K1,K2,K3, IVec}, Bin0) ->
random_bytes(N) ->
{A,B,C} = now(),
- random:seed(A, B, C),
+ _ = random:seed(A, B, C),
random_bytes_1(N, []).
random_bytes_1(0, Acc) -> Acc;
@@ -1236,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),
@@ -1338,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);
_ ->
@@ -1423,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("~ts:~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("~ts:~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("~ts: ~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("~ts:~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("~ts:~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("~ts: ~ts\n", [F,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(_F, []) -> ok.
@@ -1500,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) ->
@@ -1511,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};
@@ -1519,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..8775c84698 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -1,7 +1,7 @@
% This is an -*- erlang -*- file.
%% %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
@@ -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..1e8983f594 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. 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
@@ -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);
@@ -308,7 +309,7 @@ expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) ->
{Vvs,St1} = variable_list(Vs, St0),
return_match(Rt, 1, body(B, union(Vvs, Def), any, St1));
expr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
- St1 = expr(Arg, Def, any, St0), %Ignore values
+ St1 = expr(Arg, Def, 1, St0),
body(B, Def, Rt, St1);
expr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
St1 = body(Arg, Def, let_varcount(Vs), St0), %This is a body
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/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index 9ad2378d00..3ad3c8c690 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -68,6 +68,8 @@ is_pure(erlang, atom_to_list, 1) -> true;
is_pure(erlang, binary_part, 2) -> true;
is_pure(erlang, binary_part, 3) -> true;
is_pure(erlang, binary_to_atom, 2) -> true;
+is_pure(erlang, binary_to_float, 1) -> true;
+is_pure(erlang, binary_to_integer, 1) -> true;
is_pure(erlang, binary_to_list, 1) -> true;
is_pure(erlang, binary_to_list, 3) -> true;
is_pure(erlang, bit_size, 1) -> true;
@@ -75,8 +77,10 @@ is_pure(erlang, byte_size, 1) -> true;
is_pure(erlang, element, 2) -> true;
is_pure(erlang, float, 1) -> true;
is_pure(erlang, float_to_list, 1) -> true;
+is_pure(erlang, float_to_binary, 1) -> true;
is_pure(erlang, hash, 2) -> false;
is_pure(erlang, hd, 1) -> true;
+is_pure(erlang, integer_to_binary, 1) -> true;
is_pure(erlang, integer_to_list, 1) -> true;
is_pure(erlang, is_atom, 1) -> true;
is_pure(erlang, is_boolean, 1) -> true;
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 18fba7962b..cda3f7d81e 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.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
@@ -132,7 +132,12 @@ body(Body, Sub) ->
body(#c_values{anno=A,es=Es0}, Ctxt, Sub) ->
Es1 = expr_list(Es0, Ctxt, Sub),
- #c_values{anno=A,es=Es1};
+ case Ctxt of
+ value ->
+ #c_values{anno=A,es=Es1};
+ effect ->
+ make_effect_seq(Es1, Sub)
+ end;
body(E, Ctxt, Sub) ->
?ASSERT(verify_scope(E, Sub)),
expr(E, Ctxt, Sub).
@@ -686,11 +691,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 +721,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 +744,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 +770,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 +798,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 +828,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 +842,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 +869,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 +896,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 +933,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 +945,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 +994,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 +1006,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'},
@@ -1232,6 +1277,8 @@ eval_element(Call, #c_literal{val=Pos}, #c_var{name=V}, Types)
true ->
eval_failure(Call, badarg)
end;
+ {ok,_} ->
+ eval_failure(Call, badarg);
error ->
Call
end;
@@ -2632,16 +2679,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 +2699,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 +2714,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 +2763,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 +2896,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 97d3ff626c..7d918a55ed 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -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 812e85553f..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.
@@ -714,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) ->
@@ -1386,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
@@ -1410,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) ->
@@ -1419,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}.
@@ -1576,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]);
@@ -1585,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]);
@@ -1595,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};
@@ -1626,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}.
@@ -1907,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).
@@ -1938,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).
@@ -1958,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_kernel.erl b/lib/compiler/src/v3_kernel.erl
index b184987625..5f1c108f7c 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.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
@@ -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]).
@@ -1081,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)
@@ -1097,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'
@@ -1346,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.
@@ -1377,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;
@@ -1385,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]
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index e047166ade..51b3064589 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 \
@@ -76,6 +75,7 @@ INLINE= \
CORE_MODULES = \
bs_shadowed_size_var \
+ unused_multiple_values_error \
nested_call_in_case
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index f7388f1614..0d00769704 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -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..bf67eedd5f 100644
--- a/lib/compiler/test/beam_expect_SUITE.erl
+++ b/lib/compiler/test/beam_except_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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,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..bc1a9e2b3b 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-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
@@ -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..8609a490f5 100644
--- a/lib/compiler/test/bs_bit_binaries_SUITE.erl
+++ b/lib/compiler/test/bs_bit_binaries_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -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 9ab76449c7..4ea5235bb6 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -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 ->
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 86c8cb23f5..9f15845d33 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-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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.
@@ -1079,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..8e7b7292cc 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -28,26 +28,30 @@ 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,
+ beam_utils_liveopt]}].
init_per_suite(Config) ->
Config.
@@ -140,7 +144,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 +626,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) ->
@@ -681,4 +684,21 @@ do_split_cases(A) ->
end,
Z.
+-record(alarmInfo, {type,cause,origin}).
+
+beam_utils_liveopt(Config) ->
+ F = beam_utils_liveopt_fun(42, pebkac, user),
+ void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}),
+ ok.
+
+beam_utils_liveopt_fun(Peer, Cause, Origin) ->
+ fun(PeerNo, AlarmInfo)
+ when PeerNo == Peer andalso
+ AlarmInfo == #alarmInfo{type=sctp,
+ cause=Cause,
+ origin=Origin} ->
+ void
+ end.
+
+
id(I) -> I.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 2cd75944f4..229e5a98a1 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -27,7 +27,7 @@
app_test/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]).
@@ -44,7 +44,7 @@ all() ->
test_lib:recompile(?MODULE),
[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].
@@ -410,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/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..abc9ab6a72 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-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
@@ -21,7 +21,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1,
- eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1]).
+ eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1,
+ unused_multiple_values_error/1,unused_multiple_values/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
@@ -31,11 +32,14 @@ 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,
+ unused_multiple_values_error,unused_multiple_values]}].
+
init_per_suite(Config) ->
Config.
@@ -67,6 +71,9 @@ t_element(Config) when is_list(Config) ->
?line {'EXIT',{badarg,_}} = (catch element(5, {a,b,c,d})),
?line {'EXIT',{badarg,_}} = (catch element(5, {a,b,X,d})),
?line {'EXIT',{badarg,_}} = (catch element(5.0, {a,b,X,d})),
+ {'EXIT',{badarg,_}} = (catch element(2, not_a_tuple)),
+ {'EXIT',{badarg,_}} = (catch element(2, [])),
+ {'EXIT',{badarg,_}} = (catch element(2, Tuple == 3)),
case id({a,b,c}) of
{_,_,_}=Tup ->
?line {'EXIT',{badarg,_}} = (catch element(4, Tup))
@@ -87,6 +94,9 @@ setelement(Config) when is_list(Config) ->
?line {'EXIT',{badarg,_}} = (catch setelement_crash({a,b,c,d,e,f})),
?line error = setelement_crash_2({a,b,c,d,e,f}, <<42>>),
+
+ {'EXIT',{badarg,_}} = (catch setelement(1, not_a_tuple, New)),
+
ok.
setelement_crash(Tuple) ->
@@ -281,3 +291,41 @@ cover_is_safe_bool_expr(X) ->
end.
id(I) -> I.
+
+unused_multiple_values_error(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:dirname(code:which(?MODULE)),
+ Core = filename:join(Dir, "unused_multiple_values_error"),
+ Opts = [no_copt,clint,return,from_core,{outdir,PrivDir}
+ |test_lib:opt_opts(?MODULE)],
+ {error,[{unused_multiple_values_error,
+ [{core_lint,{return_mismatch,{hello,1}}}]}],
+ []} = c:c(Core, Opts),
+ ok.
+
+unused_multiple_values(Config) when is_list(Config) ->
+ put(unused_multiple_values, []),
+ [false] = test_unused_multiple_values(false),
+ [b,a,{a,b},false] = test_unused_multiple_values({a,b}),
+ ok.
+
+test_unused_multiple_values(X) ->
+ ok = do_unused_multiple_values(X),
+ get(unused_multiple_values).
+
+do_unused_multiple_values(X) ->
+ case do_something(X) of
+ false ->
+ A = false;
+ Res ->
+ {A,B} = Res,
+ do_something(A),
+ do_something(B)
+ end,
+ _ThisShouldNotFail = A,
+ ok.
+
+do_something(I) ->
+ put(unused_multiple_values,
+ [I|get(unused_multiple_values)]),
+ I.
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index fb51e013ce..859c4571ea 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -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..5656d23090 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -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/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl
index 2e17d3fde6..d9b92766e4 100644
--- a/lib/compiler/test/inline_SUITE.erl
+++ b/lib/compiler/test/inline_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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,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..ae7d764535 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.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
@@ -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 0376c7ef3e..44c7161530 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -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,
@@ -263,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..b91f2922fb 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.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
@@ -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..c9f5a2053e 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. 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
@@ -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..a8befbecd9 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. 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
@@ -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/unused_multiple_values_error.core b/lib/compiler/test/unused_multiple_values_error.core
new file mode 100644
index 0000000000..e06587c936
--- /dev/null
+++ b/lib/compiler/test/unused_multiple_values_error.core
@@ -0,0 +1,11 @@
+module 'unused_multiple_values_error' ['hello'/1]
+ attributes []
+'hello'/1 =
+ fun (_cor0) ->
+ do
+ case _cor0 of
+ <_cor0> when 'true' ->
+ <'ok','ok'>
+ end
+ 'ok'
+end
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index f6a572abfa..f00bfe663b 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. 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
@@ -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 b77c82309f..90d2bff0ad 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 4.8.2
+COMPILER_VSN = 4.9.1
diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml
index de98a44b6a..b0ac50ab10 100644
--- a/lib/cosEvent/doc/src/notes.xml
+++ b/lib/cosEvent/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2011</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,7 +32,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosEvent 2.1.12</title>
+ <section><title>cosEvent 2.1.13</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosEvent 2.1.12</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosEvent/src/Makefile b/lib/cosEvent/src/Makefile
index 7787fad786..2864ee0538 100644
--- a/lib/cosEvent/src/Makefile
+++ b/lib/cosEvent/src/Makefile
@@ -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/vsn.mk b/lib/cosEvent/vsn.mk
index 2cd943e4b2..85c5c6aadd 100644
--- a/lib/cosEvent/vsn.mk
+++ b/lib/cosEvent/vsn.mk
@@ -1,3 +1,3 @@
-COSEVENT_VSN = 2.1.12
+COSEVENT_VSN = 2.1.13
diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml
index fa96792c33..565b501148 100644
--- a/lib/cosEventDomain/doc/src/notes.xml
+++ b/lib/cosEventDomain/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosEventDomain 1.1.12</title>
+ <section><title>cosEventDomain 1.1.13</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosEventDomain 1.1.12</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosEventDomain/src/Makefile b/lib/cosEventDomain/src/Makefile
index 213d433c72..b4b74bd192 100644
--- a/lib/cosEventDomain/src/Makefile
+++ b/lib/cosEventDomain/src/Makefile
@@ -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/vsn.mk b/lib/cosEventDomain/vsn.mk
index 5ad421e319..e9cf92395a 100644
--- a/lib/cosEventDomain/vsn.mk
+++ b/lib/cosEventDomain/vsn.mk
@@ -1,3 +1,3 @@
-COSEVENTDOMAIN_VSN = 1.1.12
+COSEVENTDOMAIN_VSN = 1.1.13
diff --git a/lib/cosFileTransfer/doc/src/notes.xml b/lib/cosFileTransfer/doc/src/notes.xml
index f38597db10..56a85f2060 100644
--- a/lib/cosFileTransfer/doc/src/notes.xml
+++ b/lib/cosFileTransfer/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosFileTransfer 1.1.13</title>
+ <section><title>cosFileTransfer 1.1.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosFileTransfer 1.1.13</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosFileTransfer/src/Makefile b/lib/cosFileTransfer/src/Makefile
index d552349ede..9d3abb9bba 100644
--- a/lib/cosFileTransfer/src/Makefile
+++ b/lib/cosFileTransfer/src/Makefile
@@ -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/fileTransfer_SUITE.erl b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
index 79a234bd28..dfe6fabfab 100644
--- a/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
+++ b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk
index 6d0c6669a3..58545be931 100644
--- a/lib/cosFileTransfer/vsn.mk
+++ b/lib/cosFileTransfer/vsn.mk
@@ -1 +1 @@
-COSFILETRANSFER_VSN = 1.1.13
+COSFILETRANSFER_VSN = 1.1.14
diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml
index c79d040f9a..babf29d4a9 100644
--- a/lib/cosNotification/doc/src/notes.xml
+++ b/lib/cosNotification/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosNotification 1.1.18</title>
+ <section><title>cosNotification 1.1.19</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosNotification 1.1.18</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile
index decf598bbf..13153bd7a1 100644
--- a/lib/cosNotification/src/Makefile
+++ b/lib/cosNotification/src/Makefile
@@ -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/vsn.mk b/lib/cosNotification/vsn.mk
index e7c5465fe4..20eb6167ac 100644
--- a/lib/cosNotification/vsn.mk
+++ b/lib/cosNotification/vsn.mk
@@ -1,2 +1,2 @@
-COSNOTIFICATION_VSN = 1.1.18
+COSNOTIFICATION_VSN = 1.1.19
diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml
index f5f737e2a3..eaf1100c4e 100644
--- a/lib/cosProperty/doc/src/notes.xml
+++ b/lib/cosProperty/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosProperty 1.1.15</title>
+ <section><title>cosProperty 1.1.16</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosProperty 1.1.15</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosProperty/src/Makefile b/lib/cosProperty/src/Makefile
index 54d1b6021c..d7b75d8bc5 100644
--- a/lib/cosProperty/src/Makefile
+++ b/lib/cosProperty/src/Makefile
@@ -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/vsn.mk b/lib/cosProperty/vsn.mk
index a4b1a2c94e..ac7820216e 100644
--- a/lib/cosProperty/vsn.mk
+++ b/lib/cosProperty/vsn.mk
@@ -1,2 +1,2 @@
-COSPROPERTY_VSN = 1.1.15
+COSPROPERTY_VSN = 1.1.16
diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml
index c70978df2b..f468f8aa53 100644
--- a/lib/cosTime/doc/src/notes.xml
+++ b/lib/cosTime/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,7 +32,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosTime 1.1.12</title>
+ <section><title>cosTime 1.1.13</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosTime 1.1.12</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosTime/src/Makefile b/lib/cosTime/src/Makefile
index e8536a3f0a..0ab2b414ce 100644
--- a/lib/cosTime/src/Makefile
+++ b/lib/cosTime/src/Makefile
@@ -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/vsn.mk b/lib/cosTime/vsn.mk
index 14d3b61bc6..02cd669222 100644
--- a/lib/cosTime/vsn.mk
+++ b/lib/cosTime/vsn.mk
@@ -1,2 +1,2 @@
-COSTIME_VSN = 1.1.12
+COSTIME_VSN = 1.1.13
diff --git a/lib/cosTransactions/doc/src/notes.xml b/lib/cosTransactions/doc/src/notes.xml
index f3a7a83fb0..e375338a8f 100644
--- a/lib/cosTransactions/doc/src/notes.xml
+++ b/lib/cosTransactions/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>
@@ -32,7 +32,22 @@
<file>notes.xml</file>
</header>
- <section><title>cosTransactions 1.2.12</title>
+ <section><title>cosTransactions 1.2.13</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>cosTransactions 1.2.12</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile
index 1d935c4d1d..7b4a9cf858 100644
--- a/lib/cosTransactions/src/Makefile
+++ b/lib/cosTransactions/src/Makefile
@@ -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/vsn.mk b/lib/cosTransactions/vsn.mk
index 7ca604b589..5414270a3d 100644
--- a/lib/cosTransactions/vsn.mk
+++ b/lib/cosTransactions/vsn.mk
@@ -1 +1 @@
-COSTRANSACTIONS_VSN = 1.2.12
+COSTRANSACTIONS_VSN = 1.2.13
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index ffd556ca1a..a20ddff05c 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.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 91ab244620..e77e5fb8f0 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -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,8 @@
#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
@@ -67,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>
@@ -125,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);
@@ -135,6 +140,10 @@ 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[]);
@@ -172,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[]);
@@ -204,17 +213,6 @@ 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);
@@ -253,6 +251,10 @@ 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},
@@ -291,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},
@@ -325,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))
@@ -333,6 +335,8 @@ 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
@@ -347,7 +351,6 @@ ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload)
#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;
@@ -374,55 +377,60 @@ 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");
@@ -451,37 +459,75 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
init_digest_types(env);
- *priv_data = NULL;
- library_refc++;
- return 0;
-}
-
-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;
@@ -489,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[])
@@ -514,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));
}
@@ -569,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;
@@ -1199,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 */
@@ -1222,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[])
@@ -2338,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 */
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 4dcd6fc4ea..6b9b2ef207 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>2012</year>
+ <year>1999</year><year>2013</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>
@@ -259,24 +265,28 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
<name>hash(Type, Data) -> Digest</name>
<fsummary></fsummary>
<type>
- <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <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 | sha | sha224 | sha256 | sha384 | sha512</v>
+ <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>
@@ -333,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>
@@ -548,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>
@@ -565,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>
diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml
index 1c01e3f099..8371db1ff2 100644
--- a/lib/crypto/doc/src/crypto_app.xml
+++ b/lib/crypto/doc/src/crypto_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -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 4178ca2b08..bd1f179f7d 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>2012</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,36 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 2.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Enable runtime upgrade of crypto including the OpenSSL
+ library used by crypto.</p>
+ <p>
+ Own Id: OTP-10596</p>
+ </item>
+ <item>
+ <p>
+ Improve documentation and tests for hmac functions in
+ crypto. Thanks to Daniel White</p>
+ <p>
+ Own Id: OTP-10640</p>
+ </item>
+ <item>
+ <p>
+ Added ripemd160 support to crypto. Thanks to Michael
+ Loftis</p>
+ <p>
+ Own Id: OTP-10667</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile
index 910e89363c..574c2076f2 100644
--- a/lib/crypto/src/Makefile
+++ b/lib/crypto/src/Makefile
@@ -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
@@ -57,7 +57,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_obsolete_guard -DCRYPTO_VSN=\"$(VSN)\"
+ERL_COMPILE_FLAGS += +warn_obsolete_guard -DCRYPTO_VSN=\"$(VSN)\" -Werror
# ----------------------------------------------------
# Targets
@@ -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 0089e79a4f..1328a95e87 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.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
@@ -35,7 +35,7 @@
-export([sha256_mac/2, sha256_mac/3]).
-export([sha384_mac/2, sha384_mac/3]).
-export([sha512_mac/2, sha512_mac/3]).
--export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]).
+-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]).
@@ -78,12 +78,11 @@
md5_mac, md5_mac_96,
sha_mac, sha_mac_96,
sha224_mac, sha256_mac, sha384_mac, sha512_mac,
- sha_mac_init, sha_mac_update, sha_mac_final,
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,
@@ -103,6 +102,13 @@
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' | 'sha224' | 'sha256' | 'sha384' | 'sha512'.
@@ -114,7 +120,7 @@
-on_load(on_load/0).
--define(CRYPTO_NIF_VSN,101).
+-define(CRYPTO_NIF_VSN,201).
on_load() ->
LibBaseName = "crypto",
@@ -140,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 =
@@ -152,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,
@@ -191,43 +197,48 @@ version() -> ?CRYPTO_VSN.
%%
-spec hash(_, iodata()) -> binary().
-hash(md5, Data) -> md5(Data);
-hash(md4, Data) -> md4(Data);
-hash(sha, Data) -> sha(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'|'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(sha224) -> {sha224, sha224_init()};
-hash_init(sha256) -> {sha256, sha256_init()};
-hash_init(sha384) -> {sha384, sha384_init()};
-hash_init(sha512) -> {sha512, sha512_init()}.
+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({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)}.
+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({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).
+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
@@ -257,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().
@@ -412,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.
@@ -597,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).
@@ -617,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
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 7ac693f371..6f2df0f07b 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.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
@@ -38,7 +38,10 @@
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,
@@ -86,11 +89,13 @@ groups() ->
[{info, [sequence],[info, {group, rest}]},
{rest, [],
[md5, md5_update, md4, md4_update, md5_mac,
- md5_mac_io, sha, sha_update,
+ 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_rfc4231,
+ 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,
@@ -192,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) ->
@@ -349,12 +363,8 @@ hmac_update_sha256(doc) ->
hmac_update_sha256(suite) ->
[];
hmac_update_sha256(Config) when is_list(Config) ->
- case openssl_version() of
- V when V < 16#908000 ->
- {skipped,"OpenSSL version too old"};
- _ ->
- hmac_update_sha256_do()
- end.
+ if_098(fun() -> hmac_update_sha256_do() end).
+
hmac_update_sha256_do() ->
?line Key = hexstr2bin("00010203101112132021222330313233"
@@ -376,12 +386,7 @@ hmac_update_sha512(doc) ->
hmac_update_sha512(suite) ->
[];
hmac_update_sha512(Config) when is_list(Config) ->
- case openssl_version() of
- V when V < 16#908000 ->
- {skipped,"OpenSSL version too old"};
- _ ->
- hmac_update_sha512_do()
- end.
+ if_098(fun() -> hmac_update_sha512_do() end).
hmac_update_sha512_do() ->
?line Key = hexstr2bin("00010203101112132021222330313233"
@@ -416,18 +421,174 @@ hmac_update_md5(Config) when is_list(Config) ->
?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 and hmac_init, hmac_update, and hmac_final. "
+ ["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) ->
- case openssl_version() of
- V when V < 16#908000 ->
- {skipped,"OpenSSL version too old"};
- _ ->
- hmac_rfc4231_do()
- end.
+ if_098(fun() -> hmac_rfc4231_do() end).
hmac_rfc4231_do() ->
%% Test Case 1
@@ -449,29 +610,37 @@ hmac_rfc4231_do() ->
?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">>,
@@ -492,29 +661,37 @@ hmac_rfc4231_do() ->
?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),
@@ -535,29 +712,37 @@ hmac_rfc4231_do() ->
?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)),
@@ -578,29 +763,81 @@ hmac_rfc4231_do() ->
?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),
@@ -621,29 +858,37 @@ hmac_rfc4231_do() ->
?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),
@@ -666,29 +911,37 @@ hmac_rfc4231_do() ->
?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_2),
+ ?line m(Case7Exp512, Case7Mac512_3).
hmac_update_md5_io(doc) ->
["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. "
@@ -722,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")).
%%
%%
@@ -746,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")),
@@ -762,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"),
@@ -778,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"
@@ -796,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"),
@@ -996,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"),
@@ -1823,7 +2120,7 @@ worker_loop(N, Config) ->
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, hmac_update_sha256, hmac_update_sha512,
- hmac_rfc4231,
+ hmac_rfc2202, hmac_rfc4231,
aes_ctr_stream },
F = element(random:uniform(size(Funcs)),Funcs),
@@ -1959,3 +2256,10 @@ openssl_version() ->
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 ccfb1fd66e..2a0cb57aa9 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 2.2
+CRYPTO_VSN = 2.3
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index 66638a59f6..d5eef5d6a2 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,67 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 3.2.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The +pc flag to erl can be used to set the range of
+ characters considered printable. This affects how the
+ shell and io:format("~tp",...) functionality does
+ heuristic string detection. More can be read in STDLIB
+ users guide.</p>
+ <p>
+ Own Id: OTP-10884</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Debugger 3.2.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix Debugger settings dialog due to changed behavior in
+ wxFileDialog (Thanks to H�kan Mattsson)</p>
+ <p>
+ Own Id: OTP-10621</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Integer lists and utf-8 binaries in variables are now
+ displayed as strings.</p>
+ <p>
+ Own Id: OTP-10679</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 3.2.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile
index e8b350c0c7..85754da219 100644
--- a/lib/debugger/src/Makefile
+++ b/lib/debugger/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -98,7 +98,7 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_obsolete_guard
+ERL_COMPILE_FLAGS += +warn_obsolete_guard -Werror
# ----------------------------------------------------
@@ -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..f5744a6e14 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
@@ -345,15 +345,15 @@ trace(What, Args, true) ->
{Called, {Le,Li,M,F,As}} = Args,
case Called of
extern ->
- io_lib:format("++ (~w) <~w> ~w:~w~s~n",
+ io_lib:format("++ (~w) <~w> ~w:~w~ts~n",
[Le,Li,M,F,format_args(As)]);
local ->
- io_lib:format("++ (~w) <~w> ~w~s~n",
+ io_lib:format("++ (~w) <~w> ~w~ts~n",
[Le,Li,F,format_args(As)])
end;
call_fun ->
{Le,Li,F,As} = Args,
- io_lib:format("++ (~w) <~w> ~w~s~n",
+ io_lib:format("++ (~w) <~w> ~w~ts~n",
[Le, Li, F, format_args(As)]);
return ->
{Le,Val} = Args,
@@ -362,7 +362,7 @@ trace(What, Args, true) ->
bif ->
{Le,Li,M,F,As} = Args,
- io_lib:format("++ (~w) <~w> ~w:~w~s~n",
+ io_lib:format("++ (~w) <~w> ~w:~w~ts~n",
[Le, Li, M, F, format_args(As)])
end,
dbg_icmd:tell_attached({trace_output, Str});
@@ -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..3e1fb2dcae 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-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,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
@@ -321,7 +321,7 @@ gui_cmd('Messages', State) ->
fun(Msg, N) ->
Str1 = io_lib:format(" ~w:", [N]),
dbg_ui_trace_win:eval_output(Str1, bold),
- Str2 = io_lib:format(" ~s~n",[io_lib:print(Msg)]),
+ Str2 = io_lib:format(" ~ts~n",[io_lib:print(Msg)]),
dbg_ui_trace_win:eval_output(Str2, normal),
N+1
end,
@@ -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..20aac74c3d 100644
--- a/lib/debugger/src/dbg_wx_settings.erl
+++ b/lib/debugger/src/dbg_wx_settings.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
@@ -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..eaea01822c 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
@@ -331,7 +331,7 @@ gui_cmd('Messages', State) ->
fun(Msg, N) ->
Str1 = io_lib:format(" ~w:", [N]),
dbg_wx_trace_win:eval_output(State#state.win,Str1, bold),
- Str2 = io_lib:format(" ~s~n",[io_lib:print(Msg)]),
+ Str2 = io_lib:format(" ~ts~n",[io_lib:print(Msg)]),
dbg_wx_trace_win:eval_output(State#state.win,Str2, normal),
N+1
end,
@@ -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.
@@ -687,7 +687,7 @@ meta_cmd({trace_output, Str}, State) ->
%% Reply on a user command
meta_cmd({eval_rsp, Res}, State) ->
- Str = io_lib:print(Res),
+ Str = io_lib_pretty:print(Res,[{encoding,unicode}]),
dbg_wx_trace_win:eval_output(State#state.win, [$<,Str,10], normal),
State.
@@ -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..e54ce3913f 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),
@@ -858,7 +853,7 @@ handle_event(#wx{id=?EVAL_ENTRY, event=#wxCommand{type=command_text_enter}},
handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Row}},Wi) ->
Bs = get(bindings),
{Var,Val} = lists:nth(Row+1, Bs),
- Str = io_lib:format("< ~s = ~p~n", [Var, Val]),
+ Str = io_lib:format("< ~s = ~lp~n", [Var, Val]),
eval_output(Wi, Str, bold),
ignore;
handle_event(#wx{event=#wxList{type=command_list_item_activated, itemIndex=Row}},_Wi) ->
diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl
index faf3cc178f..eceacd7c88 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/i.erl b/lib/debugger/src/i.erl
index 4d0b862196..5805501524 100644
--- a/lib/debugger/src/i.erl
+++ b/lib/debugger/src/i.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
@@ -197,7 +197,7 @@ get_file(Mod) ->
end.
ilformat(A1, A2) ->
- format("~-20s ~s\n", [A1,A2]).
+ format("~-20s ~ts\n", [A1,A2]).
%% -------------------------------------------
%% Print all break points in modules.
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index b3a8a07f03..bdd671cff1 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.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
@@ -517,7 +517,7 @@ int_mod(AbsMod, Dist) when is_atom(AbsMod); is_list(AbsMod) ->
[App, AbsMod]),
error;
_Error ->
- io:format("** Invalid beam file or no abstract code: ~p\n",
+ io:format("** Invalid beam file or no abstract code: ~tp\n",
[AbsMod]),
error
end.
@@ -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}|_]}} ->
@@ -682,43 +674,52 @@ everywhere(local, Fun) ->
Fun().
scan_module_name(File) ->
- case erl_prim_loader:get_file(filename:absname(File)) of
- {ok, Bin, _FullPath} ->
- Chars = binary_to_list(Bin),
- R = (catch {ok, scan_module_name_1(Chars)}),
- case R of
- {ok, A} when is_atom(A) -> A;
- _ -> error
- end;
- _ ->
- error
+ try
+ {ok, Bin, _FullPath} =
+ erl_prim_loader:get_file(filename:absname(File)),
+ scan_module_name_1([], <<>>, Bin, enc(Bin))
+ catch
+ _:_ ->
+ throw({error, no_beam})
end.
-scan_module_name_1(Chars) ->
- case erl_scan:tokens("", Chars, 1) of
- {done, {ok, Ts, _}, Rest} ->
- scan_module_name_2(Ts, Rest);
- _ ->
- error
+scan_module_name_1(Cont0, B0, Bin0, Enc) ->
+ N = min(100, byte_size(Bin0)),
+ {Bin1, Bin} = erlang:split_binary(Bin0, N),
+ {Chars, B1} =
+ case unicode:characters_to_list(list_to_binary([B0, Bin1]), Enc) of
+ {incomplete, List, Binary} ->
+ {List, Binary};
+ List when is_list(List), List =/= [] ->
+ {List, <<>>}
+ end,
+ scan_module_name_2(Cont0, Chars, B1, Bin, Enc).
+
+scan_module_name_2(Cont0, Chars, B1, Bin, Enc) ->
+ case erl_scan:tokens(Cont0, Chars, _AnyLine = 1) of
+ {done, {ok, Ts, _}, Rest} ->
+ scan_module_name_3(Ts, Rest, B1, Bin, Enc);
+ {more, Cont} ->
+ scan_module_name_1(Cont, B1, Bin, Enc)
end.
-scan_module_name_2([{'-',_},{atom,_,module},{'(',_} | _]=Ts, _Chars) ->
- scan_module_name_3(Ts);
-scan_module_name_2([{'-',_},{atom,_,_} | _], Chars) ->
- scan_module_name_1(Chars);
-scan_module_name_2(_, _) ->
- error.
-
-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);
- _ -> error
+scan_module_name_3([{'-',_},{atom,_,module},{'(',_} | _]=Ts,
+ _Chars, _B1, _Bin, _Enc) ->
+ scan_module_name_4(Ts);
+scan_module_name_3([{'-',_},{atom,_,_} | _], Chars, B1, Bin, Enc) ->
+ scan_module_name_2("", Chars, B1, Bin, Enc).
+
+scan_module_name_4(Ts) ->
+ {ok, {attribute,_,module,M}} = erl_parse:parse_form(Ts),
+ true = is_atom(M),
+ M.
+
+enc(Bin) ->
+ case epp:read_encoding_from_binary(Bin) of
+ none -> epp:default_encoding();
+ Encoding -> Encoding
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/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
index a92251e1af..bb2669f450 100644
--- a/lib/debugger/test/erl_eval_SUITE.erl
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. 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
@@ -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..c5c6a56363 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
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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/debugger/vsn.mk b/lib/debugger/vsn.mk
index 5b572f5f20..58d462f409 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 3.2.8
+DEBUGGER_VSN = 3.2.10
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 4080dfdf77..0ac96e8ac9 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>
@@ -158,6 +158,9 @@
<item>Make Dialyzer a bit more quiet.</item>
<tag><c><![CDATA[--verbose]]></c></tag>
<item>Make Dialyzer a bit more verbose.</item>
+ <tag><c><![CDATA[--statistics]]></c></tag>
+ <item>Prints information about the progress of execution (analysis phases,
+ time spent in each and size of the relative input).</item>
<tag><c><![CDATA[--build_plt]]></c></tag>
<item>The analysis starts from an empty plt and creates a new one from
the files specified with <c><![CDATA[-c]]></c> and
@@ -228,6 +231,9 @@
match.</item>
<tag><c><![CDATA[-Wno_opaque]]></c></tag>
<item>Suppress warnings for violations of opaqueness of data types.</item>
+ <tag><c><![CDATA[-Wno_behaviours]]></c>***</tag>
+ <item>Suppress warnings about behaviour callbacks which drift from the
+ published recommended interfaces.</item>
<tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
<item>Include warnings for function calls which ignore a structured return
value or do not match against one of many possible return
@@ -237,9 +243,6 @@
exception.</item>
<tag><c><![CDATA[-Wrace_conditions]]></c>***</tag>
<item>Include warnings for possible race conditions.</item>
- <tag><c><![CDATA[-Wbehaviours]]></c>***</tag>
- <item>Include warnings about behaviour callbacks which drift from the
- published recommended interfaces.</item>
<tag><c><![CDATA[-Wunderspecs]]></c>***</tag>
<item>Warn about underspecified functions
(the -spec is strictly more allowing than the success typing).</item>
@@ -288,7 +291,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 f485e4d03b..cd809662f2 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>2012</year>
+ <year>2006</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,90 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 2.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to Unicode support. </p>
+ <p>
+ Own Id: OTP-10820</p>
+ </item>
+ <item>
+ <p>
+ User defined types with same name and different arity and
+ documentation inconsistencies. Thanks Stavros Aronis.</p>
+ <p>
+ Own Id: OTP-10861</p>
+ </item>
+ <item>
+ <p>
+ Native code compilation changes. Thanks to Kostis
+ Sagonas.</p>
+ <p>
+ Own Id: OTP-10865</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 2.5.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p> Dialyzer no longer outputs warnings for unused
+ anonymous functions ("funs"). Warnings are still output
+ for unused functions. </p>
+ <p>
+ Own Id: OTP-10433</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Fix precision of record creation violation warnings.
+ Thanks to Stavros Aronis</p>
+ <p>
+ Own Id: OTP-10681</p>
+ </item>
+ <item>
+ <p>
+ Report spec discrepancy on mismatching lists. Thanks to
+ Stavros Aronis.</p>
+ <p>
+ Own Id: OTP-10740</p>
+ </item>
+ <item>
+ <p>
+ Properly support functions with arbitrary arity in type
+ specs. Thanks to Stavros Aronis.</p>
+ <p>
+ Own Id: OTP-10772</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 2.5.3</title>
<section><title>Fixed Bugs and Malfunctions</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 a785ea5502..63c51e219a 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -383,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_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 36aef2a37f..2b9a69ce77 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -25,8 +25,6 @@
%%%
%%% Created : 28 Oct 2009 by Stavros Aronis <[email protected]>
%%%-------------------------------------------------------------------
-%%% NOTE: This module is currently experimental -- do NOT rely on it!
-%%%-------------------------------------------------------------------
-module(dialyzer_behaviours).
@@ -127,15 +125,12 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest],
erl_types:t_to_string(CbReturnType, Records)]}|Acc00]
end
end,
- Acc02 =
- case erl_types:any_none(
- erl_types:t_inf_lists(ArgTypes, CbArgTypes)) of
- false -> Acc01;
- true ->
- find_mismatching_args(type, ArgTypes, CbArgTypes, Behaviour,
- Function, Arity, Records, 1, Acc01)
- end,
- Acc02
+ case erl_types:any_none(erl_types:t_inf_lists(ArgTypes, CbArgTypes)) of
+ false -> Acc01;
+ true ->
+ find_mismatching_args(type, ArgTypes, CbArgTypes, Behaviour,
+ Function, Arity, Records, 1, Acc01)
+ end
end,
Acc2 =
case dialyzer_codeserver:lookup_mfa_contract(CbMFA, Codeserver) of
@@ -157,16 +152,14 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest],
erl_types:t_to_string(ExtraType, Records),
erl_types:t_to_string(CbReturnType, Records)]}|Acc10]
end,
- Acc12 =
- case erl_types:any_none(
- erl_types:t_inf_lists(SpecArgTypes, CbArgTypes)) of
- false -> Acc11;
- true ->
- find_mismatching_args({spec, File, Line}, SpecArgTypes,
- CbArgTypes, Behaviour, Function,
- Arity, Records, 1, Acc11)
- end,
- Acc12
+ case erl_types:any_none(
+ erl_types:t_inf_lists(SpecArgTypes, CbArgTypes)) of
+ false -> Acc11;
+ true ->
+ find_mismatching_args({spec, File, Line}, SpecArgTypes,
+ CbArgTypes, Behaviour, Function,
+ Arity, Records, 1, Acc11)
+ end
end,
NewAcc = Acc2,
check_all_callbacks(Module, Behaviour, Rest, State, NewAcc).
diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl
index 64e0ee88af..b9ad3f857d 100644
--- a/lib/dialyzer/src/dialyzer_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_callgraph.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -798,10 +798,7 @@ condensation(G) ->
fun({V1, V2}) ->
I1 = ets:lookup_element(V2I, V1, 2),
I2 = ets:lookup_element(V2I, V2, 2),
- case I1 =:= I2 of
- true -> true;
- false -> ets:insert(I2I, {I1, I2})
- end
+ I1 =:= I2 orelse ets:insert(I2I, {I1, I2})
end,
lists:foreach(Fun1, digraph:edges(G)),
Fun3 =
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 6732d96b98..2456585bd0 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -2,7 +2,7 @@
%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -488,6 +488,7 @@ expand_dependent_modules_1([Mod|Mods], Included, ModDeps) ->
expand_dependent_modules_1([], Included, _ModDeps) ->
Included.
+-define(MIN_PARALLELISM, 7).
-define(MIN_FILES_FOR_NATIVE_COMPILE, 20).
-spec hipe_compile([file:filename()], #options{}) -> 'ok'.
@@ -501,11 +502,14 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
case erlang:system_info(hipe_architecture) of
undefined -> ok;
_ ->
- Mods = [lists, dict, gb_sets, gb_trees, ordsets, sets,
+ Mods = [lists, dict, digraph, digraph_utils, ets,
+ gb_sets, gb_trees, ordsets, sets, sofs,
cerl, cerl_trees, erl_types, erl_bif_types,
- dialyzer_analysis_callgraph, dialyzer_codeserver,
- dialyzer_dataflow, dialyzer_dep, dialyzer_plt,
- dialyzer_succ_typings, dialyzer_typesig],
+ dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
+ dialyzer_codeserver, dialyzer_contracts,
+ dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
+ dialyzer_plt, dialyzer_succ_typings, dialyzer_typesig,
+ dialyzer_typesig, dialyzer_worker],
report_native_comp(Options),
{T1, _} = statistics(wall_clock),
native_compile(Mods),
@@ -515,12 +519,12 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
end.
native_compile(Mods) ->
- case erlang:system_info(schedulers) of
- %% N when N > 1 ->
- %% Parent = self(),
- %% Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods],
- %% lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
- _ -> % 1 ->
+ case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
+ true ->
+ Parent = self(),
+ Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods],
+ lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
+ false ->
lists:foreach(fun (Mod) -> hc(Mod) end, Mods)
end.
@@ -529,6 +533,7 @@ hc(Mod) ->
case code:is_module_native(Mod) of
true -> ok;
false ->
+ %% io:format(" ~s", [Mod]),
{ok, Mod} = hipe:c(Mod),
ok
end.
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 9989118671..216e06219c 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -100,10 +100,7 @@ ets_dict_to_dict(Table) ->
ets:foldl(Fold, dict:new(), Table).
ets_set_is_element(Key, Table) ->
- case ets:lookup(Table, Key) of
- [] -> false;
- _ -> true
- end.
+ ets:lookup(Table, Key) =/= [].
ets_set_insert_set(Set, Table) ->
ets_set_insert_list(sets:to_list(Set), Table).
@@ -116,7 +113,7 @@ ets_set_to_set(Table) ->
ets:foldl(Fold, sets:new(), Table).
ets_read_concurrent_table(Name) ->
- ets:new(Name,[{read_concurrency, true}]).
+ ets:new(Name, [{read_concurrency, true}]).
%%--------------------------------------------------------------------
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 0b932d5a1f..157c951f77 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -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_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 0ef008bc58..6956850f1a 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -2769,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_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 0df003a035..17a292a7d6 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -1543,12 +1543,11 @@ get_bif_constr({erlang, '==', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
mk_constraint(Arg1, sub, ArgV1),
mk_constraint(Arg2, sub, ArgV2)]);
get_bif_constr({erlang, element, 2} = _BIF, Dst, Args,
- #state{cs = Constrs} = State) ->
+ #state{cs = Constrs, opaques = Opaques}) ->
GenType = erl_bif_types:type(erlang, element, 2),
case t_is_none(GenType) of
true -> ?debug("Bif: ~w failed\n", [_BIF]), throw(error);
false ->
- Opaques = State#state.opaques,
Fun = fun(Map) ->
[I, T] = ATs = lookup_type_list(Args, Map),
ATs2 = case lists:member(T, Opaques) of
@@ -2582,19 +2581,8 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
end
end;
enter_type(Key, Val, Map) ->
- ?debug("Entering ~s :: ~s\n", [format_type(Key), format_type(Val)]),
KeyName = t_var_name(Key),
- case t_is_any(Val) of
- true ->
- erase_type(KeyName, Map);
- false ->
- LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
- case dict:find(KeyName, Map) of
- {ok, LimitedVal} -> Map;
- {ok, _} -> map_store(KeyName, LimitedVal, Map);
- error -> map_store(KeyName, LimitedVal, Map)
- end
- end.
+ enter_type(KeyName, Val, Map).
enter_type_lists([Key|KeyTail], [Val|ValTail], Map) ->
Map1 = enter_type(Key, Val, Map),
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 8046b48838..a4c4d37a0f 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -219,15 +219,18 @@ get_record_and_type_info([], _Module, Records, RecDict) ->
end.
add_new_type(TypeOrOpaque, Name, TypeForm, ArgForms, Module, RecDict) ->
- case erl_types:type_is_defined(TypeOrOpaque, Name, RecDict) of
+ Arity = length(ArgForms),
+ case erl_types:type_is_defined(TypeOrOpaque, Name, Arity, RecDict) of
true ->
- throw({error, flat_format("Type ~s already defined\n", [Name])});
+ Msg = flat_format("Type ~s/~w already defined\n", [Name, Arity]),
+ throw({error, Msg});
false ->
ArgTypes = [erl_types:t_from_form(X) || X <- ArgForms],
case lists:all(fun erl_types:t_is_var/1, ArgTypes) of
true ->
ArgNames = [erl_types:t_var_name(X) || X <- ArgTypes],
- dict:store({TypeOrOpaque, Name}, {Module, TypeForm, ArgNames}, RecDict);
+ dict:store({TypeOrOpaque, Name, Arity},
+ {Module, TypeForm, ArgNames}, RecDict);
false ->
throw({error, flat_format("Type declaration for ~w does not "
"have variables as parameters", [Name])})
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/inets b/lib/dialyzer/test/r9c_SUITE_data/results/inets
index 773525eb7f..d377f34978 100644
--- a/lib/dialyzer/test/r9c_SUITE_data/results/inets
+++ b/lib/dialyzer/test/r9c_SUITE_data/results/inets
@@ -1,5 +1,5 @@
-ftp.erl:1243: The pattern {'ok', {N, Bytes}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+ftp.erl:1243: The pattern {'ok', {N, Bytes}} can never match the type 'eof' | {'error',atom() | {'no_translation','unicode','latin1'}} | {'ok',binary() | string()}
ftp.erl:640: The pattern {'closed', _Why} can never match the type 'perm_fname_not_allowed' | 'perm_neg_compl' | 'perm_no_space' | 'pos_compl' | 'pos_interm' | 'pos_interm_acct' | 'trans_neg_compl' | 'trans_no_space' | {'error' | 'perm_fname_not_allowed' | 'perm_neg_compl' | 'perm_no_space' | 'pos_compl' | 'pos_interm' | 'pos_interm_acct' | 'pos_prel' | 'trans_neg_compl' | 'trans_no_space',atom() | [any()] | {'invalid_server_response',[any(),...]}}
http.erl:117: The pattern {'error', Reason} can never match the type #req_headers{connection::[45 | 97 | 101 | 105 | 107 | 108 | 112 | 118,...],content_length::[48,...],other::[{_,_}]}
http.erl:138: Function close_session/2 will never be called
@@ -31,10 +31,10 @@ httpd_sup.erl:92: The variable Else can never match since previous clauses compl
mod_auth.erl:559: The pattern {'error', Reason} can never match the type {_,integer(),maybe_improper_list(),_}
mod_auth_dets.erl:120: The call lists:foreach(fun((_) -> 'true' | {'error','no_such_group' | 'no_such_group_member'}),{'ok',[any()]}) will never return since it differs in the 2nd argument from the success typing arguments: (fun((_) -> any()),[any()])
mod_auth_plain.erl:100: The variable _ can never match since previous clauses completely covered the type {'ok',[any()]}
-mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [any()]
-mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [any()]
+mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [[any()]]
+mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [[any()]]
mod_cgi.erl:372: The pattern {'http_response', NewAccResponse} can never match the type 'ok'
-mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom() | {'no_translation',binary()})) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
mod_dir.erl:72: The pattern {'error', Reason} can never match the type {'ok',[[[any()] | char()],...]}
mod_get.erl:135: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]>
mod_head.erl:80: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]>
@@ -47,9 +47,9 @@ mod_include.erl:692: The pattern <{'read', Reason}, Info, Path> can never match
mod_include.erl:706: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]>
mod_include.erl:716: Function read_error/3 will never be called
mod_include.erl:719: Function read_error/4 will never be called
-mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:433: The variable Other can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:585: The variable _ can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:608: The variable _ can never match since previous clauses completely covered the type [any()]
-mod_security_server.erl:641: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:433: The variable Other can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:585: The variable _ can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:608: The variable _ can never match since previous clauses completely covered the type [tuple()]
+mod_security_server.erl:641: The variable _ can never match since previous clauses completely covered the type [tuple()]
uri.erl:146: The pattern {'error', Error} can never match since previous clauses completely covered the type {_,{[],[]}}
diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/mnesia b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia
index b397d37523..b73943422a 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',_,_}
@@ -21,8 +20,8 @@ mnesia_frag_hash.erl:24: Callback info about the mnesia_frag_hash behaviour is n
mnesia_frag_old_hash.erl:23: Callback info about the mnesia_frag_hash behaviour is not available
mnesia_index.erl:52: The call mnesia_lib:other_val(Var::{_,'commit_work' | 'index' | 'setorbag' | 'storage_type' | {'index',_}},_ReASoN_::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
mnesia_lib.erl:1028: The pattern {'EXIT', Reason} can never match the type [any()] | {'error',_}
-mnesia_lib.erl:957: The pattern {'ok', {0, _}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
-mnesia_lib.erl:959: The pattern {'ok', {_, Bin}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+mnesia_lib.erl:957: The pattern {'ok', {0, _}} can never match the type 'eof' | {'error',atom() | {'no_translation','unicode','latin1'}} | {'ok',binary() | string()}
+mnesia_lib.erl:959: The pattern {'ok', {_, Bin}} can never match the type 'eof' | {'error',atom() | {'no_translation','unicode','latin1'}} | {'ok',binary() | string()}
mnesia_loader.erl:36: The call mnesia_lib:other_val(Var::{_,'access_mode' | 'cstruct' | 'db_nodes' | 'setorbag' | 'snmp' | 'storage_type'},Reason::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
mnesia_locker.erl:1017: Function system_terminate/4 has no local return
mnesia_log.erl:707: The test {'error',{[1..255,...],[any(),...]}} | {'ok',_} == atom() can never evaluate to 'true'
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/extract_translations b/lib/dialyzer/test/race_SUITE_data/results/extract_translations
index 62aa1aa511..295404bfed 100644
--- a/lib/dialyzer/test/race_SUITE_data/results/extract_translations
+++ b/lib/dialyzer/test/race_SUITE_data/results/extract_translations
@@ -1,5 +1,5 @@
-extract_translations.erl:140: The call ets:insert('files',{atom() | binary() | [atom() | [any()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | [any()] | char()]) call in extract_translations.erl on line 135
+extract_translations.erl:140: The call ets:insert('files',{atom() | [atom() | [any()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | [atom() | [any()] | char()]) call in extract_translations.erl on line 135
extract_translations.erl:146: The call ets:insert('translations',{_,[]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('translations',Str::any()) call in extract_translations.erl on line 126
-extract_translations.erl:152: The call ets:insert('files',{atom() | binary() | [atom() | [any()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | [any()] | char()]) call in extract_translations.erl on line 148
+extract_translations.erl:152: The call ets:insert('files',{atom() | [atom() | [any()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | [atom() | [any()] | char()]) call in extract_translations.erl on line 148
extract_translations.erl:154: The call ets:insert('translations',{_,[]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('translations',Str::any()) call in extract_translations.erl on line 126
diff --git a/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify b/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
index 06dc0d63ee..91ed552eec 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
+++ b/lib/dialyzer/test/small_SUITE_data/results/cerl_hipeify
@@ -1,4 +1,3 @@
-cerl_hipeify.erl:370: Function will never be called
cerl_hipeify.erl:370: Guard test fun((none()) -> no_return()) =:= F::{_,_,_} | {_,_,_,_} | {_,_,_,_,_} | {_,_,_,_,_,_} | {_,_,_,_,_,_,_} can never succeed
cerl_hipeify.erl:641: Function env__new_function_name/2 will never be called
diff --git a/lib/dialyzer/test/small_SUITE_data/results/comm_layer b/lib/dialyzer/test/small_SUITE_data/results/comm_layer
deleted file mode 100644
index cb4bf14eb4..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/results/comm_layer
+++ /dev/null
@@ -1,2 +0,0 @@
-
-comm_layer.erl:76: Invalid type specification for function 'comm_layer_dir.comm_layer':this/0. The success typing is () -> {_,integer(),pid()}
diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
index 4850f3ff0c..173ff3a9f1 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
@@ -5,18 +5,18 @@ contracts_with_subtypes.erl:108: The call contracts_with_subtypes:rec_arg({'a',{
contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'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: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',_} | {'ok',_,binary() | string()}
-contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,binary() | string()}
+contracts_with_subtypes.erl:142: The pattern 1 can never match the type string()
+contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()}
+contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,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}
contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X}
contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X}
-contracts_with_subtypes.erl:202: The pattern 1 can never match the type binary() | string()
-contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,binary() | string()}
-contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,binary() | string()}
-contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,binary() | string()}
-contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,binary() | string()}
+contracts_with_subtypes.erl:202: The pattern 1 can never match the type string()
+contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,string()}
+contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,string()}
+contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,string()}
+contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,string()}
contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return
contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed')
contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something'
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..6f189099ed
--- /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 (_) -> [[{_,_}]]
diff --git a/lib/dialyzer/test/small_SUITE_data/results/flatten b/lib/dialyzer/test/small_SUITE_data/results/flatten
index 8aa44dd002..0bd866770c 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/flatten
+++ b/lib/dialyzer/test/small_SUITE_data/results/flatten
@@ -1,2 +1,2 @@
-flatten.erl:17: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+flatten.erl:17: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom() | {'no_translation',binary()})) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
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/results/types_arity b/lib/dialyzer/test/small_SUITE_data/results/types_arity
new file mode 100644
index 0000000000..02641bd167
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/types_arity
@@ -0,0 +1,2 @@
+
+types_arity.erl:16: Invalid type specification for function types_arity:test2/0. The success typing is () -> {'node','a','nil','nil'}
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_acceptor.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_acceptor.erl
deleted file mode 100644
index 2ca1468911..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_acceptor.erl
+++ /dev/null
@@ -1,119 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_acceptor.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description : Acceptor
-%%% This module accepts new connections and starts corresponding
-%%% comm_connection processes.
-%%%
-%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id $
--module(comm_layer_dir.comm_acceptor).
-
--export([start_link/1, init/2]).
-
--import(config).
--import(gen_tcp).
--import(inet).
--import(log).
--import(lists).
--import(process_dictionary).
-
-start_link(InstanceId) ->
- Pid = spawn_link(comm_layer_dir.comm_acceptor, init, [InstanceId, self()]),
- receive
- {started} ->
- {ok, Pid}
- end.
-
-init(InstanceId, Supervisor) ->
- process_dictionary:register_process(InstanceId, acceptor, self()),
- erlang:register(comm_layer_acceptor, self()),
- log:log(info,"[ CC ] listening on ~p:~p", [config:listenIP(), config:listenPort()]),
- LS = case config:listenIP() of
- undefined ->
- open_listen_port(config:listenPort(), first_ip());
- _ ->
- open_listen_port(config:listenPort(), config:listenIP())
- end,
- {ok, {_LocalAddress, LocalPort}} = inet:sockname(LS),
- comm_port:set_local_address(undefined, LocalPort),
- %io:format("this() == ~w~n", [{LocalAddress, LocalPort}]),
- Supervisor ! {started},
- server(LS).
-
-server(LS) ->
- case gen_tcp:accept(LS) of
- {ok, S} ->
- case comm_port:get_local_address_port() of
- {undefined, LocalPort} ->
- {ok, {MyIP, _LocalPort}} = inet:sockname(S),
- comm_port:set_local_address(MyIP, LocalPort);
- _ ->
- ok
- end,
- receive
- {tcp, S, Msg} ->
- {endpoint, Address, Port} = binary_to_term(Msg),
- % auto determine remote address, when not sent correctly
- NewAddress = if Address =:= {0,0,0,0} orelse Address =:= {127,0,0,1} ->
- case inet:peername(S) of
- {ok, {PeerAddress, _Port}} ->
- % io:format("Sent Address ~p\n",[Address]),
- % io:format("Peername is ~p\n",[PeerAddress]),
- PeerAddress;
- {error, _Why} ->
- % io:format("Peername error ~p\n",[Why]).
- Address
- end;
- true ->
- % io:format("Address is ~p\n",[Address]),
- Address
- end,
- NewPid = comm_connection:new(NewAddress, Port, S),
- gen_tcp:controlling_process(S, NewPid),
- inet:setopts(S, [{active, once}, {send_timeout, config:read(tcp_send_timeout)}]),
- comm_port:register_connection(NewAddress, Port, NewPid, S)
- end,
- server(LS);
- Other ->
- log:log(warn,"[ CC ] unknown message ~p", [Other])
- end.
-
-open_listen_port({From, To}, IP) ->
- open_listen_port(lists:seq(From, To), IP);
-open_listen_port([Port | Rest], IP) ->
- case gen_tcp:listen(Port, [binary, {packet, 4}, {reuseaddr, true},
- {active, once}, {ip, IP}]) of
- {ok, Socket} ->
- Socket;
- {error, Reason} ->
- log:log(error,"[ CC ] can't listen on ~p: ~p~n", [Port, Reason]),
- open_listen_port(Rest, IP)
- end;
-open_listen_port([], _) ->
- abort;
-open_listen_port(Port, IP) ->
- open_listen_port([Port], IP).
-
--include_lib("kernel/include/inet.hrl").
-
-first_ip() ->
- {ok, Hostname} = inet:gethostname(),
- {ok, HostEntry} = inet:gethostbyname(Hostname),
- erlang:hd(HostEntry#hostent.h_addr_list).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl
deleted file mode 100644
index 48cc50ae21..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl
+++ /dev/null
@@ -1,206 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_connection.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description : creates and destroys connections and represents the
-%%% endpoint of a connection where messages are received and
-%% send from/to the network.
-%%%
-%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
-%% @version $Id $
--module(comm_layer_dir.comm_connection).
-
--export([send/3, open_new/4, new/3, open_new_async/4]).
-
--import(config).
--import(gen_tcp).
--import(inet).
--import(io).
--import(io_lib).
--import(log).
--import(timer).
-
--include("comm_layer.hrl").
-
-%% @doc new accepted connection. called by comm_acceptor
-%% @spec new(inet:ip_address(), int(), socket()) -> pid()
-new(Address, Port, Socket) ->
- spawn(fun () -> loop(Socket, Address, Port) end).
-
-%% @doc open new connection
-%% @spec open_new(inet:ip_address(), int(), inet:ip_address(), int()) ->
-%% {local_ip, inet:ip_address(), int(), pid(), inet:socket()}
-%% | fail
-%% | {connection, pid(), inet:socket()}
-open_new(Address, Port, undefined, MyPort) ->
- Myself = self(),
- LocalPid = spawn(fun () ->
- case new_connection(Address, Port, MyPort) of
- fail ->
- Myself ! {new_connection_failed};
- Socket ->
- {ok, {MyIP, _MyPort}} = inet:sockname(Socket),
- Myself ! {new_connection_started, MyIP, MyPort, Socket},
- loop(Socket, Address, Port)
- end
- end),
- receive
- {new_connection_failed} ->
- fail;
- {new_connection_started, MyIP, MyPort, S} ->
- {local_ip, MyIP, MyPort, LocalPid, S}
- end;
-open_new(Address, Port, _MyAddress, MyPort) ->
- Owner = self(),
- LocalPid = spawn(fun () ->
- case new_connection(Address, Port, MyPort) of
- fail ->
- Owner ! {new_connection_failed};
- Socket ->
- Owner ! {new_connection_started, Socket},
- loop(Socket, Address, Port)
- end
- end),
- receive
- {new_connection_failed} ->
- fail;
- {new_connection_started, Socket} ->
- {connection, LocalPid, Socket}
- end.
-
-% ===============================================================================
-% @doc open a new connection asynchronously
-% ===============================================================================
--spec(open_new_async/4 :: (any(), any(), any(), any()) -> pid()).
-open_new_async(Address, Port, _MyAddr, MyPort) ->
- Pid = spawn(fun () ->
- case new_connection(Address, Port, MyPort) of
- fail ->
- comm_port:unregister_connection(Address, Port),
- ok;
- Socket ->
- loop(Socket, Address, Port)
- end
- end),
- Pid.
-
-
-send({Address, Port, Socket}, Pid, Message) ->
- BinaryMessage = term_to_binary({deliver, Pid, Message}),
- SendTimeout = config:read(tcp_send_timeout),
- {Time, Result} = timer:tc(gen_tcp, send, [Socket, BinaryMessage]),
- if
- Time > 1200 * SendTimeout ->
- log:log(error,"[ CC ] send to ~p took ~p: ~p",
- [Address, Time, inet:getopts(Socket, [keep_alive, send_timeout])]);
- true ->
- ok
- end,
- case Result of
- ok ->
- ?LOG_MESSAGE(erlang:element(1, Message), byte_size(BinaryMessage)),
- ok;
- {error, closed} ->
- comm_port:unregister_connection(Address, Port),
- close_connection(Socket);
- {error, _Reason} ->
- %log:log(error,"[ CC ] couldn't send to ~p:~p (~p)", [Address, Port, Reason]),
- comm_port:unregister_connection(Address, Port),
- close_connection(Socket)
- end.
-
-loop(fail, Address, Port) ->
- comm_port:unregister_connection(Address, Port),
- ok;
-loop(Socket, Address, Port) ->
- receive
- {send, Pid, Message} ->
- case send({Address, Port, Socket}, Pid, Message) of
- ok -> loop(Socket, Address, Port);
- _ -> ok
- end;
- {tcp_closed, Socket} ->
- comm_port:unregister_connection(Address, Port),
- gen_tcp:close(Socket);
- {tcp, Socket, Data} ->
- case binary_to_term(Data) of
- {deliver, Process, Message} ->
- Process ! Message,
- inet:setopts(Socket, [{active, once}]),
- loop(Socket, Address, Port);
- {user_close} ->
- comm_port:unregister_connection(Address, Port),
- gen_tcp:close(Socket);
- {youare, _Address, _Port} ->
- %% @TODO what do we get from this information?
- inet:setopts(Socket, [{active, once}]),
- loop(Socket, Address, Port);
- Unknown ->
- log:log(warn,"[ CC ] unknown message ~p", [Unknown]),
- inet:setopts(Socket, [{active, once}]),
- loop(Socket, Address, Port)
- end;
-
- {youare, _IP, _Port} ->
- loop(Socket, Address, Port);
-
- Unknown ->
- log:log(warn,"[ CC ] unknown message2 ~p", [Unknown]) ,
- loop(Socket, Address, Port)
- end.
-
-% ===============================================================================
-
--spec(new_connection(inet:ip_address(), integer(), integer()) -> inet:socket() | fail).
-new_connection(Address, Port, MyPort) ->
- case gen_tcp:connect(Address, Port, [binary, {packet, 4}, {nodelay, true}, {active, once},
- {send_timeout, config:read(tcp_send_timeout)}],
- config:read(tcp_connect_timeout)) of
- {ok, Socket} ->
- % send end point data
- case inet:sockname(Socket) of
- {ok, {MyAddress, _MyPort}} ->
- Message = term_to_binary({endpoint, MyAddress, MyPort}),
- gen_tcp:send(Socket, Message),
- case inet:peername(Socket) of
- {ok, {RemoteIP, RemotePort}} ->
- YouAre = term_to_binary({youare, RemoteIP, RemotePort}),
- gen_tcp:send(Socket, YouAre),
- Socket;
- {error, _Reason} ->
- %log:log(error,"[ CC ] reconnect to ~p because socket is ~p",
- % [Address, Reason]),
- close_connection(Socket),
- new_connection(Address, Port, MyPort)
- end;
- {error, _Reason} ->
- %log:log(error,"[ CC ] reconnect to ~p because socket is ~p",
- % [Address, Reason]),
- close_connection(Socket),
- new_connection(Address, Port, MyPort)
- end;
- {error, _Reason} ->
- %log:log(error,"[ CC ] couldn't connect to ~p:~p (~p)",
- %[Address, Port, Reason]),
- fail
- end.
-
-close_connection(Socket) ->
- spawn( fun () ->
- gen_tcp:close(Socket)
- end ).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.erl
deleted file mode 100644
index b7fdd183e1..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.erl
+++ /dev/null
@@ -1,83 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_layer.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description : Public interface to Communication Layer.
-%%% Generic functions to send messages.
-%%% Distinguishes on runtime whether the destination is in the
-%%% same Erlang virtual machine (use ! for sending) or on a remote
-%%% site (use comm_port:send()).
-%%%
-%%% Created : 04 Feb 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id $
--module(comm_layer_dir.comm_layer).
-
--author('[email protected]').
--vsn('$Id: comm_layer.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
-
--export([start_link/0, send/2, this/0, here/1]).
-
--import(io).
--import(util).
--import(log).
-
--include("comm_layer.hrl").
-
-
-% @TODO: should be ip
--type(process_id() :: {any(), integer(), pid()}).
-%%====================================================================
-%% public functions
-%%====================================================================
-
-%% @doc starts the communication port (for supervisor)
-%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
-start_link() ->
- comm_port_sup:start_link().
-
-%% @doc a process descriptor has to specify the erlang vm
-%% + the process inside. {IP address, port, pid}
-%% @type process_id() = {inet:ip_address(), int(), pid()}.
-%% @spec send(process_id(), term()) -> ok
-
-send({{_IP1, _IP2, _IP3, _IP4} = _IP, _Port, _Pid} = Target, Message) ->
- {MyIP,MyPort} = comm_port:get_local_address_port(),
- %io:format("send: ~p:~p -> ~p:~p(~p) : ~p\n", [MyIP, MyPort, _IP, _Port, _Pid, Message]),
- IsLocal = (MyIP == _IP) and (MyPort == _Port),
- if
- IsLocal ->
- ?LOG_MESSAGE(erlang:element(1, Message), byte_size(term_to_binary(Message))),
- _Pid ! Message;
- true ->
- comm_port:send(Target, Message)
- end;
-
-send(Target, Message) ->
- log:log(error,"[ CC ] wrong call to cs_send:send: ~w ! ~w", [Target, Message]),
- log:log(error,"[ CC ] stacktrace: ~w", [util:get_stacktrace()]),
- ok.
-
-%% @doc returns process descriptor for the calling process
--spec(this/0 :: () -> atom()).%process_id()).
-this() ->
- here(self()).
-
--spec(here/1 :: (pid()) -> process_id()).
-here(Pid) ->
- {LocalIP, LocalPort} = comm_port:get_local_address_port(),
- {LocalIP, LocalPort, Pid}.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.hrl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.hrl
deleted file mode 100644
index 54f31b7c55..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_layer.hrl
+++ /dev/null
@@ -1,29 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_layer.hrl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description :
-%%%
-%%% Created : 31 Jul 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id: comm_layer.hrl,v 1.1 2009/11/06 12:41:36 maria Exp $
--author('[email protected]').
--vsn('$Id: comm_layer.hrl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
-
-% enable logging of message statistics
-%-define(LOG_MESSAGE(TAG, SIZE), comm_layer.comm_logger:log(TAG, SIZE)).
--define(LOG_MESSAGE(TAG, SIZE), ok).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_logger.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_logger.erl
deleted file mode 100644
index b8882758af..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_logger.erl
+++ /dev/null
@@ -1,143 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_logger.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description :
-%%%
-%%% Created : 31 Jul 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id: comm_logger.erl,v 1.1 2009/11/06 12:41:36 maria Exp $
--module(comm_layer_dir.comm_logger).
-
--author('[email protected]').
--vsn('$Id: comm_logger.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
-
--behaviour(gen_server).
-
--import(gb_trees).
--import(gen_server).
-
-%% API
--export([start_link/0]).
-
--export([log/2, dump/0]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--record(state, {start, map}).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-%%--------------------------------------------------------------------
-%% Function: log(Tag, Size) -> ok
-%% Description: logs a message type with its size
-%%--------------------------------------------------------------------
-log(Tag, Size) ->
- gen_server:cast(?MODULE, {log, Tag, Size}).
-
-%%--------------------------------------------------------------------
-%% Function: dump() -> {gb_tree:gb_trees(), {Date, Time}}
-%% Description: gets the logging state
-%%--------------------------------------------------------------------
-dump() ->
- gen_server:call(?MODULE, {dump}).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([]) ->
- {ok, #state{start=erlang:now(), map=gb_trees:empty()}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call({dump}, _From, State) ->
- Reply = {State#state.map, State#state.start},
- {reply, Reply, State};
-handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast({log, Tag, Size}, State) ->
- case gb_trees:lookup(Tag, State#state.map) of
- none ->
- {noreply, State#state{map=gb_trees:insert(Tag, {Size, 1}, State#state.map)}};
- {value, {OldSize, OldCount}} ->
- {noreply, State#state{map=gb_trees:update(Tag, {Size + OldSize, OldCount + 1}, State#state.map)}}
- end;
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, _State) ->
- ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl
deleted file mode 100644
index e8169b4673..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl
+++ /dev/null
@@ -1,240 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_port.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description : Main CommLayer Interface
-%%% Maps remote addresses to comm_connection PIDs.
-%%%
-%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
-%% @version $Id $
--module(comm_layer_dir.comm_port).
-
--author('[email protected]').
--vsn('$Id: comm_port.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
-
--behaviour(gen_server).
-
--import(ets).
--import(gen_server).
--import(io).
--import(log).
-
--define(ASYNC, true).
-%-define(SYNC, true).
-
-%% API
--export([start_link/0,
- send/2,
- unregister_connection/2, register_connection/4,
- set_local_address/2, get_local_address_port/0]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
-%%====================================================================
-%% API
-%%====================================================================
-
-%% @doc
-%% @spec send({inet:ip_address(), int(), pid()}, term()) -> ok
--ifdef(ASYNC).
-send({Address, Port, Pid}, Message) ->
- gen_server:call(?MODULE, {send, Address, Port, Pid, Message}, 20000).
--endif.
--ifdef(SYNC).
-send({Address, Port, Pid}, Message) ->
- case ets:lookup(?MODULE, {Address, Port}) of
- [{{Address, Port}, {_LPid, Socket}}] ->
- comm_connection:send({Address, Port, Socket}, Pid, Message),
- ok;
- [] ->
- gen_server:call(?MODULE, {send, Address, Port, Pid, Message}, 20000)
- end.
--endif.
-
-
-%% @doc
-%% @spec unregister_connection(inet:ip_address(), int()) -> ok
-unregister_connection(Adress, Port) ->
- gen_server:call(?MODULE, {unregister_conn, Adress, Port}, 20000).
-
-%% @doc
-%% @spec register_connection(inet:ip_address(), int(), pid(), gen_tcp:socket()) -> ok | duplicate
-register_connection(Adress, Port, Pid, Socket) ->
- gen_server:call(?MODULE, {register_conn, Adress, Port, Pid, Socket}, 20000).
-
-%% @doc
-%% @spec set_local_address(inet:ip_address(), int()) -> ok
-set_local_address(Address, Port) ->
- gen_server:call(?MODULE, {set_local_address, Address, Port}, 20000).
-
-
-%% @doc
-%% @spec get_local_address_port() -> {inet:ip_address(),int()}
-get_local_address_port() ->
- case ets:lookup(?MODULE, local_address_port) of
- [{local_address_port, Value}] ->
- Value;
- [] ->
- undefined
- end.
-
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([]) ->
- ets:new(?MODULE, [set, protected, named_table]),
- {ok, ok}. % empty state.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call({send, Address, Port, Pid, Message}, _From, State) ->
- send(Address, Port, Pid, Message, State);
-
-handle_call({unregister_conn, Address, Port}, _From, State) ->
- ets:delete(?MODULE, {Address, Port}),
- {reply, ok, State};
-
-handle_call({register_conn, Address, Port, Pid, Socket}, _From, State) ->
- case ets:lookup(?MODULE, {Address, Port}) of
- [{{Address, Port}, _}] ->
- {reply, duplicate, State};
- [] ->
- ets:insert(?MODULE, {{Address, Port}, {Pid, Socket}}),
- {reply, ok, State}
- end;
-
-handle_call({set_local_address, Address, Port}, _From, State) ->
- ets:insert(?MODULE, {local_address_port, {Address,Port}}),
- {reply, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, _State) ->
- ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-
--ifdef(ASYNC).
-send(Address, Port, Pid, Message, State) ->
- {DepAddr,DepPort} = get_local_address_port(),
- if
- DepAddr == undefined ->
- open_sync_connection(Address, Port, Pid, Message, State);
- true ->
- case ets:lookup(?MODULE, {Address, Port}) of
- [{{Address, Port}, {ConnPid, _Socket}}] ->
- ConnPid ! {send, Pid, Message},
- {reply, ok, State};
- [] ->
- ConnPid = comm_connection:open_new_async(Address, Port,
- DepAddr, DepPort),
- ets:insert(?MODULE, {{Address, Port}, {ConnPid, undef}}),
- ConnPid ! {send, Pid, Message},
- {reply, ok, State}
- end
- end.
--endif.
-
--ifdef(SYNC).
-send(Address, Port, Pid, Message, State) ->
- case ets:lookup(?MODULE, {Address, Port}) of
- [{{Address, Port}, {_LPid, Socket}}] ->
- comm_connection:send({Address, Port, Socket}, Pid, Message),
- {reply, ok, State};
- [] ->
- open_sync_connection(Address, Port, Pid, Message, State)
- end.
--endif.
-
-
-open_sync_connection(Address, Port, Pid, Message, State) ->
- {DepAddr,DepPort} = get_local_address_port(),
- case comm_connection:open_new(Address, Port, DepAddr, DepPort) of
- {local_ip, MyIP, MyPort, MyPid, MySocket} ->
- comm_connection:send({Address, Port, MySocket}, Pid, Message),
- log:log(info,"[ CC ] this() == ~w", [{MyIP, MyPort}]),
- % set_local_address(t, {MyIP,MyPort}}),
- % register_connection(Address, Port, MyPid, MySocket),
- ets:insert(?MODULE, {local_address_port, {MyIP,MyPort}}),
- ets:insert(?MODULE, {{Address, Port}, {MyPid, MySocket}}),
- {reply, ok, State};
- fail ->
- % drop message (remote node not reachable, failure detector will notice)
- {reply, ok, State};
- {connection, LocalPid, NewSocket} ->
- comm_connection:send({Address, Port, NewSocket}, Pid, Message),
- ets:insert(?MODULE, {{Address, Port}, {LocalPid, NewSocket}}),
- % register_connection(Address, Port, LPid, NewSocket),
- {reply, ok, State}
- end.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port_sup.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port_sup.erl
deleted file mode 100644
index d7a25b14ab..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port_sup.erl
+++ /dev/null
@@ -1,88 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : comm_port_sup.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description :
-%%%
-%%% Created : 04 Feb 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id: comm_port_sup.erl,v 1.1 2009/11/06 12:41:36 maria Exp $
--module(comm_layer_dir.comm_port_sup).
-
--author('[email protected]').
--vsn('$Id: comm_port_sup.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
-
--behaviour(supervisor).
-
--import(supervisor).
--import(randoms).
--import(string).
--import(config).
-
--export([start_link/0, init/1]).
-
-%%====================================================================
-%% API functions
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the supervisor
-%%--------------------------------------------------------------------
-start_link() ->
- supervisor:start_link(?MODULE, []).
-
-%%====================================================================
-%% Supervisor callbacks
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
-%% ignore |
-%% {error, Reason}
-%% Description: Whenever a supervisor is started using
-%% supervisor:start_link/[2,3], this function is called by the new process
-%% to find out about restart strategy, maximum restart frequency and child
-%% specifications.
-%%--------------------------------------------------------------------
-init([]) ->
- InstanceId = string:concat("comm_port_", randoms:getRandomId()),
- CommPort =
- {comm_port,
- {comm_layer_dir.comm_port, start_link, []},
- permanent,
- brutal_kill,
- worker,
- []},
- CommAcceptor =
- {comm_acceptor,
- {comm_layer_dir.comm_acceptor, start_link, [InstanceId]},
- permanent,
- brutal_kill,
- worker,
- []},
- CommLogger =
- {comm_logger,
- {comm_layer_dir.comm_logger, start_link, []},
- permanent,
- brutal_kill,
- worker,
- []},
- {ok, {{one_for_all, 10, 1},
- [
- CommPort,
- CommLogger,
- CommAcceptor
- ]}}.
diff --git a/lib/dialyzer/test/small_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/pubsub/pubsub_api.erl b/lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_api.erl
deleted file mode 100644
index 85ea292077..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_api.erl
+++ /dev/null
@@ -1,99 +0,0 @@
-% Copyright 2007-2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : pubsub_api.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description : Publish API function
-%%%
-%%% Created : 17 Sep 2007 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2007-2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id $
--module(pubsub_dir.pubsub_api).
-
--author('[email protected]').
--vsn('$Id: pubsub_api.erl,v 1.1 2009/11/06 12:39:55 maria Exp $ ').
-
--export([publish/2, subscribe/2, unsubscribe/2, get_subscribers/1]).
-
--import(transstore.transaction_api).
--import(io).
--import(lists).
-
-%%====================================================================
-%% public functions
-%%====================================================================
-
-%% @doc publishs an event under a given topic.
-%% called e.g. from the java-interface
-%% @spec publish(string(), string()) -> ok
-publish(Topic, Content) ->
- Subscribers = get_subscribers(Topic),
- io:format("calling subscribers ~p~n", [Subscribers]),
- lists:foreach(fun (Subscriber) ->
- io:format("calling ~p~n", [Subscriber]),
- pubsub_publish:publish(Subscriber, Topic, Content)
- end,
- Subscribers),
- ok.
-
-%% @doc subscribes a url for a topic.
-%% called e.g. from the java-interface
-%% @spec subscribe(string(), string()) -> ok | {fail, term()}
-subscribe(Topic, URL) ->
- TFun = fun(TransLog) ->
- {{Success, _ValueOrReason} = Result, TransLog1} = transaction_api:read(Topic, TransLog),
- {Result2, TransLog2} = if
- Success == fail ->
- transaction_api:write(Topic, [URL], TransLog); %obacht: muss TransLog sein!
- true ->
- {value, Subscribers} = Result,
- transaction_api:write(Topic, [URL | Subscribers], TransLog1)
- end,
- if
- Result2 == ok ->
- {{ok, ok}, TransLog2};
- true ->
- {Result2, TransLog2}
- end
- end,
- transaction_api:do_transaction(TFun, fun (_) -> ok end, fun (X) -> {fail, X} end).
-
-%% @doc unsubscribes a url for a topic.
--spec(unsubscribe/2 :: (string(), string()) -> ok | {fail, any()}).
-unsubscribe(Topic, URL) ->
- TFun = fun(TransLog) ->
- {Subscribers, TransLog1} = transaction_api:read2(TransLog, Topic),
- case lists:member(URL, Subscribers) of
- true ->
- NewSubscribers = lists:delete(URL, Subscribers),
- TransLog2 = transaction_api:write2(TransLog1, Topic, NewSubscribers),
- {{ok, ok}, TransLog2};
- false ->
- {{fail, not_found}, TransLog}
- end
- end,
- transaction_api:do_transaction(TFun, fun (_) -> ok end, fun (X) -> {fail, X} end).
-
-%% @doc queries the subscribers of a query
-%% @spec get_subscribers(string()) -> [string()]
-get_subscribers(Topic) ->
- {Fl, _Value} = transaction_api:quorum_read(Topic),
- if
- Fl == fail -> %% Fl is either Fail or the Value/Subscribers
- [];
- true ->
- Fl
- end.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_publish.erl b/lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_publish.erl
deleted file mode 100644
index 601dbad74b..0000000000
--- a/lib/dialyzer/test/small_SUITE_data/src/pubsub/pubsub_publish.erl
+++ /dev/null
@@ -1,49 +0,0 @@
-% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%
-% Licensed under the Apache License, Version 2.0 (the "License");
-% you may not use this file except in compliance with the License.
-% You may obtain a copy of the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS,
-% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-% See the License for the specific language governing permissions and
-% limitations under the License.
-%%%-------------------------------------------------------------------
-%%% File : pubsub_publish.erl
-%%% Author : Thorsten Schuett <[email protected]>
-%%% Description : Publish function
-%%%
-%%% Created : 26 Mar 2008 by Thorsten Schuett <[email protected]>
-%%%-------------------------------------------------------------------
-%% @author Thorsten Schuett <[email protected]>
-%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
-%% @version $Id $
--module(pubsub_dir.pubsub_publish).
-
--author('[email protected]').
--vsn('$Id: pubsub_publish.erl,v 1.1 2009/11/06 12:39:55 maria Exp $ ').
-
--export([publish/3, publish_internal/3]).
-
--import(json).
--import(io).
--import(http).
--import(jsonrpc).
-
-%%====================================================================
-%% public functions
-%%====================================================================
-
-%% @doc publishs an event to a given url.
-%% @spec publish(string(), string(), string()) -> ok
-%% @todo use pool:pspawn
-publish(URL, Topic, Content) ->
- spawn(fun () -> pubsub_publish:publish_internal(URL, Topic, Content) end),
- ok.
-
-publish_internal(URL, Topic, Content) ->
- Res = jsonrpc:call(URL, [], {call, notify, [Topic, Content]}),
- io:format("~p ~p~n", [Res, URL]).
diff --git a/lib/dialyzer/test/small_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/types_arity.erl b/lib/dialyzer/test/small_SUITE_data/src/types_arity.erl
new file mode 100644
index 0000000000..4ddc986ea8
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/types_arity.erl
@@ -0,0 +1,20 @@
+-module(types_arity).
+
+-export([ test1/0
+ , test2/0
+ , test3/0
+ ]).
+
+-export_type([tree/0, tree/1]).
+
+-type tree(T) :: 'nil' | {'node', T, tree(T), tree(T)}.
+-type tree() :: tree(integer()).
+
+-spec test1() -> tree().
+test1() -> {node, 7, nil, nil}.
+
+-spec test2() -> tree().
+test2() -> {node, a, nil, nil}.
+
+-spec test3() -> tree(atom()).
+test3() -> {node, a, nil, nil}.
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/test/user_SUITE_data/results/wsp_pdu b/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
index a47b1f1f2c..d1f8f4caf2 100644
--- a/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
+++ b/lib/dialyzer/test/user_SUITE_data/results/wsp_pdu
@@ -6,7 +6,7 @@ wsp_pdu.erl:2403: The call wsp_pdu:d_date(Data1::binary()) will never return sin
wsp_pdu.erl:2406: Guard test is_integer(Sec::{[byte()] | byte() | {'long',binary()} | {'short',binary()},binary()}) can never succeed
wsp_pdu.erl:2408: The pattern {'short', Data2} can never match the type {[byte()] | byte() | {'long',binary()} | {'short',binary()},binary()}
wsp_pdu.erl:2755: Function parse_push_flag/1 has no local return
-wsp_pdu.erl:2756: The call erlang:integer_to_list(Value::[any()]) will never return since it differs in the 1st argument from the success typing arguments: (integer())
+wsp_pdu.erl:2756: The call erlang:integer_to_list(Value::[any()]) breaks the contract (Integer) -> string() when is_subtype(Integer,integer())
wsp_pdu.erl:2875: The call wsp_pdu:d_text_string(Data::byte()) will never return since it differs in the 1st argument from the success typing arguments: (binary())
wsp_pdu.erl:2976: The call wsp_pdu:d_q_value(QData::byte()) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>>)
wsp_pdu.erl:3336: The call wsp_pdu:encode_typed_field(Ver::any(),'Q-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index f344fe0604..bdb8692f8a 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.5.3
+DIALYZER_VSN = 2.6
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc
index a72ba2d75c..2f5834d359 100755
--- a/lib/diameter/bin/diameterc
+++ b/lib/diameter/bin/diameterc
@@ -4,7 +4,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
@@ -124,10 +124,10 @@ arg(["-i", Dir | Args], #argv{options = Opts} = A) ->
arg(["--name", Name | Args], #argv{options = Opts} = A) ->
arg(Args, A#argv{options = [{name, Name} | Opts]});
-
+
arg(["--prefix", Name | Args], #argv{options = Opts} = A) ->
arg(Args, A#argv{options = [{prefix, Name} | Opts]});
-
+
arg(["--inherits", Dict | Args], #argv{options = Opts} = A) ->
arg(Args, A#argv{options = Opts ++ [{inherits, Dict}]});
@@ -166,7 +166,7 @@ path_exists(File, Type) ->
_ ->
throw({"No such file: ~p", [File]})
end.
-
+
file_exists(File) ->
path_exists(File, regular).
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index 8ad38ba0d5..99a6680f12 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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
@@ -35,45 +35,17 @@ 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) \
- seealso.ent
-
-INTERNAL_HTML_FILES = $(TECHNICAL_DESCR_FILES:%.xml=$(HTMLDIR)/%.html)
-
-HTML_APP_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_EXTRA_FILES = $(XML_EXTRA_FILES:%.xml=$(HTMLDIR)/%.html)
-HTML_PART_FILES = $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-HTML_FILES = $(HTML_APP_FILES) $(HTML_EXTRA_FILES) $(HTML_PART_FILES)
+ $(XML_EXTRA_FILES)
INFO_FILE = ../../info
-HTML_REF_FILES = $(XML_REF_FILES:%.xml=$(HTMLDIR)/%.html)
-
-HTML_CHAPTER_FILES = $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-
-EXTRA_FILES = \
- $(DEFAULT_GIF_FILES) \
- $(DEFAULT_HTML_FILES) \
- $(HTML_REF_FILES) \
- $(HTML_CHAPTER_FILES)
-
MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1)
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
MAN4_FILES = $(XML_REF4_FILES:%.xml=$(MAN4DIR)/%.4)
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
STANDARD_DIR = ../standard
-STANDARDS =
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-
-XML_FLAGS +=
-DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -86,11 +58,11 @@ docs: pdf html man
ldocs: local_docs $(INDEX_TARGET)
-$(TOP_PDF_FILE): $(XML_FILES)
+$(PDF_FILE): $(XML_FILES)
-pdf: $(TOP_PDF_FILE)
+pdf: $(PDF_FILE)
-html: gifs $(HTML_REF_MAN_FILE)
+html: gifs $(HTMLDIR)/index.html
clean clean_docs: clean_pdf clean_html clean_man
rm -f errs core *~
@@ -120,7 +92,6 @@ depend: depend.mk
debug opt:
info:
- @echo "->Makefile<-"
@echo ""
@echo "INDEX_FILE = $(INDEX_FILE)"
@echo "INDEX_SRC = $(INDEX_SRC)"
@@ -139,13 +110,9 @@ info:
@echo "MAN3_FILES = $(MAN3_FILES)"
@echo "MAN4_FILES = $(MAN4_FILES)"
@echo ""
- @echo "HTML_FILES = $(HTML_FILES)"
- @echo "TOP_HTML_FILES = $(TOP_HTML_FILES)"
- @echo ""
@echo "DEFAULT_HTML_FILES = $(DEFAULT_HTML_FILES)"
@echo "DEFAULT_GIF_FILES = $(DEFAULT_GIF_FILES)"
@echo ""
- @echo ""
# ----------------------------------------------------
@@ -160,7 +127,7 @@ release_docs_spec: $(LOCAL)docs
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man1"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
$(INSTALL_DIR) "$(RELEASE_PATH)/man/man4"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
+ $(INSTALL_DATA) $(PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(HTMLDIR)/*.* "$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
$(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1"
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index b7669b760b..8e9ec06ff9 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -16,7 +16,7 @@
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -69,8 +69,8 @@ Incoming Diameter requests are communicated as callbacks to a
specified in the service configuration.</p>
<p>
-Beware the difference between <em>diameter</em> (not capitalised) and
-<em>Diameter</em> (capitalised).
+Beware the difference between <em>diameter</em> (not capitalized) and
+<em>Diameter</em> (capitalized).
The former refers to the Erlang application named diameter whose main
api is defined here, the latter to Diameter protocol in the sense of
&the_rfc;.</p>
@@ -188,7 +188,7 @@ Defaults to the value of the <c>alias</c> option if unspecified.</p>
<item>
<p>
Specifies whether or not the &app_pick_peer;
-application callback can modify the application state,
+application callback can modify the application state.
Defaults to <c>false</c> if unspecified.</p>
<note>
@@ -206,11 +206,13 @@ probably avoid it.</p>
<item>
<p>
Determines the manner in which incoming answer messages containing
-decode errors are handled.
+decode errors are handled.</p>
+
+<p>
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.
+<c>#diameter_packet{}</c> 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
@@ -224,6 +226,39 @@ question is as if a callback had taken place and returned
Defaults to <c>report</c> if unspecified.</p>
</item>
+<tag><c>{request_errors, answer_3xxx|answer|callback}</c></tag>
+<item>
+<p>
+Determines the manner in which incoming requests are handled when an
+error other than 3007, DIAMETER_APPLICATION_UNSUPPORTED (which cannot
+be associated with an application callback module), is detected.</p>
+
+<p>
+If <c>answer_3xxx</c> then requests are answered without a
+&app_handle_request; callback taking place.
+If <c>answer</c> then even 5xxx errors are answered without a
+callback unless the connection in question has configured the RFC 3588
+common dictionary as noted below.
+If <c>callback</c> then a &app_handle_request; callback always takes
+place and the return value determines the answer sent to the peer.</p>
+
+<p>
+Defaults to <c>answer_3xxx</c> if unspecified.</p>
+
+<note>
+<p>
+Answers sent by diameter set the E-bit in the Diameter Header.
+Since RFC 3588 allowed only 3xxx result codes in an
+<c>answer-message</c>, <c>answer</c> has the same semantics as
+<c>answer_3xxx</c> if the peer connection in question has configured
+the RFC 3588 common dictionary, <c>diameter_gen_base_rfc3588</c>.
+RFC 6733 allows both 3xxx and 5xxx result codes in an
+<c>answer-message</c> so a connection configured with the RFC 6733
+common dictionary, <c>diameter_gen_base_rfc6733</c>, does
+distinguish between <c>answer_3xxx</c> and <c>answer</c>.</p>
+</note>
+</item>
+
</taglist>
<marker id="call_opt"/>
@@ -488,16 +523,23 @@ candidates list.</p>
<marker id="service_event"/>
</item>
-
-<tag><c>service_event() = #diameter_event{}</c></tag>
+<tag><c>service_event() = #diameter_event{service = &service_name;,
+ info = &service_event_info;}</c></tag>
<item>
<p>
An event message sent to processes that have subscribed to these using
&subscribe;.</p>
+<marker id="service_event_info"/>
+</item>
+
+<tag><c>service_event_info() = term()</c></tag>
+
+<item>
+
<p>
-The <c>info</c> field of the event record can have one of the
-following types.</p>
+The <c>info</c> field of a &service_event; record.
+Can have one of the following types.</p>
<taglist>
@@ -527,16 +569,16 @@ Pkt = #diameter_packet{}
The RFC 3539 watchdog state machine has
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> event
+If a <c>#diameter_packet{}</c> 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 <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
+Note that a single <c>up</c> or <c>down</c> event for a given peer
+corresponds to multiple &app_peer_up; or &app_peer_down;
+callbacks, one 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
@@ -582,7 +624,7 @@ CB = &evaluable;
<p>
An incoming CER has been answered with the indicated result code or
discarded.
-<c>Caps</c> contains pairs of values for the the local node and remote
+<c>Caps</c> contains pairs of values for 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
@@ -600,7 +642,7 @@ Pkt = #diameter_packet{}
<p>
An incoming CER contained errors and has been answered with the
indicated result code.
-<c>Caps</c> contains only values for the the local node.
+<c>Caps</c> contains only values for the local node.
<c>Pkt</c> contains the CER in question.</p>
</item>
@@ -624,7 +666,7 @@ ResultCode = integer()
An incoming CEA has been rejected for the indicated reason.
An integer-valued <c>Result</c> indicates the result code sent
by the peer.
-<c>Caps</c> contains pairs of values for the the local node and remote
+<c>Caps</c> contains pairs of values for 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
@@ -640,7 +682,7 @@ Pkt = #diameter_packet{}
<p>
An incoming CEA contained errors and has been rejected.
-<c>Caps</c> contains only values for the the local node.
+<c>Caps</c> contains only values for the local node.
<c>Pkt</c> contains the CEA in question.</p>
</item>
@@ -667,11 +709,14 @@ Config = {connect|listen, [transport_opt()]}
An RFC 3539 watchdog state machine has changed state.</p>
</item>
-</taglist>
-
+<tag><c>any()</c></tag>
+<item>
<p>
For forward compatibility, a subscriber should be prepared to receive
info fields of forms other than the above.</p>
+</item>
+
+</taglist>
<marker id="service_name"/>
</item>
@@ -709,6 +754,15 @@ passed to &call;, while for an
incoming request the application identifier in the message
header determines the application, the identifier being specified in
the application's &dictionary; file.</p>
+
+<warning>
+<p>
+The capabilities advertised by a node must match its configured
+applications. In particular, <c>application</c> configuration must
+be matched by corresponding &capability; configuration, of
+Application-Id AVP's in particular.</p>
+</warning>
+
</item>
<tag><c>{restrict_connections, false
@@ -787,6 +841,16 @@ 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>
+
+<warning>
+<p>
+The capabilities advertised by a node must match its configured
+applications.
+In particular, setting <c>applications</c> on a transport typically
+implies having to set matching Application-Id AVP's in a
+&capabilities; tuple.</p>
+</warning>
+
</item>
<marker id="capabilities"/>
@@ -858,9 +922,8 @@ case the corresponding callbacks are applied until either all return
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 connecting transport, the timing of reconnection attempts is
+governed by &watchdog_timer; or &reconnect_timer; expiry.
For a listening transport, the peer determines the timing.</p>
<p>
@@ -877,7 +940,7 @@ 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
+in question, <c>Reason</c> indicating whether 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;,
@@ -947,6 +1010,42 @@ configured them.</p>
Defaults to a single callback returning <c>dpr</c>.</p>
</item>
+<marker id="length_errors"/>
+<tag><c>{length_errors, exit|handle|discard}</c></tag>
+<item>
+<p>
+Specifies how to deal with errors in the Message Length field of the
+Diameter Header in an incoming message.
+An error in this context is that the length is not at least 20 bytes
+(the length of a Header), is not a multiple of 4 (a valid length) or
+is not the length of the message in question, as received over the
+transport interface documented in &man_transport;.</p>
+
+<p>
+If <c>exit</c> then a warning report is emitted and the parent of the
+transport process in question exits, which causes the transport
+process itself to exit as described in &man_transport;.
+If <c>handle</c> then the message is processed as usual, a resulting
+&app_handle_request; or &app_handle_answer; callback (if one takes
+place) indicating the <c>5015</c> error (DIAMETER_INVALID_MESSAGE_LENGTH).
+If <c>discard</c> then the message in question is silently discarded.</p>
+
+<p>
+Defaults to <c>exit</c>.</p>
+
+<note>
+<p>
+The default value reflects the fact that a transport module for a
+stream-oriented transport like TCP may not be able to recover from a
+message length error since such a transport must use the Message
+Length header to divide the incoming byte stream into individual
+Diameter messages.
+An invalid length leaves it with no reliable way to rediscover message
+boundaries, which may result in the failure of subsequent messages.
+See &man_tcp; for the behaviour of that module.</p>
+</note>
+</item>
+
<marker id="reconnect_timer"/>
<tag><c>{reconnect_timer, Tc}</c></tag>
<item>
@@ -1026,6 +1125,30 @@ modules in order until one establishes a connection within the
corresponding timeout (see below) or all fail.</p>
</item>
+<marker id="watchdog_config"/>
+<tag><c>{watchdog_config, [{okay|suspect, non_neg_integer()}]}</c></tag>
+<item>
+<p>
+Specifies configuration that alters the behaviour of the watchdog
+state machine.
+On key <c>okay</c>, the non-negative number of answered DWR
+messages before transitioning from REOPEN to OKAY.
+On key <c>suspect</c>, the number of watchdog timeouts before
+transitioning from OKAY to SUSPECT when DWR is unanswered, or 0 to
+not make the transition.</p>
+
+<p>
+Defaults to <c>[{okay, 3}, {suspect, 1}]</c>.
+Not specifying a key is equivalent to specifying
+the default value for that key.</p>
+<warning>
+<p>
+The default value is as required by RFC 3539: changing it results
+in non-standard behaviour that should only be used to simulate
+misbehaving nodes during test.</p>
+</warning>
+</item>
+
<marker id="watchdog_timer"/>
<tag><c>{watchdog_timer, TwInit}</c></tag>
<item>
@@ -1733,7 +1856,7 @@ a service.</p>
It is not an error to subscribe to events from a service
that does not yet exist.
Doing so before adding transports is required to guarantee the
-reception of all related events.</p>
+reception of all transport-related events.</p>
</desc>
</func>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index f4db625c71..d0f1b22ebd 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -13,7 +13,7 @@
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -475,6 +475,7 @@ not selected.</p>
| discard
| {eval|eval_packet, Action, PostF}</v>
<v>Reply = {reply, &packet; | &message;}
+ | {answer_message, 3000..3999|5000..5999}
| {protocol_error, 3000..3999}</v>
<v>Opt = &mod_call_opt;</v>
<v>PostF = &mod_evaluable;</v>
@@ -509,14 +510,15 @@ Otherwise it contains the record representing the request as outlined
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.
-Note that the errors detected by diameter are all of the 5xxx series
-(Permanent Failures).
+The <c>errors</c> field specifies any results codes identifying errors
+found while decoding the request.
+This is used to set Result-Code and/or Failed-AVP in a returned
+answer unless the callback returns a <c>#diameter_packet{}</c>
+whose <c>errors</c> field is set to either a non-empty list of its
+own, in which case this list is used instead, or the atom <c>false</c>
+to disable any setting of Result-Code and Failed-AVP.
+Note that the errors detected by diameter are of the 3xxx
+and 5xxx series, Protocol Errors and Permanent Failures respectively.
The <c>errors</c> list is empty if the request has been received in
the relay application.</p>
@@ -544,24 +546,25 @@ preserved in the outgoing answer, appropriate values otherwise
being set by diameter.</p>
</item>
-<tag><c>{protocol_error, 3000..3999}</c></tag>
+<tag><c>{answer_message, 3000..3999|5000..5999}</c></tag>
<item>
<p>
Send an answer message to the peer containing the specified
-protocol error.
+Result-Code.
Equivalent to</p>
<pre>
{reply, ['answer-message' | Avps]
</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>
+Result-Code and (if the request contained one) Session-Id AVP's.</p>
<p>
-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>
+Returning a value other than 3xxx or 5xxx will cause the request
+process in question to fail, as will returning a 5xxx value if the
+peer connection in question has been configured with the RFC 3588
+common dictionary <c>diameter_gen_base_rfc3588</c>.
+(Since RFC 3588 only allows 3xxx values in an answer-message.)</p>
</item>
<tag><c>{relay, Opts}</c></tag>
@@ -614,11 +617,20 @@ containing the encoded binary.
The return value is ignored.</p>
</item>
+<tag><c>{protocol_error, 3000..3999}</c></tag>
+<item>
+<p>
+Equivalent to <c>{answer_message, 3000..3999}</c>.</p>
+</item>
+
</taglist>
+<note>
<p>
-Note that protocol errors detected by diameter will result in an
-answer message without <c>handle_request/3</c> being invoked.</p>
+Requests containing errors may be answered by diameter, without a
+callback taking place, depending on the value of the
+&mod_application_opt; <c>request_errors</c>.</p>
+</note>
</desc>
</func>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index 8b0687a22e..1034781ff2 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -16,7 +16,7 @@
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -76,14 +76,18 @@ 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
+The diameter application includes five dictionary modules
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
+<c>diameter_gen_base_rfc3588</c> and <c>diameter_gen_base_rfc6733</c>
+for the Diameter Common Messages application with application
+identifier 0,
+<c>diameter_gen_accounting</c> (for RFC 3588) and
+<c>diameter_gen_acct_rfc6733</c> for the Diameter Base Accounting
application with application identifier 3 and
<c>diameter_gen_relay</c> the Relay application with application
-identifier 0xFFFFFFFF.
+identifier 0xFFFFFFFF.</p>
+
+<p>
The Common Message and Relay applications are the only applications
that diameter itself has any specific knowledge of.
The Common Message application is used for messages that diameter
diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml
index 5e3fd5eaf1..df140b16b9 100644
--- a/lib/diameter/doc/src/diameter_sctp.xml
+++ b/lib/diameter/doc/src/diameter_sctp.xml
@@ -15,7 +15,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -81,7 +81,6 @@ and implements the behaviour documented in
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
diff --git a/lib/diameter/doc/src/diameter_soc.xml b/lib/diameter/doc/src/diameter_soc.xml
index 16f6b9d5bb..4f5419122f 100644
--- a/lib/diameter/doc/src/diameter_soc.xml
+++ b/lib/diameter/doc/src/diameter_soc.xml
@@ -4,12 +4,12 @@
%also;
]>
-<chapter>
+<chapter xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
<year>2011</year>
-<year>2012</year>
+<year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -80,6 +80,8 @@ just be unnecessary configuration of a value that it has no control over.</p>
</list>
+<xi:include href="diameter_soc_rfc6733.xml"/>
+
</section>
<!-- ===================================================================== -->
diff --git a/lib/diameter/doc/src/diameter_soc_rfc6733.xml b/lib/diameter/doc/src/diameter_soc_rfc6733.xml
new file mode 100644
index 0000000000..8d85569650
--- /dev/null
+++ b/lib/diameter/doc/src/diameter_soc_rfc6733.xml
@@ -0,0 +1,8692 @@
+<?xml version="1.0" encoding="latin1" ?>
+
+<!--
+
+<copyright>
+<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>
+
+-->
+
+<!DOCTYPE section SYSTEM "chapter.dtd" [
+ <!ENTITY gen_sctp '<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>'>
+ <!ENTITY gen_tcp '<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>'>
+ <!ENTITY service '<seealso marker="diameter#start_service-2">service</seealso>'>
+ <!ENTITY capabilities '<seealso marker="diameter#capability">capabilities</seealso>'>
+ <!ENTITY events '<seealso marker="diameter#service_event">events</seealso>'>
+ <!ENTITY nada '<p>No comment.</p>'>
+ <!ENTITY % also SYSTEM "seealso.ent" >
+ %also;
+]>
+
+<section>
+<title>Commentary</title>
+
+<p>
+A more detailed commentary on &the_rfc; follows.
+Its purpose is to (hopefully) clarify not only what is supported but
+how, given that semantics and features discussed in the RFC are not
+solely the responsibility of the diameter application:
+in many cases much depends on the configuration a user passes to
+diameter, the implementation of &man_app; callback modules in
+particular.</p>
+
+<p>
+Comments apply to all text following the preceding comment.
+Be sure to distinguish between capitalized <em>Diameter</em>, the
+protocol defined by the RFC, and lowercase <em>diameter</em>, the
+Erlang application to which the commentary applies.</p>
+
+<warning>
+<p>
+The commentary is not yet complete.
+Comments currently stop at chapter 4.</p>
+</warning>
+
+<pre>
+Fajardo, et al. Standards Track [Page 6]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+1. Introduction
+
+ 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 led to new demands on AAA
+ protocols.
+</pre>
+
+<p>
+Note that diameter implements the Diameter protocol as defined in
+&the_rfc;.
+It also supported the previous version of the protocol, as defined in
+RFC 3588, when there are differences.
+(Which will be noted below.)
+It does not support RADIUS.</p>
+
+<pre>
+
+ Network access requirements for AAA protocols are summarized in
+ Aboba, et al. [RFC2989]. These include:
+
+ Failover
+
+ [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.
+</pre>
+
+&nada;
+
+<pre>
+
+ Transmission-level security
+
+ 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) [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
+ transmission-level security, and enable both intra- and inter-
+ domain AAA deployments, Diameter provides support for TLS/TCP and
+ DTLS/SCTP. Security is discussed in Section 13.
+</pre>
+
+<p>
+Whether or not IPsec is used is transparent to diameter.</p>
+
+<p>
+The transport protocol used on a given peer connection is also
+transparent to diameter in that transport to diameter is simply a
+module that implements the transport protocol documented in
+&man_transport;.
+A diameter user configures this module as the &mod_transport_opt;
+<c>transport_module</c>.</p>
+
+<p>
+While a user can implement their own transport modules, diameter
+includes implementations for TCP and SCTP:
+&man_tcp; based on &gen_tcp; and &man_sctp; based on &gen_sctp;.
+The former supports TLS but the latter does not currently support
+DTLS.</p>
+
+<pre>
+
+ 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. Standards Track [Page 7]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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.
+</pre>
+
+&nada;
+
+<pre>
+
+ Server-initiated messages
+
+ 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
+ address this issue, support for server-initiated messages is
+ mandatory in Diameter.
+</pre>
+
+<p>
+A diameter user can both send and receive messages.</p>
+
+<pre>
+
+ 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
+ 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
+ devices and Diameter agents. This capability enables Diameter
+ support to be added to legacy networks, by addition of a gateway
+ or server speaking both RADIUS and Diameter.
+</pre>
+
+<p>
+RADIUS Attributes can be redefined as Diameter AVP's using diameter's
+&man_dict; interface but diameter provides no such definitions.</p>
+
+<pre>
+
+ In addition to addressing the above requirements, Diameter also
+ provides support for the following:
+
+ Capability negotiation
+
+ RADIUS does not support error messages, capability negotiation, or
+ 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
+ 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. Standards Track [Page 8]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Peer discovery and configuration
+
+ 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
+ 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
+ enables dynamic discovery of peers (see Section 5.2). Derivation
+ 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
+ feasible to implement it within embedded devices.
+</pre>
+
+&nada;
+
+<pre>
+
+1.1. Diameter Protocol
+
+ The Diameter base protocol provides the following facilities:
+
+ o Ability to exchange messages and deliver AVPs
+</pre>
+
+<p>
+There are two interfaces directly involved in message exchange when
+using diameter: the function &mod_call; for sending outgoing requests,
+and the application callback interface, documented in &man_app; for
+receiving incoming request and answers.</p>
+
+<pre>
+
+ o Capabilities negotiation
+</pre>
+
+<p>
+Capabilities negotiation is the responsibility of diameter:
+a user configures a diameter service and/or transport with
+&capabilities; to provide AVP values for CER and CEA messages but it
+is diameter itself that sends these messages.
+A user receives notification of a successful capabilities exchange by
+way of &app_peer_up; callbacks.</p>
+
+<pre>
+
+ o Error notification
+</pre>
+
+<p>
+A user can subscribe to &events;, using &mod_subscribe;, in order to
+receive notification of various failures.
+Errors in Diameter messaging are communicated via the application
+callbacks &app_handle_request;, &app_handle_answer; and
+&app_handle_error;.</p>
+
+
+<pre>
+
+ o Extensibility, required in [RFC2989], through addition of new
+ applications, commands, and AVPs
+</pre>
+
+<p>
+Support for applications, commands and AVP's is extensible using
+diameter's dictionary interface, as documented in &man_dict;.
+Dictionaries are compiled to Erlang encode/decode modules using
+&man_compile; or &man_make;.</p>
+
+<pre>
+
+ o Basic services necessary for applications, such as the handling of
+ user sessions or accounting
+</pre>
+
+<p>
+Compiled dictionaries are provided for the RFC 3588 and RFC 6733
+Diameter applications: common, base accounting and relay.
+Dictionaries for a number of standardized
+applications are provided in uncompiled form below the <c>examples</c>
+subdirectory of the diameter application directory.</p>
+
+<pre>
+
+ 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 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. Standards Track [Page 9]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+
+ 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
+ access [RFC4005]. It is also possible for the base protocol to be
+ extended for use in new applications, via the addition of new
+ commands or AVPs. The initial focus of Diameter was network access
+ and accounting applications. A truly generic AAA protocol used by
+ 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 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
+ 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
+ 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.
+
+ The Diameter protocol also supports server-initiated messages, such
+ as a request to abort service to a particular user.
+</pre>
+
+&nada;
+
+<pre>
+
+1.1.1. Description of the Document Set
+
+ The Diameter specification consists of an updated version of the base
+ protocol specification (this document) and the Transport Profile
+ [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.
+ 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 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
+
+ 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 [RFC2119].
+</pre>
+
+&nada;
+
+<pre>
+
+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 the implementation of
+ Diameter (RFC 3588). An overview of some the major changes are given
+ below.
+</pre>
+
+<p>
+RFC 6733 is not fully backwards compatible with RFC 3588.
+(For example, in what values of Result-Code values are permissible with
+the E-bit.)
+The implications of incompatibilities for diameter are noted where
+appropriate.</p>
+
+<pre>
+
+ 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.
+</pre>
+
+<p>
+&man_tcp; supports both methods of negotiating TLS:
+bootstrapping via Inband-Security and directly following connection
+establishment.</p>
+
+<pre>
+
+ o Deprecated the exchange of CER/CEA messages in the open state.
+ 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
+ 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.
+</pre>
+
+<p>
+Capabilities exchange in the open state is not supported: an incoming
+CER in the open state will cause diameter to ask the relevant
+transport process to terminate, which implies the loss of the peer
+connection in the case of &man_tcp; and &man_sctp;.</p>
+
+<p>
+Capabilities update, as defined by RFC 6737, is not yet supported.
+Support will require diameter to handle CUR/CUA in the same way that
+it handles CER/CEA.</p>
+
+<pre>
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 11]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ o Simplified security requirements. The use of a secured transport
+ for exchanging Diameter messages remains mandatory. However, TLS/
+ 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.
+</pre>
+
+<p>
+The End-to-End security framework is not supported since it's use is
+largely unspecified: diameter will set the P-bit in outgoing AVP's as
+directed by the relevant dictionary and/or &app_prepare_request; or
+&app_handle_request; callbacks, but whether or not the P-bit is set on
+incoming AVP's has no consequence.</p>
+
+<p>
+As noted above, DTLS is not currently supported and whether or not
+IPsec is used is transparent to diameter.</p>
+
+<pre>
+
+ 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 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).
+
+ 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).
+</pre>
+
+<p>
+Peer discover is not currently supported: peers to which a node should
+connect must be configured.
+Connection requests are accepted from arbitrary peers but a
+&mod_transport_opt; <c>capabilities_cb</c> can be used to reject a
+peer based on an incoming CER or CEA.</p>
+
+<pre>
+
+ 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. Standards Track [Page 12]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ ABNF
+
+ Augmented Backus-Naur Form [RFC5234]. A metalanguage with its own
+ formal syntax and rules. It is based on the Backus-Naur Form and
+ 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
+ allocation.
+
+ Accounting Record
+
+ An accounting record represents a summary of the resource
+ consumption of a user over the entire session. Accounting servers
+ creating the accounting record may do so by processing interim
+ 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).
+
+ 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
+ accounting information.
+</pre>
+
+&nada;
+
+<pre>
+
+ Command Code Format (CCF)
+
+ A modified form of ABNF used to define Diameter commands (see
+ Section 3.2).
+</pre>
+
+<p>
+The <c>@messages</c> section of the &man_dict; format has the CCF as
+content.</p>
+
+<pre>
+
+ Diameter Agent
+
+ A Diameter Agent is a Diameter node that provides 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
+ 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
+ (NAS) and the Mobile IP Foreign Agent (FA).
+
+ Diameter Node
+
+ 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
+
+ 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
+ applications in addition to the base protocol.
+</pre>
+
+<p>
+A Diameter Node is implemented by configuring a service
+using &mod_start_service; and one or more transports using
+&mod_add_transport;.
+The service typically represents a Diameter Node but since
+capabilities can be configured on individual transports it's more
+accurate to say that the node is a collection of transports
+advertising the same Origin-Host.</p>
+
+<p>
+The role of a node (agent, client or server) is not something that's
+configured explicitly.
+Transports are either connecting or listening, depending on whether
+diameter should establish a peer connection and send CER or accept
+connections and receive CER, but the role a node implements depends
+largely on dictionary configuration and &man_app; callback
+implementation.</p>
+
+<pre>
+
+ Downstream
+
+ Downstream is used to identify the direction of a particular
+ 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 that serves the Home Realm.
+
+ Interim Accounting
+
+ An interim accounting message provides a snapshot of usage during
+ 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.
+
+ Multi-session
+
+ A multi-session represents a logical linking of several sessions.
+ Multi-sessions are tracked by using the Acct-Multi-Session-Id. An
+ example of a multi-session would be a Multi-link PPP bundle. Each
+ 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
+ purposes.
+
+ Proxy Agent or Proxy
+
+ In addition to forwarding requests and responses, proxies make
+ policy decisions relating to resource usage and provisioning.
+ 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
+ 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
+ routed or redirected. In RADIUS, realm names are not necessarily
+ piggybacked on the DNS namespace but may be independent of it.
+
+
+
+
+
+
+
+
+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. 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
+ AVPs and routing table entries. Since relays do not make policy
+ decisions, they do not examine or alter non-routing AVPs. As a
+ result, relays never originate messages, do not need to understand
+ 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
+ sessions in progress.
+
+ Redirect Agent
+
+ Rather than forwarding requests and responses between clients and
+ servers, redirect agents refer clients to servers and allow them
+ to communicate directly. Since redirect agents do not sit in the
+ forwarding path, they do not alter any AVPs transiting between
+ 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 relay
+ agents, redirect agents do not keep state with respect to sessions
+ or NAS resources.
+</pre>
+
+&nada;
+
+<pre>
+
+ Session
+
+ A session is a related progression of events devoted to a
+ particular activity. Diameter application documents provide
+ guidelines as to when a session begins and ends. All Diameter
+ packets with the same Session-Id are considered to be part of the
+ same session.
+</pre>
+
+<p>
+Sessions are not something that diameter is aware of.
+The function &mod_session_id; can be used to construct appropriate
+values for Session-Id AVP's but logic connecting events in the same
+session is the responsibility of the diameter user.</p>
+
+<pre>
+
+ 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
+ until expiration.
+
+
+
+Fajardo, et al. Standards Track [Page 16]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Sub-session
+
+ A sub-session represents a distinct service (e.g., QoS or data
+ characteristics) provided to a given session. These services may
+ happen concurrently (e.g., simultaneous voice and data transfer
+ during the same session) or serially. These changes in sessions
+ are tracked with the Accounting-Sub-Session-Id.
+
+ 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
+ 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 answer. A stateless agent is one that only maintains
+ transaction state.
+
+ Translation Agent
+
+ 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.
+
+ User
+
+ The entity or device requesting or using some resource, in support
+ of which a Diameter client has generated a request.
+</pre>
+
+&nada;
+
+<pre>
+
+1.3. Approach to Extensibility
+
+ The Diameter protocol is designed to be extensible, using several
+ mechanisms, including:
+
+ o Defining new AVP values
+
+ o Creating new AVPs
+
+ o Creating new commands
+
+ o Creating new applications
+
+
+
+
+Fajardo, et al. Standards Track [Page 17]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ From the point of view of extensibility, Diameter authentication,
+ authorization, and accounting applications are treated in the same
+ way.
+</pre>
+
+<p>
+Extensibility in diameter is by way of the dictionary interface
+documented in &man_dict;: a diameter user creates applications,
+commands and AVP's by implementing a new dictionary,
+compiling the dictionary to a codec module using &man_compile; or
+&man_make;, and configuring the resulting dictionary module on a
+service.
+The dictionary modules provided with diameter are all implemented in
+this manner.</p>
+
+<pre>
+ 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 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.
+</pre>
+
+<p>
+Reuse in dictionary files is achieved by way of the <c>@inherits</c>
+section.
+AVP's are inherited, commands are not.</p>
+
+<pre>
+
+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
+ AVP value. IANA considerations for these AVP values are discussed in
+ 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 RFC would require
+ IETF Review [RFC5226], whereas values for vendor-specific AVPs can be
+ allocated by the vendor.
+
+1.3.2. Creating New AVPs
+
+ A new AVP being defined MUST use one of the data types listed in
+ 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 other mechanisms.
+</pre>
+
+<p>
+Creating new AVP's is an issue for the dictionary designer, not
+diameter.</p>
+
+<pre>
+
+1.3.3. Creating New Commands
+
+ A new Command Code MUST be allocated when required AVPs (those
+ 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.
+
+
+
+Fajardo, et al. Standards Track [Page 18]
+
+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 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 Command Codes are discussed in
+ Section 3.1.
+</pre>
+
+<p>
+Creating new commands is an issue for the dictionary designer, not
+diameter.</p>
+
+<pre>
+
+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.
+</pre>
+
+<p>
+Creating new applications is an issue for the dictionary designer,
+not diameter.</p>
+
+<p>
+An application's Application Id is specified in the <c>@id</c> section
+of a dictionary file.</p>
+
+<pre>
+
+ 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 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,
+ then a failure is generated (see Section 7).
+</pre>
+
+<p>
+The M-bit is set on outgoing AVP's as directed by the relevant
+dictionary.
+For incoming AVP's, an M-bit set on an AVP that isn't
+explicitly included in the definition of the command in question is
+interpreted as a 5001 error, DIAMETER_AVP_UNSUPPORTED, the
+consequences of which depend on the value of the &mod_application_opt;
+<c>answer_errors</c> or <c>request_errors</c>.</p>
+
+<pre>
+
+ It is the decision of the protocol designer when to develop a new
+ Diameter application rather than extending Diameter in other ways.
+ However, a new Diameter application MUST be created when one or more
+ of the following criteria are met:
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 19]
+
+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 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 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.
+
+ 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.
+</pre>
+
+&nada;
+
+<pre>
+
+2. Protocol Overview
+
+ The base Diameter protocol concerns itself with establishing
+ connections to peers, capabilities negotiation, how messages are sent
+ and routed through peers, and how the connections are eventually torn
+ down. The base protocol also defines certain rules that apply to all
+ message exchanges between Diameter nodes.
+
+ Communication between Diameter peers begins with one peer sending a
+ message to another Diameter peer. The set of AVPs included in the
+ message is determined by a particular Diameter application. One AVP
+ that is included to reference a user's session is the Session-Id.
+
+ 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
+
+
+
+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.
+
+ Session state (associated with a Session-Id) MUST be freed upon
+ receipt of the Session-Termination-Request, Session-Termination-
+ Answer, expiration of authorized service time in the Session-Timeout
+ AVP, and according to rules established in a particular Diameter
+ application.
+</pre>
+
+<p>
+Like Session-Id, session state is maintained by the diameter user:
+diameter has no session state of its own and does not interpret
+STR/STA in any way.</p>
+
+<pre>
+
+ The base Diameter protocol may be used by itself for accounting
+ applications. For authentication and authorization, it is always
+ extended for a particular application.
+
+ 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.,
+ 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
+ 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 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
+ includes accounting, and all Diameter applications.
+
+ Diameter proxies MUST support the base protocol, which includes
+ accounting. In addition, they MUST fully support each Diameter
+ application that is needed to implement proxied services, e.g.,
+ NASREQ and/or Mobile IPv4. A Diameter proxy MUST be referred to as
+ "Diameter X Proxy" where X is the application which it supports, and
+ not a "Diameter Proxy".
+
+</pre>
+
+&nada;
+
+<pre>
+
+
+
+
+
+
+
+
+
+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 [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.
+</pre>
+
+<p>
+Which port a transport connects to or listens on is a matter of
+configuration.
+Both &man_tcp; and &man_sctp; will default to 3868 if no other value
+is specified.</p>
+
+<pre>
+
+ If the Diameter peer does not support receiving TLS/TCP and DTLS/SCTP
+ 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; 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
+ 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.
+</pre>
+
+<p>
+The &mod_service_opt; <c>restrict_connection</c> controls to what
+extent a diameter service allows multiple connections to the same
+peer.
+(As identified by the value of Origin-Host received from it
+during capabilities exchange.)</p>
+
+<pre>
+
+ When no transport connection exists with a peer, an attempt to
+ 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.
+
+</pre>
+
+<p>
+The frequency of reconnection attempts is configured with the
+&mod_transport_opt; <c>reconnect_timer</c> and
+<c>watchdog_timer</c>.</p>
+
+<pre>
+
+ When connecting to a peer and either zero or more transports are
+ 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.
+</pre>
+
+<p>
+The order in which different transports are attempted depends on the
+order of &mod_transport_opt; <c>transport_module</c> and
+<c>transport_config</c> tuples in transport configuration.</p>
+
+<pre>
+
+
+
+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
+ not reachable, subject to security policy on trusting such messages.
+ Further guidance regarding the treatment of ICMP errors can be found
+ in [RFC5927] and [RFC5461]. Diameter implementations SHOULD also be
+ able to interpret a reset from the transport and timed-out connection
+ attempts. If Diameter receives data from the lower layer that cannot
+ be parsed or identified as a Diameter error made by the peer, the
+ stream is compromised and cannot be recovered. The transport
+ connection MUST be closed using a RESET call (send a TCP RST bit) or
+ an SCTP ABORT message (graceful closure is compromised).
+</pre>
+
+<p>
+ICMP messages and other transport-level errors aren't directly
+visible to diameter but transport implementations like &man_tcp; and
+&man_sctp; propagate these as terminating transport processes.</p>
+
+<pre>
+
+2.1.1. SCTP Guidelines
+
+ 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 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.
+</pre>
+
+<p>
+&man_sctp; allows the sender to specify a stream number explicitly.
+The stream on which an incoming message is received it passed to
+&app_handle_request; and &app_handle_answer; callbacks as
+<c>transport_data</c> in a <c>#diameter_packet{}</c>.</p>
+
+<p>
+Ordered or unordered delivery can be configured per transport.</p>
+
+<pre>
+
+ Out-of-order delivery has special concerns during a connection
+ establishment and termination. When a connection is established, the
+ responder side sends a CEA message and moves to R-Open state as
+ specified in Section 5.6. If an application message is sent shortly
+ after the CEA and delivered out-of-order, the initiator side, still
+ in Wait-I-CEA state, will discard the application message and close
+ 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 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.
+</pre>
+
+<p>
+&man_sctp; does not currently allow the user to switch between ordered
+and unordered delivery, or to specify the manner of sending per
+message: one or the other must be configured, the defaults being
+ordered.</p>
+
+<pre>
+
+ 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. Standards Track [Page 23]
+
+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.
+
+ 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.
+</pre>
+
+&nada;
+
+<pre>
+
+2.2. Securing Diameter Messages
+
+ Connections between Diameter peers SHOULD be protected by TLS/TCP and
+ DTLS/SCTP. All Diameter base protocol implementations MUST support
+ 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 one of TLS, DTLS, or IPsec.
+</pre>
+
+<p>
+As noted above, DTLS is not currently supported and IPsec usage is
+transparent to diameter.
+Security is not enforced by diameter.</p>
+
+<pre>
+
+2.3. Diameter Application Compliance
+
+ Application Ids are advertised during the capabilities exchange phase
+ (see Section 5.3). Advertising support of an application implies
+ that the sender supports the functionality specified in the
+ respective Diameter application specification.
+
+ 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 CCF syntax specification
+ allows for it. Please refer to Section 11.1.1 for details.
+</pre>
+
+&nada;
+
+<pre>
+
+2.4. Application Identifiers
+
+ 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.
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 24]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ The following Application Id values are defined:
+
+ Diameter common message 0
+ Diameter base accounting 3
+ Relay 0xffffffff
+</pre>
+
+<p>
+These applications are implemented in the dictionary modules
+<c>diameter_gen_base_rfc6733</c>, <c>diameter_gen_acct_rfc6733</c> and
+<c>diameter_relay</c> respectively.
+There are also RFC 3588 versions or the common and accounting
+dictionaries: <c>diameter_gen_base_rfc3588</c> and
+<c>diameter_base_accounting</c>.
+(The inconsistent naming is historical.)
+Dictionary modules are configured using the &mod_application_opt;
+<c>dictionary</c>.</p>
+
+<pre>
+ 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
+ message. If none can be found, an error message is returned with the
+ Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
+</pre>
+
+&nada;
+
+<pre>
+
+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.
+
+ 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 that exists between the
+ Diameter client and the Diameter server; it is identified via the
+ Session-Id AVP.
+
+ +--------+ +-------+ +--------+
+ | Client | | Relay | | Server |
+ +--------+ +-------+ +--------+
+ &lt;----------> &lt;---------->
+ peer connection A peer connection B
+
+ &lt;----------------------------->
+ User session x
+
+ 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
+ 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.
+
+
+
+
+Fajardo, et al. Standards Track [Page 25]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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).
+</pre>
+
+<p>
+As noted above, diameter is not involved in session management.
+This is the responsibility of the diameter user.</p>
+
+<pre>
+
+2.6. Peer Table
+
+ 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
+
+ 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 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
+
+ Specifies the time at which dynamically discovered peer table
+ 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).
+</pre>
+
+<p>
+The Peer Table is not directly accessible to the diameter user.
+Information about connected peers can be retrieved using
+&mod_service_info;.</p>
+
+<pre>
+
+
+
+Fajardo, et al. Standards Track [Page 26]
+
+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). Each routing
+ table entry contains the following fields:
+
+ Realm Name
+
+ 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
+ can have a different destination based on the Application Id in
+ 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.
+
+ 2. RELAY - All Diameter messages that fall within this category
+ 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.
+
+ 3. PROXY - All Diameter messages that fall within this category
+ MUST be routed to a next Diameter entity that is indicated by
+ the identifier described below. The local server MAY apply
+ its local policies to the message by including new AVPs to the
+ message prior to routing. See Section 6.1.9 for proxying
+ guidelines.
+
+ 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 redirection guidelines.
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 27]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Server Identifier
+
+ 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 to which the message MUST be redirected.
+
+ Static or Dynamic
+
+ Specifies whether a route entry was statically configured or
+ dynamically discovered.
+
+ Expiration Time
+
+ Specifies the time at which a dynamically discovered route table
+ 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.
+ Agents do not need to support all modes of operation in order to
+ 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
+ advertised itself as a relay or proxy agent. Otherwise, an error is
+ returned with the Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
+</pre>
+
+<p>
+Routing does not need specific support in diameter: a user can
+maintain their own routing table if desired and implement any desired
+routing in &man_app; callbacks.
+However, it may be convenient to add more specific routing support to
+diameter in the future.</p>
+
+<pre>
+
+2.8. Role of Diameter Agents
+
+ In addition to clients and servers, the Diameter protocol introduces
+ relay, proxy, redirect, and translation agents, each of which is
+ defined in Section 1.2. Diameter agents are useful for several
+ reasons:
+</pre>
+
+<p>
+An noted above, the role a node plays is largely a question of
+configuration and &man_app; callback implementation.</p>
+
+<pre>
+
+ o They can distribute administration of systems to a configurable
+ grouping, including the maintenance of security associations.
+
+
+
+
+Fajardo, et al. Standards Track [Page 28]
+
+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.
+
+ o A complex network will have multiple authentication sources, they
+ can sort requests and forward towards the correct target.
+
+ 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
+ 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
+ answer. A stateless agent is one that only maintains transaction
+ state.
+
+ The Proxy-Info AVP allows stateless agents to add local state to a
+ Diameter request, with the guarantee that the same state will be
+ present in the answer. However, the protocol's failover procedures
+ require that agents maintain a copy of pending requests.
+
+ 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 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.
+
+ Maintaining session state may be useful in certain applications, such
+ as:
+
+ o Protocol translation (e.g., RADIUS &lt;-> Diameter)
+
+ o Limiting resources authorized to a particular user
+
+ 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
+ others.
+</pre>
+
+&nada;
+
+<pre>
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 29]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+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., 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
+ (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 they do not modify any other portion of a message.
+ Relays SHOULD NOT maintain session state but MUST maintain
+ transaction state.
+
+ +------+ ---------> +------+ ---------> +------+
+ | | 1. Request | | 2. Request | |
+ | NAS | | DRL | | HMS |
+ | | 4. Answer | | 3. Answer | |
+ +------+ &lt;--------- +------+ &lt;--------- +------+
+ example.net example.net example.com
+
+ Figure 2: Relaying of Diameter messages
+
+ 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, the NAS performs a Diameter route lookup, using
+ "example.com" as the key, and determines that the message is to be
+ 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 the NAS using saved transaction
+ state.
+
+ 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.
+</pre>
+
+<p>
+Requests are relayed by returning a <c>relay</c> tuple from a
+&app_handle_request; callback.</p>
+
+<pre>
+
+
+
+Fajardo, et al. Standards Track [Page 30]
+
+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 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
+ they support.
+</pre>
+
+&nada;
+
+<pre>
+
+2.8.3. Redirect Agents
+
+ Redirect agents are useful in scenarios where the Diameter routing
+ configuration needs to be centralized. An example is a redirect
+ agent that provides services to all members of a consortium, but does
+ not wish to be burdened with relaying all messages between realms.
+ This scenario is advantageous since it does not require that the
+ consortium provide routing updates to its members when changes are
+ made to a member's infrastructure.
+
+ Since redirect agents do not relay messages, and only return an
+ answer with the information necessary for Diameter agents to
+ communicate directly, they do not modify messages. Since redirect
+ agents do not receive answer messages, they cannot maintain session
+ state.
+
+ 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. The DRL has a
+ default route configured to DRD, which is a redirect agent that
+ 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.
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 31]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ +------+
+ | |
+ | DRD |
+ | |
+ +------+
+ ^ |
+ 2. Request | | 3. Redirection
+ | | Notification
+ | v
+ +------+ ---------> +------+ ---------> +------+
+ | | 1. Request | | 4. Request | |
+ | NAS | | DRL | | HMS |
+ | | 6. Answer | | 5. Answer | |
+ +------+ &lt;--------- +------+ &lt;--------- +------+
+ example.net example.net example.com
+
+ Figure 3: Redirecting a Diameter Message
+
+ Since redirect agents do not perform any application-level
+ processing, they provide relaying services for all Diameter
+ applications; therefore, they MUST advertise the Relay Application
+ ID.
+</pre>
+
+&nada;
+
+<pre>
+
+2.8.4. Translation Agents
+
+ A translation agent is a device that provides translation between two
+ protocols (e.g., RADIUS&lt;->Diameter, TACACS+&lt;->Diameter). Translation
+ agents are likely to be used as aggregation servers to communicate
+ with a Diameter infrastructure, while allowing for the embedded
+ systems to be migrated at a slower pace.
+
+ Given that the Diameter protocol introduces the concept of long-lived
+ authorized sessions, translation agents MUST be session stateful and
+ MUST maintain transaction state.
+
+ Translation of messages can only occur if the agent recognizes the
+ application of a particular request; therefore, translation agents
+ MUST only advertise their locally supported applications.
+
+ +------+ ---------> +------+ ---------> +------+
+ | | RADIUS Request | | Diameter Request | |
+ | NAS | | TLA | | HMS |
+ | | RADIUS Answer | | Diameter Answer | |
+ +------+ &lt;--------- +------+ &lt;--------- +------+
+ example.net example.net example.com
+
+ Figure 4: Translation of RADIUS to Diameter
+</pre>
+
+&nada;
+
+<pre>
+
+
+
+
+Fajardo, et al. Standards Track [Page 32]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+2.9. Diameter Path Authorization
+
+ 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 and can be replay and integrity
+ protected.
+
+ 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
+ (CER/CEA) also MUST be carried out, in order to determine what
+ Diameter applications are supported by each peer. Diameter sessions
+ MUST be routed only through authorized nodes that have advertised
+ support for the Diameter application required by the session.
+
+ 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 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
+ request is acceptable. For example, administrators within the home
+ 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 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.
+
+ A home realm may also wish to check that each accounting request
+ message corresponds to a Diameter response authorizing the session.
+ Accounting requests without corresponding authorization responses
+ SHOULD be subjected to further scrutiny, as should accounting
+ requests indicating a difference between the requested and provided
+ service.
+
+ Forwarding of an authorization response is considered evidence of a
+ 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 that would violate those limits. By issuing an
+
+
+
+Fajardo, et al. Standards Track [Page 33]
+
+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
+ accounting requests for the alternate service instead.
+</pre>
+
+&nada;
+
+<pre>
+
+3. Diameter Header
+
+ A summary of the Diameter header format is shown below. The fields
+ are transmitted in network byte order.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Version | Message Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Command Flags | Command Code |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Application-ID |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Hop-by-Hop Identifier |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | End-to-End Identifier |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | AVPs ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-
+</pre>
+
+<p>
+The Diameter Header is represented by the <c>diameter_header</c>
+record defined in <c>diameter.hrl</c>.
+The <c>diameter_packet</c> record contains a <c>header</c> field whose
+value will be a decoded <c>#diameter_header{}</c> for incoming
+messages passed to &app_handle_request; and &app_handle_answer;
+callbacks.
+In the case of outgoing messages, diameter and the relevant
+dictionary populate the Diameter Header appropriately, although
+&app_prepare_request; and &app_handle_request; callbacks can modify
+header values.
+(Which can be useful in test.)</p>
+
+<pre>
+
+ Version
+
+ This Version field MUST be set to 1 to indicate Diameter Version
+ 1.
+
+ Message Length
+
+ 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.
+
+ 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|
+ +-+-+-+-+-+-+-+-+
+
+ R(equest)
+
+ If set, the message is a request. If cleared, the message is
+ an answer.
+
+ P(roxiable)
+
+ 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 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).
+
+ 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 has been sent again.
+ This flag MUST NOT be set in answer messages.
+
+ r(eserved)
+
+ These flag bits are reserved for future use; they MUST be set
+ to zero and ignored by the receiver.
+</pre>
+
+<p>
+Reserved bits are set to 0 in outgoing messages.</p>
+
+<pre>
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 35]
+
+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 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.
+
+ Hop-by-Hop Identifier
+
+ The Hop-by-Hop Identifier is an unsigned 32-bit integer field (in
+ 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) 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
+ 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 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
+
+
+
+Fajardo, et al. Standards Track [Page 36]
+
+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
+ Diameter message. See Section 4 for more information on AVPs.
+</pre>
+
+&nada;
+
+<pre>
+
+3.1. Command Codes
+
+ 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
+ be taken for a particular message. The following Command Codes are
+ defined in the Diameter base protocol:
+
+ Section
+ Command Name Abbrev. Code Reference
+ --------------------------------------------------------
+ Abort-Session-Request ASR 274 8.5.1
+ Abort-Session-Answer ASA 274 8.5.2
+ Accounting-Request ACR 271 9.7.1
+ Accounting-Answer ACA 271 9.7.2
+ Capabilities-Exchange- CER 257 5.3.1
+ Request
+ Capabilities-Exchange- CEA 257 5.3.2
+ Answer
+ Device-Watchdog-Request DWR 280 5.5.1
+ Device-Watchdog-Answer DWA 280 5.5.2
+ Disconnect-Peer-Request DPR 282 5.4.1
+ Disconnect-Peer-Answer DPA 282 5.4.2
+ Re-Auth-Request RAR 258 8.3.1
+ Re-Auth-Answer RAA 258 8.3.2
+ Session-Termination- STR 275 8.4.1
+ Request
+ Session-Termination- STA 275 8.4.2
+ Answer
+</pre>
+
+<p>
+These messages are all defined in diameter's implementation of the
+common dictionary in modules <c>diameter_gen_base_rfc6733</c> and
+<c>diameter_gen_base_rfc3588</c>.
+Corresponding record definitions are found in
+<c>diameter_gen_base_rfc6733.hrl</c> and
+<c>diameter_gen_base_rfc3588.hrl</c>.</p>
+
+<pre>
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 37]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+3.2. Command Code Format Specification
+
+ 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:
+</pre>
+
+<p>
+The CCF is what is specified in the <c>@messages</c> section of the
+&man_dict; format, except as noted below.</p>
+
+<pre>
+
+ command-def = "&lt;" command-name ">" "::=" diameter-message
+</pre>
+
+<p>
+Angle brackets are currently not allowed here.
+This was a change between RFC 3588 and RFC 6733: the former disallowed
+them in the grammar but included them in its own command definitions.</p>
+
+<pre>
+
+ command-name = diameter-name
+
+ diameter-name = ALPHA *(ALPHA / DIGIT / "-")
+
+ diameter-message = header *fixed *required *optional
+
+ header = "&lt;Diameter-Header:" command-id
+ [r-bit] [p-bit] [e-bit] [application-id]">"
+
+ application-id = 1*DIGIT
+
+ command-id = 1*DIGIT
+ ; The Command Code assigned to the command.
+
+ 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] "&lt;" avp-spec ">"
+ ; Defines the fixed position of an AVP.
+
+ required = [qual] "{" avp-spec "}"
+ ; The AVP MUST be present and can appear
+ ; anywhere in the message.
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 38]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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'.
+
+ 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. Standards Track [Page 39]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ The following is a definition of a fictitious Command Code:
+
+ Example-Request ::= &lt; Diameter Header: 9999999, REQ, PXY >
+ { User-Name }
+ 1* { Origin-Host }
+ * [ AVP ]
+</pre>
+
+&nada;
+
+<pre>
+
+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
+ delimited by a hyphen. A three-letter acronym for both the request
+ and answer is also normally provided.
+
+ An example is a message set used to terminate a session. The command
+ name is Session-Terminate-Request and Session-Terminate-Answer, while
+ 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
+ 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
+ answer, which includes a result code that communicates one of the
+ following:
+
+ o The request was successful
+
+ o The request failed
+
+ o An additional request has to be sent to provide information the
+ peer requires prior to returning a successful or failed answer.
+
+ o The receiver could not process the request, but provides
+ information about a Diameter peer that is able to satisfy the
+ request, known as redirect.
+
+ Additional information, encoded within AVPs, may also be included in
+ answer messages.
+</pre>
+
+<p>
+The &man_dict; format places no requirement on the naming of commands.</p>
+
+<pre>
+
+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. Standards Track [Page 40]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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 until a word
+ boundary is reached. The length of the padding is not reflected in
+ the AVP Length field.
+
+4.1. AVP Header
+
+ The fields in the AVP header MUST be sent in network byte order. The
+ format of the header is:
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | AVP Code |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |V M P r r r r r| AVP Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Vendor-ID (opt) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data ...
+ +-+-+-+-+-+-+-+-+
+
+ AVP Code
+
+ The AVP Code, combined with the Vendor-Id field, identifies the
+ attribute uniquely. AVP numbers 1 through 255 are reserved for
+ 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.1).
+
+ AVP Flags
+
+ The AVP Flags field informs the receiver how each attribute must
+ 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. Standards Track [Page 41]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ unrecognized AVPs.
+
+ The 'M' bit MUST be set according to the rules defined in the
+ 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; 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
+ space.
+
+ AVP Length
+
+ The AVP Length field is three octets, and indicates the number of
+ 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
+ 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"
+ [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. 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
+
+ The Data field is zero or more octets and contains information
+ specific to the Attribute. The format and length of the Data field
+ is determined by the AVP Code and AVP Length fields. The format of
+ the Data field MUST be one of the following base data types or a data
+ 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 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
+ 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
+ MUST be set to 16 (20 if the 'V' bit is enabled).
+
+ Unsigned32
+
+ 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. Standards Track [Page 43]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Float32
+
+ This represents floating point values of single precision as
+ described by [FLOATPOINT]. The 32-bit value is transmitted in
+ 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
+ described by [FLOATPOINT]. The 64-bit value is transmitted in
+ 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. 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 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 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. Standards Track [Page 44]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Time
+
+ 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 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.
+ 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 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 transformation format [RFC3629].
+
+ Since additional code points are added by amendments to the 10646
+ standard from time to time, implementations MUST be prepared to
+ encounter any code point from 0x00000001 to 0x7fffffff. Byte
+ sequences that do not correspond to the valid encoding of a code
+ point into UTF-8 charset or are outside this range are prohibited.
+
+ The use of control codes SHOULD be avoided. When it is necessary
+ to represent a new line, the control code sequence CR LF SHOULD be
+ used.
+
+ The use of leading or trailing white space SHOULD be avoided.
+
+ For code points not directly supported by user interface hardware
+ or software, an alternative means of entry and display, such as
+ hexadecimal, MAY be provided.
+
+ 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 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.
+
+
+
+
+Fajardo, et al. Standards Track [Page 45]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ DiameterIdentity
+
+ The DiameterIdentity format is derived from the OctetString Basic
+ AVP Format.
+
+ DiameterIdentity = FQDN/Realm
+
+ 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
+ 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).
+
+ DiameterURI
+
+ The DiameterURI MUST follow the Uniform Resource Identifiers (RFC
+ 3986) syntax [RFC3986] rules specified below:
+
+ "aaa://" FQDN [ port ] [ transport ] [ protocol ]
+
+ ; No transport security
+
+ "aaas://" FQDN [ port ] [ transport ] [ protocol ]
+
+ ; Transport security used
+
+ FQDN = &lt; Fully Qualified Domain Name >
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 46]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ port = ":" 1*DIGIT
+
+ ; One of the ports used to listen for
+ ; incoming connections.
+ ; If absent, the default Diameter port
+ ; (3868) is assumed if no transport
+ ; security is used and port 5658 when
+ ; transport security (TLS/TCP and DTLS/SCTP)
+ ; is used.
+
+ transport = ";transport=" transport-protocol
+
+ ; One of the transports used to listen
+ ; for incoming connections. If absent,
+ ; the default protocol is assumed to be TCP.
+ ; UDP MUST NOT be used when the aaa-protocol
+ ; field is set to diameter.
+
+ transport-protocol = ( "tcp" / "sctp" / "udp" )
+
+ protocol = ";protocol=" aaa-protocol
+
+ ; If absent, the default AAA protocol
+ ; is Diameter.
+
+ aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
+
+ 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
+
+ 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. Standards Track [Page 47]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ IPFilterRule
+
+ 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:
+
+ Direction (in or out)
+ Source and destination IP address (possibly masked)
+ Protocol
+ Source and destination port (lists or ranges)
+ TCP flags
+ IP fragment flag
+ 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.
+
+ IPFilterRule filters MUST follow the format:
+
+ action dir proto from src to dst [options]
+
+ action permit - Allow packets that match the rule.
+ deny - Drop packets that match the rule.
+
+ dir "in" is from the terminal, "out" is to the
+ terminal.
+
+ proto An IP protocol specified by number. The "ip"
+ keyword means any protocol will match.
+
+ src and dst &lt;address/mask> [ports]
+
+ The &lt;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. Standards Track [Page 48]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ NOT have bits set beyond the mask.
+ For a match to occur, the same IP
+ version must be present in the
+ packet that was used in describing
+ the IP address. To test for a
+ particular IP version, the bits part
+ can be set to zero. The keyword
+ "any" is 0.0.0.0/0 or the IPv6
+ equivalent. The keyword "assigned"
+ is the address or set of addresses
+ assigned to the terminal. For IPv4,
+ a typical first rule is often "deny
+ in ip! assigned".
+
+ The sense of the match can be inverted by
+ preceding an address with the not modifier (!),
+ causing all other addresses to be matched
+ instead. This does not affect the selection of
+ port numbers.
+
+ With the TCP, UDP, and SCTP protocols, optional
+ ports may be specified as:
+
+ {port/port-port}[,ports[,...]]
+
+ The '-' notation specifies a range of ports
+ (including boundaries).
+
+ Fragmented packets that have a non-zero offset
+ (i.e., not the first fragment) will never match
+ a rule that has one or more port
+ specifications. See the frag option for
+ details on matching fragmented packets.
+
+ options:
+ frag Match if the packet is a fragment and this is not
+ the first fragment of the datagram. frag may not
+ be used in conjunction with either tcpflags or
+ TCP/UDP port specifications.
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 49]
+
+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
+ supported IP options are:
+
+ ssrr (strict source route), lsrr (loose source
+ 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
+ supported TCP options are:
+
+ mss (maximum segment size), window (tcp window
+ advertisement), sack (selective ack), ts (rfc1323
+ timestamp), and cc (rfc1644 t/tcp connection
+ count). The absence of a particular option may
+ be denoted with a '!'.
+
+ established
+ TCP packets only. Match packets that have the RST
+ or ACK bits set.
+
+ setup TCP packets only. Match packets that have the SYN
+ bit set but no ACK bit.
+
+
+ tcpflags spec
+ TCP packets only. Match if the TCP header
+ 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
+ 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
+ offset. See the frag option for details on
+ matching fragmented packets.
+
+ icmptypes types
+ ICMP packets only. Match if the ICMP type is in
+ the list types. The list may be specified as any
+ combination of ranges or individual types
+ separated by commas. Both the numeric values and
+ the symbolic values listed below can be used. The
+ supported ICMP types are:
+
+
+
+Fajardo, et al. Standards Track [Page 50]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ echo reply (0), destination unreachable (3),
+ source quench (4), redirect (5), echo request
+ (8), router advertisement (9), router
+ 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),
+ 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.
+
+4.4. Grouped AVP Values
+
+ The Diameter protocol allows AVP values of type 'Grouped'. This
+ 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.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
+ 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
+ Grouped AVPs embedded within the Grouped AVP.
+
+ Every Grouped AVP definition MUST include a corresponding grammar,
+ using ABNF [RFC5234] (with modifications), as defined below.
+
+ grouped-avp-def = "&lt;" name ">" "::=" avp
+
+ name-fmt = ALPHA *(ALPHA / DIGIT / "-")
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 51]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+
+ header = "&lt;" "AVP-Header:" avpcode [vendor] ">"
+
+ avpcode = 1*DIGIT
+ ; 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
+
+ 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 CCF grammar:
+
+ Example-AVP ::= &lt; 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"
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 52]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ optional AVPs included are
+
+ Recovery-Policy = &lt;binary>
+ 2163bc1d0ad82371f6bc09484133c3f09ad74a0dd5346d54195a7cf0b35
+ 2cabc881839a4fdcfbc1769e2677a4c1fb499284c5f70b48f58503a45c5
+ c2d6943f82d5930f2b7c1da640f476f0e9c9572a50db8ea6e51e1c2c7bd
+ f8bb43dc995144b8dbe297ac739493946803e1cee3e15d9b765008a1b2a
+ cf4ac777c80041d72c01e691cf751dbf86e85f509f3988e5875dc905119
+ 26841f00f0e29a6d1ddc1a842289d440268681e052b30fb638045f7779c
+ 1d873c784f054f688f5001559ecff64865ef975f3e60d2fd7966b8c7f92
+
+ Futuristic-Acct-Record = &lt;binary>
+ fe19da5802acd98b07a5b86cb4d5d03f0314ab9ef1ad0b67111ff3b90a0
+ 57fe29620bf3585fd2dd9fcc38ce62f6cc208c6163c008f4258d1bc88b8
+ 17694a74ccad3ec69269461b14b2e7a4c111fb239e33714da207983f58c
+ 41d018d56fe938f3cbf089aac12a912a2f0d1923a9390e5f789cb2e5067
+ d3427475e49968f841
+
+ 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. Standards Track [Page 53]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ This AVP would be encoded as follows:
+
+ 0 1 2 3 4 5 6 7
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 0 | Example AVP Header (AVP Code = 999999), Length = 496 |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 8 | Origin-Host AVP Header (AVP Code = 264), Length = 19 |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 16 | 'e' | 'x' | 'a' | 'm' | 'p' | 'l' | 'e' | '.' |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 24 | 'c' | 'o' | 'm' |Padding| Session-Id AVP Header |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 32 | (AVP Code = 263), Length = 49 | 'g' | 'r' | 'u' | 'm' |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ . . .
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 72 | 'F' | '3' | 'B' | '8' | '1' |Padding|Padding|Padding|
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 80 | Session-Id AVP Header (AVP Code = 263), Length = 50 |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 88 | 'g' | 'r' | 'u' | 'm' | 'p' | '.' | 'e' | 'x' |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ . . .
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 120| '5' | '8' | ';' | '0' | 'A' | 'F' | '3' | 'B' |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 128| '8' | '2' |Padding|Padding| Recovery-Policy Header (AVP |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 136| Code = 8341), Length = 223 | 0x21 | 0x63 | 0xbc | 0x1d |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 144| 0x0a | 0xd8 | 0x23 | 0x71 | 0xf6 | 0xbc | 0x09 | 0x48 |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ . . .
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 352| 0x8c | 0x7f | 0x92 |Padding| Futuristic-Acct-Record Header |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 328|(AVP Code = 15930),Length = 137| 0xfe | 0x19 | 0xda | 0x58 |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 336| 0x02 | 0xac | 0xd9 | 0x8b | 0x07 | 0xa5 | 0xb8 | 0xc6 |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ . . .
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ 488| 0xe4 | 0x99 | 0x68 | 0xf8 | 0x41 |Padding|Padding|Padding|
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 54]
+
+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, and possible flag values.
+
+ Due to space constraints, the short form DiamIdent is used to
+ represent DiameterIdentity.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 55]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ +----------+
+ | AVP Flag |
+ | rules |
+ |----+-----|
+ AVP Section | |MUST |
+ Attribute Name Code Defined Data Type |MUST| NOT |
+ -----------------------------------------|----+-----|
+ Acct- 85 9.8.2 Unsigned32 | M | V |
+ Interim-Interval | | |
+ Accounting- 483 9.8.7 Enumerated | M | V |
+ Realtime-Required | | |
+ Acct- 50 9.8.5 UTF8String | M | V |
+ Multi-Session-Id | | |
+ Accounting- 485 9.8.3 Unsigned32 | M | V |
+ Record-Number | | |
+ Accounting- 480 9.8.1 Enumerated | M | V |
+ Record-Type | | |
+ Acct- 44 9.8.4 OctetString| M | V |
+ Session-Id | | |
+ Accounting- 287 9.8.6 Unsigned64 | M | V |
+ Sub-Session-Id | | |
+ Acct- 259 6.9 Unsigned32 | M | V |
+ Application-Id | | |
+ Auth- 258 6.8 Unsigned32 | M | V |
+ Application-Id | | |
+ Auth-Request- 274 8.7 Enumerated | M | V |
+ Type | | |
+ Authorization- 291 8.9 Unsigned32 | M | V |
+ Lifetime | | |
+ Auth-Grace- 276 8.10 Unsigned32 | M | V |
+ Period | | |
+ Auth-Session- 277 8.11 Enumerated | M | V |
+ State | | |
+ Re-Auth-Request- 285 8.12 Enumerated | M | V |
+ Type | | |
+ Class 25 8.20 OctetString| M | V |
+ Destination-Host 293 6.5 DiamIdent | M | V |
+ Destination- 283 6.6 DiamIdent | M | V |
+ Realm | | |
+ Disconnect-Cause 273 5.4.3 Enumerated | M | V |
+ Error-Message 281 7.3 UTF8String | | V,M |
+ Error-Reporting- 294 7.4 DiamIdent | | V,M |
+ Host | | |
+ Event-Timestamp 55 8.21 Time | M | V |
+ Experimental- 297 7.6 Grouped | M | V |
+ Result | | |
+ -----------------------------------------|----+-----|
+
+
+
+
+Fajardo, et al. Standards Track [Page 56]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ +----------+
+ | AVP Flag |
+ | rules |
+ |----+-----|
+ AVP Section | |MUST |
+ Attribute Name Code Defined Data Type |MUST| NOT |
+ -----------------------------------------|----+-----|
+ Experimental- 298 7.7 Unsigned32 | M | V |
+ Result-Code | | |
+ Failed-AVP 279 7.5 Grouped | M | V |
+ Firmware- 267 5.3.4 Unsigned32 | | V,M |
+ Revision | | |
+ Host-IP-Address 257 5.3.5 Address | M | V |
+ Inband-Security | M | V |
+ -Id 299 6.10 Unsigned32 | | |
+ Multi-Round- 272 8.19 Unsigned32 | M | V |
+ Time-Out | | |
+ Origin-Host 264 6.3 DiamIdent | M | V |
+ Origin-Realm 296 6.4 DiamIdent | M | V |
+ Origin-State-Id 278 8.16 Unsigned32 | M | V |
+ Product-Name 269 5.3.7 UTF8String | | V,M |
+ Proxy-Host 280 6.7.3 DiamIdent | M | V |
+ Proxy-Info 284 6.7.2 Grouped | M | V |
+ Proxy-State 33 6.7.4 OctetString| M | V |
+ Redirect-Host 292 6.12 DiamURI | M | V |
+ Redirect-Host- 261 6.13 Enumerated | M | V |
+ Usage | | |
+ Redirect-Max- 262 6.14 Unsigned32 | M | V |
+ Cache-Time | | |
+ Result-Code 268 7.1 Unsigned32 | M | V |
+ Route-Record 282 6.7.1 DiamIdent | M | V |
+ Session-Id 263 8.8 UTF8String | M | V |
+ Session-Timeout 27 8.13 Unsigned32 | M | V |
+ Session-Binding 270 8.17 Unsigned32 | M | V |
+ Session-Server- 271 8.18 Enumerated | M | V |
+ Failover | | |
+ Supported- 265 5.3.6 Unsigned32 | M | V |
+ Vendor-Id | | |
+ Termination- 295 8.15 Enumerated | M | V |
+ Cause | | |
+ User-Name 1 8.14 UTF8String | M | V |
+ Vendor-Id 266 5.3.3 Unsigned32 | M | V |
+ Vendor-Specific- 260 6.11 Grouped | M | V |
+ Application-Id | | |
+ -----------------------------------------|----+-----|
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 57]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+5. Diameter Peers
+
+ This section describes how Diameter nodes establish connections and
+ communicate with peers.
+
+5.1. Peer Connections
+
+ Connections between diameter peers are established using their valid
+ DiameterIdentity. A Diameter node initiating a connection to a peer
+ 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 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 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
+ of active connections exists.
+
+ 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 shut down. The peer is moved to the closed state.
+
+ 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
+ assume the role of either primary or secondary.
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 58]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+5.2. Diameter Peer Discovery
+
+ 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:
+
+ 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, 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'
+ [RFC6408].
+
+ The client can follow the resolution process defined by the
+ 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
+ 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. Standards Track [Page 59]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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 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 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 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
+
+ When two Diameter peers establish a transport connection, they MUST
+ 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,
+ the identifiers of supported Diameter applications, security
+ mechanisms, etc.).
+
+
+
+
+Fajardo, et al. Standards Track [Page 60]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ layer connection. Note that receiving a CER or CEA from a peer
+ 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 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
+ the receiver regarding all of its application capabilities.
+
+ Diameter implementations SHOULD first attempt to establish a TLS/TCP
+ 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 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.
+ In both cases, the transport connection is closed. If the local
+ policy permits receiving CERs from unknown hosts, a successful CEA
+ MAY be returned. If a CER from an unknown peer is answered with a
+ successful CEA, the lifetime of the peer entry is equal to the
+ lifetime of the transport connection. In case of a transport
+ 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.
+
+
+
+
+Fajardo, et al. Standards Track [Page 61]
+
+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
+ 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
+ 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.
+
+ 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
+ locally used when transmitting Diameter messages.
+
+ Message Format
+
+ &lt;CER> ::= &lt; Diameter Header: 257, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ 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
+ set to 257 and the Command Flags' 'R' bit cleared, is sent in
+ response to a CER message.
+
+ When Diameter is run over SCTP [RFC4960] or DTLS/SCTP [RFC6083],
+ which allow connections to span multiple interfaces, hence, multiple
+ IP addresses, the Capabilities-Exchange-Answer message MUST contain
+ one Host-IP-Address AVP for each potential IP address that MAY be
+ locally used when transmitting Diameter messages.
+
+ Message Format
+
+ &lt;CEA> ::= &lt; Diameter Header: 257 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Failed-AVP ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+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"
+ [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 Firmware-Revision (Section 5.3.4) AVPs may
+ provide useful debugging information.
+
+ 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
+ computers running Diameter software modules, for instance), the
+ revision of the Diameter software module may be reported instead.
+
+5.3.5. Host-IP-Address AVP
+
+ The Host-IP-Address AVP (AVP Code 257) is of type Address and is used
+ 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
+ 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"
+ [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
+ AVP SHOULD remain constant across firmware revisions for the same
+ product.
+
+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
+ 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
+
+
+
+
+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.
+
+ 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
+ to 282 and the Command Flags' 'R' bit set, is sent to a peer to
+ 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
+
+ &lt;DPR> ::= &lt; Diameter Header: 282, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ { Disconnect-Cause }
+ * [ AVP ]
+
+5.4.2. Disconnect-Peer-Answer
+
+ 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 shut down.
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 65]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Message Format
+
+ &lt;DPA> ::= &lt; 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 shut
+ down the transport connection. The following values are supported:
+
+ 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.
+ 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. A receiver of a
+ DPR with above result code SHOULD NOT attempt reconnection.
+
+5.5. Transport Failure Detection
+
+ Given the nature of the Diameter protocol, it is recommended that
+ transport failures be detected as soon as possible. Detecting such
+ failures will minimize the occurrence of messages sent to unavailable
+ agents, resulting in unnecessary delays, and will provide better
+ failover performance. The Device-Watchdog-Request and Device-
+ 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
+ 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
+ to an alternate peer.
+
+ Message Format
+
+ &lt;DWR> ::= &lt; Diameter Header: 280, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ [ Origin-State-Id ]
+ * [ AVP ]
+
+5.5.2. Device-Watchdog-Answer
+
+ 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.
+
+ Message Format
+
+ &lt;DWA> ::= &lt; Diameter Header: 280 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ [ Failed-AVP ]
+ [ Origin-State-Id ]
+ * [ AVP ]
+
+5.5.3. Transport Failure Algorithm
+
+ The transport failure algorithm is defined in [RFC3539]. All
+ 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
+
+ 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".
+
+
+
+
+
+
+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
+ given peer. When an answer message is received, the corresponding
+ 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
+ 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
+ field in the Diameter header along with the Origin-Host AVP MUST be
+ used to identify duplicate messages.
+
+ As described in Section 2.1, a connection request should be
+ periodically attempted with the failed peer in order to re-establish
+ 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".
+
+5.6. Peer State Machine
+
+ This section contains a finite state machine that MUST be observed by
+ all Diameter implementations. Each Diameter node MUST follow the
+ state machine described below when communicating with each peer.
+ Multiple actions are separated by commas, and may continue on
+ succeeding lines, as space requires. Similarly, state and next state
+ may also span multiple lines, as space requires.
+
+ 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. In particular, note that
+ [RFC3539] requires the use of watchdog messages to probe connections.
+ For Diameter, DWR and DWA messages are to be used.
+
+ 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,
+ 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.
+
+ A CER message is always sent on the initiating connection immediately
+ after the connection request is successfully completed. In the case
+ of an election, one of the two connections will shut down. The
+ responder connection will survive if the Origin-Host of the local
+ Diameter entity is higher than that of the peer; the initiator
+ connection will survive if the peer's Origin-Host is higher. All
+ subsequent messages are sent on the surviving connection. Note that
+ 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, 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
+ implementations that are not prepared to received TLS/TCP and DTLS/
+ 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
+ handshake when both ends are in the open state. If the 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.
+
+ The state machine constrains only the behavior of a Diameter
+ implementation as seen by Diameter peers through events on the wire.
+
+ 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
+ R-Conn-CER R-Accept, R-Open
+ Process-CER,
+ R-Snd-CEA
+
+ Wait-Conn-Ack I-Rcv-Conn-Ack I-Snd-CER Wait-I-CEA
+ I-Rcv-Conn-Nack Cleanup Closed
+ R-Conn-CER R-Accept, Wait-Conn-Ack/
+ Process-CER Elect
+ Timeout Error Closed
+
+ Wait-I-CEA I-Rcv-CEA Process-CEA I-Open
+ R-Conn-CER R-Accept, Wait-Returns
+ Process-CER,
+ Elect
+ I-Peer-Disc I-Disc Closed
+ I-Rcv-Non-CEA Error Closed
+ Timeout Error Closed
+
+ Wait-Conn-Ack/ I-Rcv-Conn-Ack I-Snd-CER,Elect Wait-Returns
+ Elect I-Rcv-Conn-Nack R-Snd-CEA R-Open
+ R-Peer-Disc R-Disc Wait-Conn-Ack
+ R-Conn-CER R-Reject Wait-Conn-Ack/
+ Elect
+ Timeout Error Closed
+
+ Wait-Returns Win-Election I-Disc,R-Snd-CEA R-Open
+ I-Peer-Disc I-Disc, R-Open
+ R-Snd-CEA
+ I-Rcv-CEA R-Disc I-Open
+ R-Peer-Disc R-Disc Wait-I-CEA
+ R-Conn-CER R-Reject Wait-Returns
+ Timeout Error Closed
+
+ R-Open Send-Message R-Snd-Message R-Open
+ R-Rcv-Message Process R-Open
+ R-Rcv-DWR Process-DWR, R-Open
+ R-Snd-DWA
+ 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 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
+ I-Snd-DWA
+ 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 Closing
+ I-Peer-Disc I-Disc Closed
+
+ Closing I-Rcv-DPA I-Disc Closed
+ R-Rcv-DPA R-Disc Closed
+ Timeout Error Closed
+ I-Peer-Disc I-Disc Closed
+ R-Peer-Disc R-Disc Closed
+
+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; 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
+ 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 a CER arrives or if an
+ implementation-defined timeout occurs prior to receipt of CER.
+
+ Because handling of incoming connections up to and including receipt
+ 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- prefixes, since the actual
+ event would be identical, but it would occur on one of two possible
+ connections.
+
+
+
+Fajardo, et al. Standards Track [Page 71]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Start The Diameter application has signaled that a
+ connection should be initiated with the peer.
+
+ R-Conn-CER An acknowledgement is received stating that the
+ transport connection has been established, and the
+ associated CER has arrived.
+
+ Rcv-Conn-Ack A positive acknowledgement is received confirming that
+ the transport connection is established.
+
+ Rcv-Conn-Nack A negative acknowledgement was received stating that
+ the transport connection was not established.
+
+ Timeout An application-defined timer has expired while waiting
+ for some event.
+
+ Rcv-CER A CER message from the peer was received.
+
+ Rcv-CEA A CEA message 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.
+
+ Rcv-DPR A DPR message from the peer was received.
+
+ Rcv-DPA A DPA message from the peer was received.
+
+ Win-Election An election was held, and the local node was the
+ winner.
+
+ Send-Message A message is to be sent.
+
+ Rcv-Message A message other than CER, CEA, DPR, DPA, DWR, or DWA
+ was received.
+
+ Stop The Diameter application has signaled that a
+ connection should be terminated (e.g., on system
+ shutdown).
+
+5.6.3. Actions
+
+ 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- prefixes,
+ since the actual action would be identical, but it would occur on one
+ of two possible connections.
+
+
+
+Fajardo, et al. Standards Track [Page 72]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Snd-Conn-Req A transport connection is initiated with the peer.
+
+ Accept The incoming connection associated with the R-Conn-CER
+ is accepted as the responder connection.
+
+ Reject The incoming connection associated with the R-Conn-CER
+ 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 shut down, and any
+ local resources are freed.
+
+ Error The transport layer connection is disconnected,
+ either politely or abortively, in response to
+ an error condition. Local resources are freed.
+
+ Process-CEA A received CEA is processed.
+
+ Snd-DPR A DPR message is sent to the peer.
+
+ Snd-DPA A DPA message is sent to the peer.
+
+ Disc The transport layer connection is disconnected,
+ and local resources are freed.
+
+ Elect An election occurs (see Section 5.6.4 for more
+ information).
+
+ Snd-Message A message is sent.
+
+ Snd-DWR A DWR message is sent.
+
+ Snd-DWA A DWA message is sent.
+
+ Process-DWR The DWR message is serviced.
+
+ Process-DWA The DWA message is serviced.
+
+ Process A message is serviced.
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 73]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+5.6.4. The Election Process
+
+ 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 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.
+
+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 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 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
+ 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
+ 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
+ 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.
+
+
+
+
+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
+ 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 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:
+
+ 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".
+
+ o The procedure listed in Section 6.1.6 is followed, which is known
+ as "Request Routing".
+
+ 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.
+
+ 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
+
+ When creating a request, in addition to any other procedures
+ described in the application definition for that specific request,
+ the following procedures MUST be followed:
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 75]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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;
+ and
+
+ o the Destination-Host and Destination-Realm AVPs MUST be set to the
+ appropriate values, as described in Section 6.1.
+
+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.
+
+ Other actions to perform on the message based on the particular role
+ the agent is playing are described in the following sections.
+
+6.1.3. Receiving Requests
+
+ A relay or proxy agent MUST check for forwarding loops when receiving
+ requests. A loop is detected if the server finds its own identity in
+ a Route-Record AVP. When such an event occurs, the agent MUST answer
+ with the Result-Code AVP set to DIAMETER_LOOP_DETECTED.
+
+6.1.4. Processing Local Requests
+
+ A request is known to be for local consumption when one of the
+ following conditions occurs:
+
+ 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
+
+ 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 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
+ be forwarded to the peer.
+
+6.1.6. Request Routing
+
+ 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
+ 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 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).
+
+ Realm names and Application Ids are the minimum supported routing
+ criteria, additional information may be needed to support redirect
+ semantics.
+
+6.1.7. Predictive Loop Avoidance
+
+ Before forwarding or routing a request, Diameter agents, in addition
+ 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
+
+ When a redirect agent receives a request whose routing entry is set
+ 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 a separate
+ Redirect-Host AVP.
+
+ +------------------+
+ | Diameter |
+ | Redirect Agent |
+ +------------------+
+ ^ | 2. command + 'E' bit
+ 1. Request | | Result-Code =
+ [email protected] | | DIAMETER_REDIRECT_INDICATION +
+ | | Redirect-Host AVP(s)
+ | v
+ +-------------+ 3. Request +-------------+
+ | example.com |------------->| example.net |
+ | Relay | | Diameter |
+ | Agent |&lt;-------------| Server |
+ +-------------+ 4. Answer +-------------+
+
+ Figure 5: Diameter Redirect Agent
+
+ 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
+ message with the 'E' bit set selects exactly one of these hosts as
+ the destination of the redirected message.
+
+ 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 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 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
+ rule MUST be used against the redirect usage value to resolve the
+ contention. The precedence rule can be found in Section 6.13.
+
+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 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.
+
+ 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 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].
+
+ o The keys and cryptographic protection algorithms should be at
+ least 128 bits in strength.
+
+ o The keys should not be used for any other purpose than generating
+ and verifying instances of the Proxy-Info AVP.
+
+ o The keys should be changed regularly.
+
+ 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.
+
+
+
+
+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)
+ (Route-Record=nas.example.net)
+ +------+ ------> +------+ ------> +------+
+ | | (Request) | | (Request) | |
+ | NAS +-------------------+ DRL +-------------------+ HMS |
+ | | | | | |
+ +------+ &lt;------ +------+ &lt;------ +------+
+ 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
+
+ Relay and proxy agents are not required to perform full inspection of
+ 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 in which it is interested.
+
+6.2. Diameter Answer Processing
+
+ When a request is locally processed, the following procedures MUST be
+ applied to create the associated answer, in addition to any
+ 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
+ answer.
+
+ o The local host's identity is encoded in the Origin-Host AVP.
+
+ o The Destination-Host and Destination-Realm AVPs MUST NOT be
+ present in the answer message.
+
+ o The Result-Code AVP is added with its value indicating success or
+ failure.
+
+ o If the Session-Id is present in the request, it MUST be included
+ in the answer.
+
+ 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) are also subjected to
+ the above processing rules.
+
+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
+ corresponding message should be removed from the list of pending
+ requests. It SHOULD ignore answers received that do not match a
+ known Hop-by-Hop Identifier.
+
+6.2.2. Relaying and Proxying Answers
+
+ 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
+ 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
+ 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; 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
+ original request from.
+
+6.3. Origin-Host AVP
+
+ The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and
+ 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.
+
+ Note that the Origin-Host AVP may resolve to more than one address as
+ the Diameter peer may support more than one address.
+
+ This AVP SHOULD be placed as close to the Diameter header as
+ possible.
+
+6.4. Origin-Realm AVP
+
+ The Origin-Realm AVP (AVP Code 296) is of type DiameterIdentity.
+ This AVP contains the Realm of the originator of any Diameter message
+ and MUST be present in all messages.
+
+ This AVP SHOULD be placed as close to the Diameter header as
+ possible.
+
+6.5. Destination-Host AVP
+
+ 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
+ messages.
+
+ 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.
+
+ This AVP SHOULD be placed as close to the Diameter header as
+ possible.
+
+6.6. Destination-Realm 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.
+
+ 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-
+ 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.
+
+6.7. Routing AVPs
+
+ The AVPs defined in this section are Diameter AVPs used for routing
+ purposes. These AVPs change as Diameter messages are processed by
+ agents.
+
+6.7.1. Route-Record AVP
+
+ The Route-Record AVP (AVP Code 282) is of type DiameterIdentity. The
+ identity added in this AVP MUST be the same as the one received in
+ the Origin-Host of the Capabilities Exchange message.
+
+6.7.2. Proxy-Info AVP
+
+ 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 CCF grammar:
+
+ Proxy-Info ::= &lt; AVP Header: 284 >
+ { Proxy-Host }
+ { Proxy-State }
+ * [ AVP ]
+
+6.7.3. Proxy-Host AVP
+
+ The Proxy-Host AVP (AVP Code 280) is of type DiameterIdentity. This
+ AVP contains the identity of the host that added the Proxy-Info AVP.
+
+6.7.4. Proxy-State AVP
+
+ The Proxy-State AVP (AVP Code 33) is of type OctetString. It
+ contains state information that would otherwise be stored at the
+ Diameter entity that created it. As such, this AVP MUST be treated
+ as opaque data by other Diameter entities.
+
+6.8. Auth-Application-Id AVP
+
+ The Auth-Application-Id AVP (AVP Code 258) is of type Unsigned32 and
+ is used in order to advertise support of the Authentication and
+ Authorization portion of an application (see Section 2.4). If
+ present in a message other than CER and CEA, the value of the Auth-
+ 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
+ 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.
+
+6.10. Inband-Security-Id AVP
+
+ 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 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:
+
+
+ NO_INBAND_SECURITY 0
+
+ This peer does not support TLS/TCP and DTLS/SCTP. This is the
+ default value, if the AVP is omitted.
+
+ TLS 1
+
+ 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-
+ 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 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.
+
+ AVP Format
+
+ &lt;Vendor-Specific-Application-Id> ::= &lt; AVP Header: 260 >
+ { Vendor-Id }
+ [ Auth-Application-Id ]
+ [ Acct-Application-Id ]
+
+ 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 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,
+ which MUST contain an example of an Auth-Application-Id AVP and an
+ Acct-Application-Id AVP.
+
+ If a Vendor-Specific-Application-Id is received that contains both
+ 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
+ and Acct-Application-Id AVP.
+
+6.12. Redirect-Host AVP
+
+ The Redirect-Host AVP (AVP Code 292) is of type DiameterURI. One or
+ 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.
+
+ Upon receiving the above, the receiving Diameter node SHOULD forward
+ the request directly to one of the hosts identified in these AVPs.
+ The server contained in the selected Redirect-Host AVP SHOULD be used
+ for all messages matching the criteria set by the Redirect-Host-Usage
+ AVP.
+
+6.13. Redirect-Host-Usage AVP
+
+ The Redirect-Host-Usage AVP (AVP Code 261) is of type Enumerated.
+ 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 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.
+
+ 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.
+
+ 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
+ redirect usage values of the cached routes during normal routing to
+ resolve contentions that may occur. The precedence rule is the order
+ that dictate which redirect usage should be considered before any
+ 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
+
+ 3. REALM_AND_APPLICATION
+
+ 4. ALL_REALM
+
+ 5. ALL_APPLICATION
+
+ 6. ALL_HOST
+
+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,
+ 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.
+
+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.
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 87]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 1. Request +---------+ Link Broken
+ +-------------------------->|Diameter |----///----+
+ | +---------------------| | v
+ +------+--+ | 2. answer + 'E' set | Relay 2 | +--------+
+ |Diameter |&lt;-+ (Unable to Forward) +---------+ |Diameter|
+ | | | Home |
+ | Relay 1 |--+ +---------+ | Server |
+ +---------+ | 3. Request |Diameter | +--------+
+ +-------------------->| | ^
+ | Relay 3 |-----------+
+ +---------+
+
+ 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
+ detects that it cannot forward the request to the home server, an
+ answer message is returned with the 'E' bit set and the Result-Code
+ AVP set to DIAMETER_UNABLE_TO_DELIVER. Given that this error falls
+ within the protocol error category, Relay 1 would take special
+ action, and given the error, attempt to route the message through its
+ alternate Relay 3.
+
+ +---------+ 1. Request +---------+ 2. Request +---------+
+ | Access |------------>|Diameter |------------>|Diameter |
+ | | | | | Home |
+ | Device |&lt;------------| Relay |&lt;------------| Server |
+ +---------+ 4. Answer +---------+ 3. Answer +---------+
+ (Missing AVP) (Missing AVP)
+
+ 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
+ and adds the Result-Code AVP with the proper value. Application
+ 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 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 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
+ containing the offending AVP.
+
+ o A request with an AVP that is received with an unrecognized value
+ causes an answer to be returned with the Result-Code AVP set to
+ DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
+ AVP causing the error.
+
+ 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
+ other fields set as expected in the missing AVP. The created 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
+ 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. Standards Track [Page 89]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ o 1xxx (Informational)
+
+ o 2xxx (Success)
+
+ o 3xxx (Protocol Errors)
+
+ o 4xxx (Transient Failures)
+
+ o 5xxx (Permanent Failure)
+
+ An unrecognized class (one whose first digit is not defined in this
+ section) MUST be handled as a permanent failure.
+
+7.1.1. Informational
+
+ Errors that fall within this category are used to inform the
+ 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
+ inform the access device that the authentication mechanism being
+ used requires multiple round trips, and a subsequent request needs
+ to be issued in order for access to be granted.
+
+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.
+
+ DIAMETER_LIMITED_SUCCESS 2002
+
+ When returned, the request was successfully completed, but
+ additional processing is required by the application in order to
+ provide service to the user.
+
+7.1.3. Protocol Errors
+
+ 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.
+
+
+
+
+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 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 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
+ to an alternate peer. This error MUST only be used when a
+ specific server is requested, and it cannot provide the requested
+ service.
+
+ DIAMETER_LOOP_DETECTED 3005
+
+ An agent detected a loop while trying to get the message to the
+ intended recipient. The message MAY be sent to an alternate peer,
+ 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
+ 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
+
+ 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.
+
+
+
+Fajardo, et al. Standards Track [Page 91]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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.
+ 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
+ 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
+ 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,
+ 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.
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 92]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+
+ 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
+ Code definition. If this value is sent in the Result-Code AVP, a
+ Failed-AVP AVP SHOULD be included in the message. The Failed-AVP
+ AVP MUST contain an example of the missing AVP complete with the
+ 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 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 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
+
+ A message was received with an AVP that MUST NOT be present. The
+ 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
+ the offending AVP that exceeded the maximum number of occurrences.
+
+ DIAMETER_NO_COMMON_APPLICATION 5010
+
+ This error is returned by a Diameter node that receives a CER
+ 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
+
+ The request contained an AVP with an invalid length. A Diameter
+ message indicating this error MUST include the offending AVPs
+ within a Failed-AVP AVP. In cases where the erroneous AVP length
+ 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
+ indicate the offending AVP. In the case where the offending AVP
+ header cannot be fully decoded when the AVP length is less than
+
+
+
+
+Fajardo, et al. Standards Track [Page 94]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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_MESSAGE_LENGTH 5015
+
+ This error is returned when a request is received with an invalid
+ message length.
+
+ DIAMETER_INVALID_AVP_BIT_COMBO 5016
+
+ 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.
+
+ 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
+
+ The 'E' (Error Bit) in the Diameter header is set when the request
+ caused a protocol-related error (see Section 7.1.3). A message with
+ 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 CCF specification for the command;
+ instead, it and will conform to the following CCF:
+
+ Message Format
+
+ &lt;answer-message> ::= &lt; Diameter Header: code, ERR [, PXY] >
+ 0*1&lt; Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Result-Code }
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Experimental-Result ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+
+
+
+Fajardo, et al. Standards Track [Page 95]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ found in the request message.
+
+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
+ 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 be parsed by network entities.
+
+7.4. Error-Reporting-Host AVP
+
+ The Error-Reporting-Host AVP (AVP Code 294) is of type
+ DiameterIdentity. This AVP contains the identity of the Diameter
+ 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 it MUST be set when the
+ Result-Code AVP indicates a failure.
+
+7.5. Failed-AVP AVP
+
+ The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+ 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 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 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
+ 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. Standards Track [Page 96]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ than the minimum AVP header length or greater than the reported
+ 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.
+
+ AVP Format
+
+ &lt;Failed-AVP> ::= &lt; AVP Header: 279 >
+ 1* {AVP}
+
+7.6. Experimental-Result AVP
+
+ The Experimental-Result AVP (AVP Code 297) is of type Grouped, and
+ indicates whether a particular vendor-specific request was completed
+ successfully or whether an error occurred. This AVP has the
+ following structure:
+
+ AVP Format
+
+ Experimental-Result ::= &lt; AVP Header: 297 >
+ { Vendor-Id }
+ { 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 that
+ follows. All Diameter answer messages defined in vendor-specific
+ applications MUST include either one Result-Code AVP or one
+ Experimental-Result AVP.
+
+7.7. Experimental-Result-Code AVP
+
+ The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32
+ and contains a vendor-assigned value representing the result of
+ processing the request.
+
+ 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
+ values).
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 97]
+
+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 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,
+ the Diameter client issues an auth request to its local server. The
+ 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
+ for the client and servers to correlate a Diameter message with a
+ user session.
+
+ 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-
+ Session-State AVP with the value set to NO_STATE_MAINTAINED as a hint
+ to the server. If the server accepts the hint, it agrees that since
+ no session termination message will be received once service to the
+ user is terminated, it cannot maintain state for the session. If the
+ answer message from the server contains a different value in the
+ Auth-Session-State AVP (or the default value if the AVP is absent),
+ the access device MUST follow the server's directives. Note that the
+ value NO_STATE_MAINTAINED MUST NOT be set in subsequent re-
+ authorization requests and answers.
+
+
+
+
+Fajardo, et al. Standards Track [Page 98]
+
+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
+ 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 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 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,
+ ASR/ASA messages for stateful sessions.
+
+8.1. Authorization Session State Machine
+
+ 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
+ session in which the server is maintaining session state, indicated
+ by the value of the Auth-Session-State AVP (or its absence). One
+ describes the session from a client perspective, the other from a
+ server perspective. The second two state machines are used when the
+ server does not maintain session state. Here again, one describes
+ the session from a client perspective, the other from a server
+ perspective.
+
+ 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 an error condition,
+ and an answer, if applicable, MUST be returned to the originator of
+ the message.
+
+
+
+Fajardo, et al. Standards Track [Page 99]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ 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
+ 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
+ successfully sent' is the complement of 'Failure to send X'.
+
+ The following state machine is observed by a client when state is
+ maintained on the server:
+
+ CLIENT, STATEFUL
+ State Event Action New State
+ ---------------------------------------------------------------
+ Idle Client or device requests Send Pending
+ access service-
+ specific
+ auth req
+
+ Idle ASR Received Send ASA Idle
+ for unknown session with
+ Result-Code =
+ UNKNOWN_
+ SESSION_ID
+
+ Idle RAR Received Send RAA Idle
+ for unknown session with
+ Result-Code =
+ UNKNOWN_
+ SESSION_ID
+
+ 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,
+ but service not provided
+
+ Pending Error processing successful Sent STR Discon
+ service-specific authorization
+ answer
+
+
+
+Fajardo, et al. Standards Track [Page 100]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Pending Failed service-specific Clean up Idle
+ authorization answer received
+
+ Open User or client device Send Open
+ requests access to service service-
+ specific
+ auth req
+
+ Open Successful service-specific Provide Open
+ authorization answer received service
+
+ Open Failed service-specific Discon. Idle
+ authorization answer user/device
+ received.
+
+ Open RAR received and client will Send RAA Open
+ perform subsequent re-auth with
+ Result-Code =
+ SUCCESS
+
+ Open RAR received and client will Send RAA Idle
+ not perform subsequent with
+ re-auth Result-Code !=
+ SUCCESS,
+ Discon.
+ user/device
+
+ Open Session-Timeout expires on Send STR Discon
+ access device
+
+ Open ASR received, Send ASA Discon
+ client will comply with
+ with request to end the Result-Code =
+ session = SUCCESS,
+ Send STR.
+
+ Open ASR Received, Send ASA Open
+ client will not comply with
+ with request to end the Result-Code !=
+ session != SUCCESS
+
+ Open Authorization-Lifetime + Send STR Discon
+ Auth-Grace-Period expires on
+ access device
+
+ Discon ASR received Send ASA Discon
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 101]
+
+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
+ maintaining state for the session:
+
+ SERVER, STATEFUL
+ State Event Action New State
+ ---------------------------------------------------------------
+ Idle Service-specific authorization Send Open
+ request received, and successful
+ user is authorized service-
+ specific
+ answer
+
+ Idle Service-specific authorization Send Idle
+ request received, and failed
+ user is not authorized service-
+ specific
+ answer
+
+ Open Service-specific authorization Send Open
+ request received, and user successful
+ is authorized service-
+ specific
+ answer
+
+ Open Service-specific authorization Send Idle
+ request received, and user failed
+ is not authorized service-
+ specific
+ answer,
+ Clean up
+
+ Open Home server wants to confirm Send RAR Pending
+ authentication and/or
+ authorization of the user
+
+ Pending Received RAA with a failed Clean up Idle
+ Result-Code
+
+ Pending Received RAA with Result-Code Update Open
+ = SUCCESS session
+
+ Open Home server wants to Send ASR Discon
+ terminate the service
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 102]
+
+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 Clean up Idle
+ ASA Received with Result-Code
+
+ Not ASA Received None No Change
+ Discon
+
+ Any STR Received Send STA, Idle
+ Clean up
+
+ The following state machine is observed by a client when state is not
+ maintained on the server:
+
+ CLIENT, STATELESS
+ State Event Action New State
+ ---------------------------------------------------------------
+ Idle Client or device requests Send Pending
+ access service-
+ specific
+ auth req
+
+ Pending Successful service-specific Grant Open
+ authorization answer access
+ received with Auth-Session-
+ State set to
+ NO_STATE_MAINTAINED
+
+ Pending Failed service-specific Clean up Idle
+ authorization answer
+ received
+
+ Open Session-Timeout expires on Discon. Idle
+ access device user/device
+
+ Open Service to user is terminated Discon. Idle
+ user/device
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 103]
+
+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 Idle
+ request received, and service-
+ successfully processed specific
+ answer
+
+8.2. Accounting Session State Machine
+
+ The following state machines MUST be supported for applications that
+ have an accounting portion or that require only accounting services.
+ The first state machine is to be observed by clients.
+
+ See Section 9.7 for Accounting Command Codes and Section 9.8 for
+ Accounting AVPs.
+
+ The server side in the accounting state machine depends in some cases
+ on the particular application. The Diameter base protocol defines a
+ default state machine that MUST be followed by all applications that
+ have not specified other state machines. This is the second state
+ 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 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
+ can happen either immediately after record reception or in a post-
+ processing phase. However, as these tasks are typically application
+ 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-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
+ keeping track of the session state at the accounting server. Note
+ 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; hence,
+ accounting connectivity problems are required to cause the serviced
+ user to be disconnected. Otherwise, records produced by the client
+
+
+
+Fajardo, et al. Standards Track [Page 104]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ 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
+ notification DIAMETER_OUT_OF_SPACE, DIAMETER_TOO_BUSY, or
+ DIAMETER_LOOP_DETECTED in the Result-Code AVP of the Accounting
+ Answer command.
+
+ 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 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
+ for pending states to wait for an answer to an accounting request
+ related to a Start, Interim, Stop, Event, or buffered record,
+ respectively.
+
+ CLIENT, ACCOUNTING
+ State Event Action New State
+ ---------------------------------------------------------------
+ Idle Client or device requests Send PendingS
+ access accounting
+ start req.
+
+ Idle Client or device requests Send PendingE
+ a one-time service accounting
+ event req
+
+ Idle Records in storage Send PendingB
+ record
+
+
+
+
+Fajardo, et al. Standards Track [Page 105]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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 real time
+ equal to GRANT_AND_LOSE
+
+ PendingS Failure to send and no Disconnect Idle
+ buffer space available and user/dev
+ real time not equal to
+ GRANT_AND_LOSE
+
+ PendingS Failed accounting start answer Open
+ received and real time equal
+ to GRANT_AND_LOSE
+
+ PendingS Failed accounting start answer Disconnect Idle
+ received and real time not user/dev
+ equal to GRANT_AND_LOSE
+
+ PendingS User service terminated Store PendingS
+ stop
+ record
+
+ Open Interim interval elapses Send PendingI
+ accounting
+ interim
+ record
+
+ Open User service terminated Send PendingL
+ accounting
+ stop req.
+
+ PendingI Successful accounting interim Open
+ answer received
+
+ PendingI Failure to send and (buffer Store Open
+ space available or old interim
+ record can be overwritten) record
+ and real time not equal to
+ DELIVER_AND_GRANT
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 106]
+
+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
+
+ 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 real time
+ equal to GRANT_AND_LOSE
+
+ PendingI Failed accounting interim Disconnect Idle
+ answer received and user/dev
+ real time not equal to
+ GRANT_AND_LOSE
+
+ PendingI User service terminated Store PendingI
+ stop
+ record
+ PendingE Successful accounting Idle
+ event answer received
+
+ PendingE Failure to send and buffer Store Idle
+ space available event
+ record
+
+ PendingE Failure to send and no buffer Idle
+ space available
+
+ PendingE Failed accounting event answer Idle
+ received
+
+ PendingB Successful accounting answer Delete Idle
+ received record
+
+ PendingB Failure to send Idle
+
+ PendingB Failed accounting answer Delete Idle
+ received record
+
+ PendingL Successful accounting Idle
+ stop answer received
+
+ PendingL Failure to send and buffer Store Idle
+ 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
+
+ PendingL Failed accounting stop answer Idle
+ received
+
+
+ SERVER, STATELESS ACCOUNTING
+ State Event Action New State
+ ---------------------------------------------------------------
+
+ Idle Accounting start request Send Idle
+ received and successfully accounting
+ processed. start
+ answer
+
+ Idle Accounting event request Send Idle
+ received and successfully accounting
+ processed. event
+ answer
+
+ Idle Interim record received Send Idle
+ and successfully processed. accounting
+ interim
+ answer
+
+ Idle Accounting stop request Send Idle
+ received and successfully accounting
+ processed stop answer
+
+ Idle Accounting request received; Send Idle
+ no space left to store accounting
+ 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
+ processed. start
+ answer;
+ Start Ts
+
+ Idle Accounting event request Send Idle
+ received and successfully accounting
+ processed. event
+ answer
+ Idle Accounting request received; Send Idle
+ no space left to store accounting
+ records answer;
+ Result-Code =
+ OUT_OF_
+ SPACE
+
+ Open Interim record received Send Open
+ and successfully processed. accounting
+ interim
+ answer;
+ Restart Ts
+
+ Open Accounting stop request Send Idle
+ received and successfully accounting
+ processed stop answer;
+ Stop Ts
+
+ Open Accounting request received; Send Idle
+ no space left to store accounting
+ records answer;
+ Result-Code =
+ OUT_OF_
+ 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 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 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
+ 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
+ user be re-authenticated and/or re-authorized.
+
+
+ Message Format
+
+ &lt;RAR> ::= &lt; Diameter Header: 258, REQ, PXY >
+ &lt; Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ { Re-Auth-Request-Type }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+8.3.2. Re-Auth-Answer
+
+ 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 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
+
+ &lt;RAA> ::= &lt; Diameter Header: 258, PXY >
+ &lt; Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+8.4. Session Termination
+
+ It is necessary for a Diameter server that authorized a session, for
+ which it is maintaining state, to be notified when that session is no
+ longer active, both for tracking purposes as well as to allow
+ stateful agents to release any resources that they may have provided
+ for the user's session. For sessions whose state is not being
+ maintained, this section is not used.
+
+ When a user session that required Diameter authorization terminates,
+ the access device that provided the service MUST issue a Session-
+ Termination-Request (STR) message to the Diameter server that
+ authorized the service, to notify it that the session is no longer
+ active. An STR MUST be issued when a user session terminates for any
+ reason, including user logoff, expiration of Session-Timeout,
+ administrative action, termination upon receipt of an Abort-Session-
+ Request (see below), orderly shutdown of the access device, etc.
+
+ The access device also MUST issue an STR for a session that was
+ authorized but never actually started. This could occur, for
+ example, due to a sudden resource shortage in the access device, or
+ because the access device is unwilling to provide the type of service
+ requested in the authorization, or because the access device does not
+ support a mandatory AVP returned in the authorization, etc.
+
+ 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
+ NO_STATE_MAINTAINED, a proxy that causes an authorized session not to
+ be started MUST issue an STR to the Diameter server that authorized
+ the session, since the access device has no way of knowing that the
+ session had been authorized.
+
+ 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.
+
+ A Diameter server also MUST clean up resources when the Session-
+ Timeout expires, or when the Authorization-Lifetime and the Auth-
+ 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.
+
+8.4.1. Session-Termination-Request
+
+ 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
+ authenticated and/or authorized session is being terminated.
+
+ Message Format
+
+ &lt;STR> ::= &lt; Diameter Header: 275, REQ, PXY >
+ &lt; Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Auth-Application-Id }
+ { Termination-Cause }
+ [ User-Name ]
+ [ Destination-Host ]
+ * [ Class ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 112]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+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 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
+
+ &lt;STA> ::= &lt; Diameter Header: 275, PXY >
+ &lt; Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ * [ Class ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Origin-State-Id ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+8.5. Aborting a Session
+
+ A Diameter server may request that the access device stop providing
+ service for a particular session by issuing an Abort-Session-Request
+ (ASR).
+
+ For example, the Diameter server that originally authorized the
+ session may be required to cause that session to be stopped for lack
+ of credit or other reasons that were not anticipated when the session
+ was first authorized.
+
+ 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
+ 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
+ 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.
+
+ Message Format
+
+ &lt;ASR> ::= &lt; Diameter Header: 274, REQ, PXY >
+ &lt; Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+8.5.2. Abort-Session-Answer
+
+ 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
+ disposition of the request.
+
+ If the session identified by Session-Id in the ASR was successfully
+ 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, the Result-Code is set to
+ DIAMETER_UNABLE_TO_COMPLY.
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 114]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Message Format
+
+ &lt;ASA> ::= &lt; Diameter Header: 274, PXY >
+ &lt; Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+8.6. Inferring Session Termination from Origin-State-Id
+
+ The Origin-State-Id is used to allow detection of terminated sessions
+ for which no STR would have been issued, due to unanticipated
+ 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 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 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 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.
+
+ 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 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.
+ 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 it MUST
+ contain the relevant application-specific authentication AVPs that
+ are needed by the Diameter server to authenticate the user.
+
+ AUTHORIZE_ONLY 2
+
+ 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
+ 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).
+
+ 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 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.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):
+
+ &lt;DiameterIdentity>;&lt;high 32 bits>;&lt;low 32 bits>[;&lt;optional value>]
+
+ &lt;high 32 bits> and &lt;low 32 bits> are decimal representations of the
+ high and low 32 bits of a monotonically increasing 64-bit value. The
+ 64-bit value is rendered in two part to simplify formatting by 32-bit
+ 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
+ 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.
+
+
+ &lt;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:
+
+ accesspoint7.example.com;1876543210;523
+
+ Example, in which there is an optional value:
+
+ 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.
+
+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
+ 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-
+ 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 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
+ value that is equal to, or smaller than, the one provided by the
+ client.
+
+8.10. Auth-Grace-Period AVP
+
+ The Auth-Grace-Period AVP (AVP Code 276) is of type Unsigned32 and
+ contains the number of seconds the Diameter server will wait
+ following the expiration of the Authorization-Lifetime AVP before
+ cleaning up resources for the session.
+
+8.11. Auth-Session-State AVP
+
+ The Auth-Session-State AVP (AVP Code 277) is of type Enumerated and
+ specifies whether state is maintained for a particular session. The
+ client MAY include this AVP in requests as a hint to the server, but
+ 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
+ maintained, and the access device MUST issue a session termination
+ 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.
+
+
+
+
+Fajardo, et al. Standards Track [Page 118]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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
+ and contains the maximum number of seconds of service to be provided
+ to the user before termination of the session. When both the
+ Session-Timeout and the Authorization-Lifetime AVPs are present in an
+ answer message, the former MUST be equal to or greater than the value
+ of the latter.
+
+ 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).
+
+ A Session-Timeout AVP MAY be present in a re-authorization answer
+ 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
+ has an unlimited number of seconds before termination.
+
+ 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
+ client.
+
+8.14. User-Name AVP
+
+ The User-Name AVP (AVP Code 1) [RFC2865] is of type UTF8String, which
+ contains the User-Name, in a format consistent with the NAI
+ specification [RFC4282].
+
+
+
+
+Fajardo, et al. Standards Track [Page 119]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+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 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.
+
+ 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
+ Origin-State-Id to the time of startup, or it MAY use an incrementing
+ counter retained in non-volatile memory across restarts.
+
+ The Origin-State-Id, if present, MUST reflect the state of the entity
+ indicated by Origin-Host. If a proxy modifies Origin-Host, it MUST
+ either remove Origin-State-Id or modify it appropriately as well.
+ Typically, Origin-State-Id is used by an access device that always
+ starts up with no active sessions; that is, any session active prior
+ to restart will have been lost. By including Origin-State-Id in a
+ 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.
+
+8.17. Session-Binding AVP
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+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
+ include the Destination-Host AVP. When cleared, the default
+ 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
+ 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
+ include the Destination-Host AVP. When cleared, the default
+ 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
+ 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
+ value. If present, this AVP MAY inform the Diameter client that if a
+ re-auth or STR message fails due to a delivery problem, the Diameter
+ client SHOULD issue a subsequent message without the Destination-Host
+ AVP. When absent, the default value is REFUSE_SERVICE.
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 121]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ TRY_AGAIN 1
+
+ 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
+ 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
+ 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
+ request.
+
+8.20. Class AVP
+
+ The Class AVP (AVP Code 25) is of type OctetString and is used by
+ Diameter servers to return state information to the access device.
+ When one or more Class AVPs are present in application-specific
+ authorization answer messages, they MUST be present in subsequent re-
+ authorization, session termination and accounting messages. Class
+ AVPs found in a re-authorization answer message override the ones
+ found in any previous authorization answer message. Diameter server
+ implementations SHOULD NOT return Class AVPs that require more than
+ 4096 bytes of storage on the Diameter client. A Diameter client that
+ receives Class AVPs whose size exceeds local available storage MUST
+ terminate the session.
+
+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.
+
+
+
+
+Fajardo, et al. Standards Track [Page 122]
+
+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 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.
+
+9.1. Server Directed Model
+
+ The server directed model means that the device generating the
+ accounting data gets information from either the authorization server
+ (if contacted) or the accounting server regarding the way accounting
+ data shall be forwarded. This information includes accounting record
+ 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
+ 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.
+
+ The authorization server (chain) directs the selection of proper
+ transfer strategy, based on its knowledge of the user and
+ relationships of roaming partnerships. The server (or agents) uses
+ the Acct-Interim-Interval and Accounting-Realtime-Required AVPs to
+ control the operation of the Diameter peer operating as a client.
+ The Acct-Interim-Interval AVP, when present, instructs the Diameter
+ node acting as a client to produce accounting records continuously
+ even during a session. Accounting-Realtime-Required AVP is used to
+ control the behavior of the client when the transfer of accounting
+ records from the Diameter client is delayed or unsuccessful.
+
+ The Diameter accounting server MAY override the 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.
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 123]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+9.2. Protocol Messages
+
+ A Diameter node that receives a successful authentication and/or
+ 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
+ to confirm reception. The Accounting-Answer message includes the
+ Result-Code AVP, which MAY indicate that an error was present in the
+ accounting message. The value of the Accounting-Realtime-Required
+ AVP received earlier for the session in question may indicate that
+ the user's session has to be terminated when a rejected Accounting-
+ Request message was received.
+
+9.3. Accounting Application Extension and Requirements
+
+ 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:
+
+ Split Accounting Service
+
+ The accounting message will carry the Application Id of the
+ Diameter base accounting application (see Section 2.4).
+ Accounting messages may be routed to Diameter nodes other than the
+ corresponding Diameter application. These nodes might be
+ centralized accounting servers that provide accounting service for
+ multiple different Diameter applications. These nodes MUST
+ advertise the Diameter base accounting Application Id during
+ capabilities exchange.
+
+ Coupled Accounting Service
+
+ 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 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. Standards Track [Page 124]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+9.4. Fault Resilience
+
+ 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 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
+ accounting records over reboots or extended network failures, network
+ 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 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
+ 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 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
+
+ In all accounting records, the Session-Id AVP MUST be present; the
+ User-Name AVP MUST be present if it is available to the Diameter
+ client.
+
+ 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. Standards Track [Page 125]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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.
+
+ If the accounted service is of a measurable length, then the AVP MUST
+ use the values START_RECORD, STOP_RECORD, and possibly,
+ INTERIM_RECORD. If the authorization server has not directed interim
+ accounting to be enabled for the session, two accounting records MUST
+ be generated for each service of type session. When the initial
+ Accounting-Request for a given session is sent, the Accounting-
+ Record-Type AVP MUST be set to the value START_RECORD. When the last
+ Accounting-Request is sent, the value MUST be STOP_RECORD.
+
+ If the authorization server has directed interim accounting to be
+ enabled, the Diameter client MUST produce additional records between
+ the START_RECORD and STOP_RECORD, marked INTERIM_RECORD. The
+ production of these records is directed by Acct-Interim-Interval as
+ well as any re-authentication or re-authorization of the session.
+ The Diameter client MUST overwrite any previous interim accounting
+ records that are locally stored for delivery, if a new record is
+ being generated for the same session. This ensures that only one
+ pending interim record can exist on an access device for any given
+ session.
+
+ A particular value of Accounting-Sub-Session-Id MUST appear only in
+ 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_RECORDs and a single
+ STOP_RECORD. A particular Diameter application specification MUST
+ define the type of sequences that MUST be used.
+
+9.6. Correlation of Accounting Records
+
+ If an application uses accounting messages, it can correlate
+ 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-
+ related information is needed to perform correlation.
+
+ In cases where an application requires multiple accounting sub-
+ 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 used to correlate all the sub-sessions to a
+ particular application session. Note that receiving a STOP_RECORD
+
+
+
+
+Fajardo, et al. Standards Track [Page 126]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ with no Accounting-Sub-Session-Id AVP when sub-sessions were
+ originally used in the START_RECORD messages implies that all sub-
+ sessions are terminated.
+
+ There are also cases where an application needs to correlate multiple
+ application sessions into a single accounting record; the accounting
+ 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
+ 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. 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 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.
+
+9.7. Accounting Command Codes
+
+ 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
+ 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.
+
+ In addition to the AVPs listed below, Accounting-Request messages
+ SHOULD include service-specific accounting AVPs.
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 127]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Message Format
+
+ &lt;ACR> ::= &lt; Diameter Header: 271, REQ, PXY >
+ &lt; Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Destination-Host ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+9.7.2. Accounting-Answer
+
+ 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,
+ SHOULD respond with the Accounting-Answer command.
+
+ In addition to the AVPs listed below, Accounting-Answer messages
+ SHOULD include service-specific accounting AVPs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 128]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ Message Format
+
+ &lt;ACA> ::= &lt; Diameter Header: 271, PXY >
+ &lt; Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+9.8. Accounting AVPs
+
+ This section contains AVPs that describe accounting usage information
+ related to a specific session.
+
+9.8.1. Accounting-Record-Type AVP
+
+ The Accounting-Record-Type AVP (AVP Code 480) is of type Enumerated
+ 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 it is the only record of the service.
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 129]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ START_RECORD 2
+
+ 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
+ information for an existing accounting session. Interim
+ Accounting Records SHOULD be sent every time a re-authentication
+ or re-authorization occurs. Further, additional interim record
+ triggers MAY be defined by application-specific Diameter
+ 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
+ is sent from the Diameter home authorization server to the Diameter
+ 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
+ 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. Standards Track [Page 130]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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.
+
+ The client MUST ensure that the interim record production times
+ are randomized so that large accounting message storms are not
+ created either among records or around a common service start
+ time.
+
+9.8.3. Accounting-Record-Number AVP
+
+ 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
+ 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
+ INTERIM_RECORD, 2 for the second, and so on until the value for
+ STOP_RECORD is one more than for the last INTERIM_RECORD.
+
+9.8.4. Acct-Session-Id AVP
+
+ The Acct-Session-Id AVP (AVP Code 44) is of type OctetString is only
+ used when RADIUS/Diameter translation occurs. This AVP contains the
+ contents of the RADIUS Acct-Session-Id attribute.
+
+9.8.5. Acct-Multi-Session-Id AVP
+
+ 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 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
+
+ The Accounting-Sub-Session-Id AVP (AVP Code 287) is of type
+ Unsigned64 and contains the accounting sub-session identifier. The
+ combination of the Session-Id and this AVP MUST be unique per sub-
+ 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. Standards Track [Page 131]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+9.8.7. Accounting-Realtime-Required AVP
+
+ The Accounting-Realtime-Required AVP (AVP Code 483) is of type
+ Enumerated and is sent from the Diameter home authorization server to
+ the Diameter client or in the Accounting-Answer from the accounting
+ server. The client uses information in this AVP to decide what to do
+ 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
+ service MUST only be granted as long as there is a connection to
+ an accounting server. Note that the set of alternative accounting
+ servers are treated as one server in this sense. Having to move
+ 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
+ SHOULD be granted if there is a connection, or as long as records
+ can still be stored as described in Section 9.4.
+
+ 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. Standards Track [Page 132]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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.
+
+10.1. Base Protocol Command AVP Table
+
+ The table in this section is limited to the non-Accounting Command
+ Codes defined in this specification.
+
+ +-----------------------------------------------+
+ | Command Code |
+ +---+---+---+---+---+---+---+---+---+---+---+---+
+ Attribute Name |CER|CEA|DPR|DPA|DWR|DWA|RAR|RAA|ASR|ASA|STR|STA|
+ --------------------+---+---+---+---+---+---+---+---+---+---+---+---+
+ Acct-Interim- |0 |0 |0 |0 |0 |0 |0-1|0 |0 |0 |0 |0 |
+ Interval | | | | | | | | | | | | |
+ Accounting-Realtime-|0 |0 |0 |0 |0 |0 |0-1|0 |0 |0 |0 |0 |
+ Required | | | | | | | | | | | | |
+ Acct-Application-Id |0+ |0+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Auth-Application-Id |0+ |0+ |0 |0 |0 |0 |1 |0 |1 |0 |1 |0 |
+ Auth-Grace-Period |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Auth-Request-Type |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Auth-Session-State |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Authorization- |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Lifetime | | | | | | | | | | | | |
+ 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 |
+ 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-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|
+ Product-Name |1 |1 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Proxy-Info |0 |0 |0 |0 |0 |0 |0+ |0+ |0+ |0+ |0+ |0+ |
+ Redirect-Host |0 |0 |0 |0 |0 |0 |0 |0+ |0 |0+ |0 |0+ |
+ Redirect-Host-Usage |0 |0 |0 |0 |0 |0 |0 |0-1|0 |0-1|0 |0-1|
+ Redirect-Max-Cache- |0 |0 |0 |0 |0 |0 |0 |0-1|0 |0-1|0 |0-1|
+ Time | | | | | | | | | | | | |
+ Result-Code |0 |1 |0 |1 |0 |1 |0 |1 |0 |1 |0 |1 |
+ Re-Auth-Request-Type|0 |0 |0 |0 |0 |0 |1 |0 |0 |0 |0 |0 |
+ Route-Record |0 |0 |0 |0 |0 |0 |0+ |0 |0+ |0 |0+ |0 |
+ Session-Binding |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Session-Id |0 |0 |0 |0 |0 |0 |1 |1 |1 |1 |1 |1 |
+ Session-Server- |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Failover | | | | | | | | | | | | |
+ Session-Timeout |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Supported-Vendor-Id |0+ |0+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Termination-Cause |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |1 |0 |
+ User-Name |0 |0 |0 |0 |0 |0 |0-1|0-1|0-1|0-1|0-1|0-1|
+ Vendor-Id |1 |1 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Vendor-Specific- |0+ |0+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
+ Application-Id | | | | | | | | | | | | |
+ --------------------+---+---+---+---+---+---+---+---+---+---+---+---+
+
+10.2. Accounting AVP Table
+
+ The table in this section is used to represent which AVPs defined in
+ this document are to be present in the Accounting messages. These
+ AVP occurrence requirements are guidelines, which may be expanded,
+ and/or overridden by application-specific requirements in the
+ Diameter applications documents.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 134]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ +-----------+
+ | Command |
+ | Code |
+ +-----+-----+
+ Attribute Name | ACR | ACA |
+ ------------------------------+-----+-----+
+ Acct-Interim-Interval | 0-1 | 0-1 |
+ Acct-Multi-Session-Id | 0-1 | 0-1 |
+ Accounting-Record-Number | 1 | 1 |
+ Accounting-Record-Type | 1 | 1 |
+ Acct-Session-Id | 0-1 | 0-1 |
+ Accounting-Sub-Session-Id | 0-1 | 0-1 |
+ Accounting-Realtime-Required | 0-1 | 0-1 |
+ Acct-Application-Id | 0-1 | 0-1 |
+ Auth-Application-Id | 0 | 0 |
+ Class | 0+ | 0+ |
+ Destination-Host | 0-1 | 0 |
+ 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+ |
+ Route-Record | 0+ | 0 |
+ Result-Code | 0 | 1 |
+ Session-Id | 1 | 1 |
+ Termination-Cause | 0 | 0 |
+ User-Name | 0-1 | 0-1 |
+ 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. Standards Track [Page 135]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+11.1.1. AVP Codes
+
+ 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.2. AVP Flags
+
+ Section 4.1 describes the existing AVP Flags. The remaining bits can
+ only be assigned via a Standards Action [RFC5226].
+
+11.2. Diameter Header
+
+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
+ 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
+ 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 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
+ 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; 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
+
+ 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.3.5. Redirect-Host-Usage AVP Values
+
+ New values are available for assignment via IETF Review [RFC5226].
+
+11.3.6. Session-Server-Failover AVP Values
+
+ New values are available for assignment via IETF Review [RFC5226].
+
+11.3.7. Session-Binding AVP Values
+
+ 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 &lt;[email protected]>
+
+ Contact: IETF Chair &lt;[email protected]>
+
+ Description: Diameter over TLS/TCP and DTLS/SCTP
+
+ Reference: RFC 6733
+
+ Port Number: 5868, from the User Range
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 138]
+
+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
+
+ 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:
+
+ Diameter Peer
+
+ A Diameter entity MAY communicate with peers that are statically
+ configured. A statically configured Diameter peer would require
+ that either the IP address or the fully qualified domain name
+ (FQDN) be supplied, which would then be used to resolve through
+ DNS.
+
+ Routing Table
+
+ 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. The routing table MAY also include a
+ "default route", which is typically used for all messages that
+ cannot be locally processed.
+
+ Tc timer
+
+ The Tc timer controls the frequency that transport connection
+ attempts are done to a peer with whom no active transport
+ connection exists. The recommended value is 30 seconds.
+
+
+
+Fajardo, et al. Standards Track [Page 139]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+13. Security Considerations
+
+ The Diameter base protocol messages SHOULD be secured by using TLS
+ [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 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 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 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 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
+ 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 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:
+
+ TLS_RSA_WITH_RC4_128_MD5
+ TLS_RSA_WITH_RC4_128_SHA
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA
+
+ Diameter nodes SHOULD be able to negotiate the following TLS/TCP and
+ DTLS/SCTP cipher suite:
+
+ TLS_RSA_WITH_AES_128_CBC_SHA
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 140]
+
+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.
+
+ 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
+
+ As with any peer-to-peer protocol, proper configuration of the trust
+ model within a Diameter peer is essential to security. When
+ certificates are used, it is necessary to configure the root
+ certificate authorities trusted by the Diameter peer. These root CAs
+ are likely to be unique to Diameter usage and distinct from the root
+ CAs that might be trusted for other purposes such as Web browsing.
+ In general, it is expected that those root CAs will be configured so
+ as to reflect the business relationships between the organization
+ hosting the Diameter peer and other organizations. As a result, a
+ Diameter peer will typically not be configured to allow connectivity
+ with any arbitrary peer. With certificate authentication, Diameter
+ 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",
+ &lt;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. Standards Track [Page 142]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ [RFC3539] Aboba, B. and J. Wood, "Authentication, Authorization and
+ Accounting (AAA) Transport Profile", RFC 3539, June 2003.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [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.
+
+ [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,
+ August 2005.
+
+ [RFC4005] Calhoun, P., Zorn, G., Spence, D., and D. Mitton,
+ "Diameter Network Access Server Application", RFC 4005,
+ August 2005.
+
+ [RFC4006] Hakala, H., Mattila, L., Koskinen, J-P., Stura, M., and J.
+ Loughney, "Diameter Credit-Control Application", RFC 4006,
+ August 2005.
+
+ [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
+ Requirements for Security", BCP 106, RFC 4086, June 2005.
+
+ [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.
+
+ [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", STD 68, RFC 5234, January 2008.
+
+ [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
+ (TLS) Protocol Version 1.2", RFC 5246, August 2008.
+
+
+
+
+Fajardo, et al. Standards Track [Page 143]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ [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.
+
+ [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",
+ RFC 5890, August 2010.
+
+ [RFC5891] Klensin, J., "Internationalized Domain Names in
+ Applications (IDNA): Protocol", RFC 5891, August 2010.
+
+ [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
+
+ [ENTERPRISE] IANA, "SMI Network Management Private Enterprise
+ Codes",
+ &lt;http://www.iana.org/assignments/enterprise-numbers>.
+
+ [IANATCV] IANA, "Termination-Cause AVP Values (code 295)",
+ &lt;http://www.iana.org/assignments/aaa-parameters/
+ aaa-parameters.xml#aaa-parameters-16>.
+
+ [RFC1492] Finseth, C., "An Access Control Protocol, Sometimes
+ Called TACACS", RFC 1492, July 1993.
+
+ [RFC1661] Simpson, W., "The Point-to-Point Protocol (PPP)",
+ STD 51, RFC 1661, July 1994.
+
+ [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC:
+ Keyed-Hashing for Message Authentication", RFC 2104,
+ February 1997.
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 144]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR
+ for specifying the location of services (DNS SRV)",
+ RFC 2782, February 2000.
+
+ [RFC2865] Rigney, C., Willens, S., Rubens, A., and W. Simpson,
+ "Remote Authentication Dial In User Service (RADIUS)",
+ RFC 2865, June 2000.
+
+ [RFC2866] Rigney, C., "RADIUS Accounting", RFC 2866, June 2000.
+
+ [RFC2869] Rigney, C., Willats, W., and P. Calhoun, "RADIUS
+ Extensions", RFC 2869, June 2000.
+
+ [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.
+
+ [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.
+
+ [RFC4301] Kent, S. and K. Seo, "Security Architecture for the
+ Internet Protocol", RFC 4301, December 2005.
+
+ [RFC4690] Klensin, J., Faltstrom, P., Karp, C., and IAB, "Review
+ and Recommendations for Internationalized Domain Names
+ (IDNs)", RFC 4690, September 2006.
+
+ [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.
+
+
+
+
+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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fajardo, et al. Standards Track [Page 146]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+Appendix A. Acknowledgements
+
+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
+ contributions on almost all of the proposed solutions incorporated
+ 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
+ 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. 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:
+
+ Avi Lior, Jari Arkko, Glen Zorn, Lionel Morand, Mark Jones, Tolga
+ 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, 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.
+
+ 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
+
+
+
+
+Fajardo, et al. Standards Track [Page 147]
+
+RFC 6733 Diameter Base Protocol October 2012
+
+
+ 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 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
+ comments as a result of a thorough review of the spec.
+
+ The authors would also like to acknowledge the following people for
+ their contribution in the development of the Diameter protocol:
+
+ Allan C. Rubens, Haseeb Akhtar, William Bulley, Stephen Farrell,
+ 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
+ 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.
+
+Appendix B. S-NAPTR Example
+
+ As an example, consider a client that wishes to resolve aaa:
+ 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.ex1.example.com
+ IN NAPTR 100 50 "s" "aaa:diameter.tcp" ""
+ _aaa._tcp.ex1.example.com
+ IN NAPTR 150 50 "s" "aaa:diameter.sctp" ""
+ _diameter._sctp.ex1.example.com
+
+ 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
+
+
+
+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.ex1.example.com
+ IN SRV 0 2 5060 server2.ex1.example.com
+
+ As an alternative example, a client that wishes to resolve aaa:
+ 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.ex2.example.com
+ IN NAPTR 150 50 "a" "aaa:diameter.tls.tcp" ""
+ server2.ex2.example.com
+
+ This indicates that the server supports TCP available at the returned
+ host names.
+
+Appendix C. Duplicate Detection
+
+ As described in Section 9.4, accounting record duplicate detection is
+ based on session identifiers. Duplicates can appear for various
+ reasons:
+
+ o Failover to an alternate server. Where close to real-time
+ 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
+ retransmission behavior of RADIUS is not defined within [RFC2865],
+ the likelihood of duplication will vary according to the
+ implementation.
+
+ o Implementation problems and misconfiguration.
+
+ 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
+ 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
+ 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
+ real-time duplicate detection, such as when credit limits are imposed
+ or real-time fraud detection is desired.
+
+ 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 a possible duplicate by setting the T
+ flag. Since the Diameter server is responsible for duplicate
+ 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
+ fault, perhaps a day. So only records within this time window need
+ to be looked at in the backward direction. Secondly, hashing
+ techniques or other schemes, such as the use of the T flag in the
+ received messages, may be used to eliminate the need to do a full
+ search even in this set except for rare cases.
+
+ 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
+
+
+
+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
+ 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 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.
+
+Appendix D. Internationalized Domain Names
+
+ To be compatible with the existing DNS infrastructure and simplify
+ host and domain name comparison, Diameter identities (FQDNs) are
+ represented in ASCII form. This allows the Diameter protocol to fall
+ in-line with the DNS strategy of being transparent from the effects
+ of Internationalized Domain Names (IDNs) by following the
+ 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].
+</pre>
+
+</section>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index e3b8c733b7..01c781d553 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -26,7 +26,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -65,9 +65,8 @@ It can be specified as the value of a <c>transport_module</c> option to
&mod_add_transport;
and implements the behaviour documented in
&man_transport;.
-TLS security is supported, both as an upgrade following
-capabilities exchange as specified by &the_rfc; and
-at connection establishment as in the current draft standard.</p>
+TLS security is supported, either as an upgrade following
+capabilities exchange or at connection establishment.</p>
<p>
Note that the ssl application is required for TLS and must be started
@@ -94,7 +93,8 @@ before configuring TLS capability on diameter transports.</p>
<v>Reason = term()</v>
<v>OwnOpt = {raddr, &ip_address;}
| {rport, integer()}
- | {port, integer()}</v>
+ | {port, integer()}
+ | {fragment_timer, infinity | 0..16#FFFFFFFF}</v>
<v>SslOpt = {ssl_options, true | list()}</v>
<v>TcpOpt = term()</v>
</type>
@@ -104,7 +104,6 @@ before configuring TLS capability on diameter transports.</p>
The start function required by &man_transport;.</p>
<p>
-The only diameter_tcp-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 are not valid for a listening
transport.
@@ -113,7 +112,18 @@ that should support TLS: a value of <c>true</c> results in a
TLS handshake immediately upon connection establishment while
<c>list()</c> specifies options to be passed to &ssl_connect2; or
&ssl_accept2;
-after capabilities exchange if TLS is negotiated.
+after capabilities exchange if TLS is negotiated.</p>
+
+<p>
+Option <c>fragment_timer</c> specifies the timeout, in milliseconds,
+of a timer used to flush messages from the incoming byte
+stream even if the number of bytes indicated in the Message Length
+field of its Diameter Header have not yet been accumulated:
+such a message is received over the transport interface after
+two successive timeouts without the reception of additional bytes.
+Defaults to 1000.</p>
+
+<p>
Remaining options are any accepted by &ssl_connect3; or
&gen_tcp_connect3; for
a connecting transport, or &ssl_listen2; or &gen_tcp_listen2; for
diff --git a/lib/diameter/doc/src/files.mk b/lib/diameter/doc/src/files.mk
index 00ced3d91e..510786a7fb 100644
--- a/lib/diameter/doc/src/files.mk
+++ b/lib/diameter/doc/src/files.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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
@@ -38,7 +38,9 @@ XML_REF4_FILES = \
XML_PART_FILES = \
user_man.xml
-XML_EXTRA_FILES =
+XML_EXTRA_FILES = \
+ seealso.ent \
+ diameter_soc_rfc6733.xml
XML_CHAPTER_FILES = \
diameter_intro.xml \
@@ -50,5 +52,4 @@ XML_CHAPTER_FILES = \
BOOK_FILES = \
book.xml
-GIF_FILES = \
- notes.gif
+GIF_FILES =
diff --git a/lib/diameter/doc/src/notes.gif b/lib/diameter/doc/src/notes.gif
deleted file mode 100644
index e000cca26a..0000000000
--- a/lib/diameter/doc/src/notes.gif
+++ /dev/null
Binary files differ
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index d241e2bd19..2daf84b0d4 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -11,7 +11,7 @@
<header>
<copyright>
<year>2011</year>
-<year>2012</year>
+<year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -42,6 +42,209 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>Diameter 1.4.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken Vendor-Specific-Application-Id configuration.</p>
+ <p>
+ RFC 6733 changed the definition of this Grouped AVP,
+ changing the arity of Vendor-Id from 1* to 1. A component
+ Vendor-Id can now be either list- or integer-valued in
+ service and transport configuration, allowing it to be
+ used with both RFC 3588 and RFC 6733 dictionaries.</p>
+ <p>
+ Own Id: OTP-10942</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add transport_opt() watchdog_config to allow non-standard
+ behaviour of the watchdog state machine.</p>
+ <p>
+ This can be useful during test but should not be used on
+ nodes that must conform to RFC 3539.</p>
+ <p>
+ Own Id: OTP-10898</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Diameter 1.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix erroneous watchdog transition from DOWN to INITIAL.</p>
+ <p>
+ This transition took place when a peer connection was
+ reestablished following a failed capabilities exchange.
+ RFC 3539 requires DOWN to transition into REOPEN.</p>
+ <p>
+ Own Id: OTP-10692</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add application_opt() request_errors to make the handling
+ of incoming requests containing decode errors
+ configurable.</p>
+ <p>
+ The value 'callback' ensures that a handle_request
+ callback takes place for all such requests, the default
+ being for diameter to answer 3xxx series errors itself.</p>
+ <p>
+ Own Id: OTP-10686</p>
+ </item>
+ <item>
+ <p>
+ Add transport_opt() length_errors.</p>
+ <p>
+ The value determines how messages received over the
+ transport interface with an incorrect Message Length are
+ dealt with.</p>
+ <p>
+ Own Id: OTP-10687</p>
+ </item>
+ <item>
+ <p>
+ Add commentary on RFC 6733 to Standards Compliance
+ chapter of the User's Guide.</p>
+ <p>
+ Own Id: OTP-10688</p>
+ </item>
+ <item>
+ <p>
+ Allow a 5xxx result code in an answer-message on peer
+ connections using the RFC 6733 common dictionary.</p>
+ <p>
+ RFC 6733 allows this while RFC 3588 does not. A
+ handle_request callback can return {answer_message,
+ 3000..3999|5000..5999} in the simplest case.</p>
+ <p>
+ Own Id: OTP-10759</p>
+ </item>
+ <item>
+ <p>
+ Add dictionaries for RFC 6733.</p>
+ <p>
+ Both the common and accounting dictionaries differ from
+ their RFC 3588 counterparts, which is reflected in
+ generated record definitions. Application configuration
+ on a service or transport determines the dictionary that
+ will be used on a given peer connection.</p>
+ <p>
+ Own Id: OTP-10760</p>
+ </item>
+ <item>
+ <p>
+ Allow a handle_request callback to control diameter's
+ setting of Result-Code and Failed-AVP.</p>
+ <p>
+ Setting errors = false in a returned #diameter_packet{}
+ disables the setting.</p>
+ <p>
+ Own Id: OTP-10761</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Diameter 1.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add registered server names to the app file.</p>
+ <p>
+ Own Id: OTP-10442</p>
+ </item>
+ <item>
+ <p>
+ Fix #diameter_header{} handling broken by OTP-10445.</p>
+ <p>
+ The fault caused the the header of a [Header | Avps]
+ request to be ignored if both end_to_end_id and
+ hop_by_hop_id were undefined.</p>
+ <p>
+ Own Id: OTP-10609</p>
+ </item>
+ <item>
+ <p>
+ Fix error handling for handle_request callback.</p>
+ <p>
+ A callback that returned a #diameter_packet{} would fail
+ if the incoming request had decode errors.</p>
+ <p>
+ Own Id: OTP-10614</p>
+ </item>
+ <item>
+ <p>
+ Fix timing of service start event.</p>
+ <p>
+ The event did not necessarily precede other events as
+ documented.</p>
+ <p>
+ Own Id: OTP-10618</p>
+ </item>
+ <item>
+ <p>
+ Fix setting of header T flag at peer failover.</p>
+ <p>
+ The flag is now set in the diameter_header record passed
+ to a prepare_retransmit callback.</p>
+ <p>
+ Own Id: OTP-10619</p>
+ </item>
+ <item>
+ <p>
+ Fix sending of CER/CEA timeout event at capx_timeout.</p>
+ <p>
+ The event was not sent as documented.</p>
+ <p>
+ Own Id: OTP-10628</p>
+ </item>
+ <item>
+ <p>
+ Fix improper setting of Application-ID in the Diameter
+ header of an answer message whose E flag is set.</p>
+ <p>
+ The value should be that of the request in question. The
+ fault caused it always to be 0.</p>
+ <p>
+ Own Id: OTP-10655</p>
+ </item>
+ <item>
+ <p>
+ Fix faulty handling of AVP length errors.</p>
+ <p>
+ An incorrect AVP length but no other errors caused an
+ incoming request to fail.</p>
+ <p>
+ Own Id: OTP-10693</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Diameter 1.3.1</title>
<section><title>Known Bugs and Problems</title>
diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent
index 4647c42f85..76b9823f79 100644
--- a/lib/diameter/doc/src/seealso.ent
+++ b/lib/diameter/doc/src/seealso.ent
@@ -4,7 +4,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
@@ -41,7 +41,8 @@ significant.
<!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_services '<seealso marker="diameter#services-0">diameter:services/0</seealso>'>
+<!ENTITY mod_session_id '<seealso marker="diameter#session_id-1">diameter:session_id/1</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>'>
@@ -54,6 +55,7 @@ significant.
<!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_event_info '<seealso marker="diameter#service_event_info">diameter:service_event_info()</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>'>
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index beb577afaf..79c4dce541 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -25,7 +25,11 @@
-define(DIAMETER_APP_ID_ACCOUNTING, 3).
-define(DIAMETER_APP_ID_RELAY, 16#FFFFFFFF).
-%% Corresponding dictionaries:
+%% Corresponding dictionaries. These macros are deprecated now that
+%% there is an RFC6733 whose dictionaries are not strictly backwards
+%% compatible. The RFC 6733 common and accounting dictionaries are
+%% diameter_gen_base_rfc6733 and diameter_gen_acct_rfc6733
+%% respectively.
-define(DIAMETER_DICT_COMMON, diameter_gen_base_rfc3588).
-define(DIAMETER_DICT_ACCOUNTING, diameter_gen_base_accounting).
-define(DIAMETER_DICT_RELAY, diameter_gen_relay).
@@ -139,6 +143,6 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- options = [{answer_errors, report}]}). %% | callback | discard
-
+ options = [{answer_errors, report}, %% | callback | discard
+ {request_errors, answer_3xxx}]}). %% | callback | answer
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl
index 13a6c462af..03aa557c2e 100644
--- a/lib/diameter/include/diameter_gen.hrl
+++ b/lib/diameter/include/diameter_gen.hrl
@@ -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
@@ -320,7 +320,7 @@ pack_avp(Name, #diameter_avp{name = AvpName} = Avp, Acc) ->
pack_avp(Name, 0, Avp, Acc) ->
pack_AVP(Name, Avp, Acc);
-pack_avp(_, Arity, Avp, Acc) ->
+pack_avp(_, Arity, Avp, Acc) ->
pack(Arity, Avp#diameter_avp.name, Avp, Acc).
%% pack_AVP/3
@@ -332,7 +332,7 @@ pack_AVP(Name, Avp, Acc) ->
Arity ->
pack(Arity, 'AVP', Avp, Acc)
end.
-
+
%% 3588:
%%
%% DIAMETER_AVP_UNSUPPORTED 5001
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 060659bce9..df10c33268 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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
@@ -104,12 +104,13 @@ endif
ERL_COMPILE_FLAGS += \
+'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ +'{attribute,insert,app_vsn,"$(APP_VSN)"}' \
+warn_export_vars \
+warn_unused_vars \
-pa $(ABS_EBIN) \
-I $(INCDIR) \
- -I gen
+ -I gen \
+ $(STRICT_FLAGS)
# -pa is to be able to include_lib from the include directory: the
# path must contain the application name.
@@ -119,25 +120,44 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
+ $(dia_verbose) \
../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
+# Compile with -Werror during development. Don't do this in the 'opt'
+# target so that new warnings don't break the build. It's also
+# convenient to have both targets when weeding out warnings isn't the
+# priority. (Or when they're intentional, when debugging.)
+strict:
+ $(MAKE) opt STRICT_FLAGS=-Werror
+
+# Build unofficial patches with some degree of traceability. Refuse to
+# build if there are diffs from HEAD since that defeats the purpose.
+patch:
+ git diff --exit-code HEAD
+ $(MAKE) opt PRE_VSN="-$(shell git rev-list --max-count=1 HEAD | cut -c 1-8)"
+
debug:
@$(MAKE) TYPE=debug opt
# The dictionary parser.
gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl
+ $(yecc_verbose) \
$(ERLC) -Werror -o $(@D) $<
# Generate the app file.
$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
+ $(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
+ $(vsn_verbose) \
sed -e 's;%VSN%;$(VSN);' $< > $@
app: $(APP_TARGET) $(APPUP_TARGET)
@@ -239,11 +259,15 @@ release_docs_spec:
# Dependencies
# ----------------------------------------------------
-gen/diameter_gen_base_accounting.erl gen/diameter_gen_relay.erl \
-gen/diameter_gen_base_accounting.hrl gen/diameter_gen_relay.hrl: \
+gen/diameter_gen_base_accounting.erl gen/diameter_gen_base_accounting.hrl: \
$(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR)
-gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl: \
+gen/diameter_gen_acct_rfc6733.erl gen/diameter_gen_acct_rfc6733.hrl: \
+ $(EBIN)/diameter_gen_base_rfc6733.$(EMULATOR)
+
+gen/diameter_gen_relay.erl gen/diameter_gen_relay.hrl \
+gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl \
+gen/diameter_gen_base_rfc6733.erl gen/diameter_gen_base_rfc6733.hrl: \
$(COMPILER_MODULES:%=$(EBIN)/%.$(EMULATOR))
$(DICT_MODULES:gen/%=$(EBIN)/%.$(EMULATOR)): \
@@ -265,7 +289,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
.PHONY: $(EXAMPLE_DIRS:%/=release_examples_%)
-.PHONY: plt dialyze
+.PHONY: plt dialyze patch strict
# 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..ceefb9b398 100644
--- a/lib/diameter/src/base/diameter.app.src
+++ b/lib/diameter/src/base/diameter.app.src
@@ -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
@@ -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 a04a387918..359f434941 100644
--- a/lib/diameter/src/base/diameter.appup.src
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -20,13 +20,21 @@
{"%VSN%",
[
- {"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}]},
+ {"0.9", [{restart_application, diameter}]}, %% R14B03
+ {"0.10", [{restart_application, diameter}]}, %% R14B04
+ {"1.0", [{restart_application, diameter}]}, %% R15B
+ {"1.1", [{restart_application, diameter}]}, %% R15B01
+ {"1.2", [{restart_application, diameter}]}, %% R15B02
{"1.2.1", [{restart_application, diameter}]},
- {"1.3", [{load_module, diameter_service}]}
+ {"1.3", [{restart_application, diameter}]}, %% R15B03
+ {"1.3.1", [{restart_application, diameter}]},
+ {"1.4", [{restart_application, diameter}]}, %% R16A
+ {"1.4.1", [{load_module, diameter_reg}, %% R16B
+ {load_module, diameter_stats},
+ {load_module, diameter_service},
+ {load_module, diameter_watchdog},
+ {load_module, diameter_capx},
+ {load_module, diameter}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -35,6 +43,9 @@
{"1.1", [{restart_application, diameter}]},
{"1.2", [{restart_application, diameter}]},
{"1.2.1", [{restart_application, diameter}]},
- {"1.3", [{load_module, diameter_service}]}
+ {"1.3", [{restart_application, diameter}]},
+ {"1.3.1", [{restart_application, diameter}]},
+ {"1.4", [{restart_application, diameter}]},
+ {"1.4.1", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 8f9901907a..5f06cef020 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -213,7 +213,7 @@ origin_state_id() ->
-> any().
call(SvcName, App, Message, Options) ->
- diameter_service:call(SvcName, {alias, App}, Message, Options).
+ diameter_traffic:send_request(SvcName, {alias, App}, Message, Options).
call(SvcName, App, Message) ->
call(SvcName, App, Message, []).
@@ -306,7 +306,8 @@ call(SvcName, App, Message) ->
| {module, app_module()}
| {state, any()}
| {call_mutates_state, boolean()}
- | {answer_errors, callback|report|discard}.
+ | {answer_errors, callback|report|discard}
+ | {request_errors, answer_3xxx|answer|callback}.
-type app_alias()
:: any().
@@ -332,8 +333,10 @@ call(SvcName, App, Message) ->
| {capabilities_cb, evaluable()}
| {capx_timeout, 'Unsigned32'()}
| {disconnect_cb, evaluable()}
- | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
+ | {length_errors, exit | handle | discard}
| {reconnect_timer, 'Unsigned32'()}
+ | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
+ | {watchdog_config, [{okay|suspect, non_neg_integer()}]}
| {private, any()}.
%% Predicate passed to remove_transport/2
diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl
index c6c3d2934d..9a443fead0 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-2012. 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
@@ -47,14 +47,13 @@
-module(diameter_capx).
--export([build_CER/1,
- recv_CER/2,
- recv_CEA/2,
+-export([build_CER/2,
+ recv_CER/3,
+ recv_CEA/3,
make_caps/2]).
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
--include("diameter_gen_base_rfc3588.hrl").
-define(SUCCESS, 2001). %% DIAMETER_SUCCESS
-define(NOAPP, 5010). %% DIAMETER_NO_COMMON_APPLICATION
@@ -67,27 +66,31 @@
-type tried(T) :: {ok, T} | {error, {term(), list()}}.
--spec build_CER(#diameter_caps{})
- -> tried(#diameter_base_CER{}).
+-spec build_CER(#diameter_caps{}, module())
+ -> tried(CER)
+ when CER :: tuple().
-build_CER(Caps) ->
- try_it([fun bCER/1, Caps]).
+build_CER(Caps, Dict) ->
+ try_it([fun bCER/2, Caps, Dict]).
--spec recv_CER(#diameter_base_CER{}, #diameter_service{})
+-spec recv_CER(CER, #diameter_service{}, module())
-> tried({[diameter:'Unsigned32'()],
#diameter_caps{},
- #diameter_base_CEA{}}).
+ CEA})
+ when CER :: tuple(),
+ CEA :: tuple().
-recv_CER(CER, Svc) ->
- try_it([fun rCER/2, CER, Svc]).
+recv_CER(CER, Svc, Dict) ->
+ try_it([fun rCER/3, CER, Svc, Dict]).
--spec recv_CEA(#diameter_base_CEA{}, #diameter_service{})
+-spec recv_CEA(CEA, #diameter_service{}, module())
-> tried({[diameter:'Unsigned32'()],
[diameter:'Unsigned32'()],
- #diameter_caps{}}).
+ #diameter_caps{}})
+ when CEA :: tuple().
-recv_CEA(CEA, Svc) ->
- try_it([fun rCEA/2, CEA, Svc]).
+recv_CEA(CEA, Svc, Dict) ->
+ try_it([fun rCEA/3, CEA, Svc, Dict]).
make_caps(Caps, Opts) ->
try_it([fun mk_caps/2, Caps, Opts]).
@@ -161,16 +164,60 @@ ipaddr(A) ->
?THROW(T)
end.
-%% bCER/1
+%% bCER/2
%%
%% Build a CER record to send to a remote peer.
%% Use the fact that diameter_caps has the same field names as CER.
-bCER(#diameter_caps{} = Rec) ->
- #diameter_base_CER{}
- = list_to_tuple([diameter_base_CER | tl(tuple_to_list(Rec))]).
+bCER(#diameter_caps{} = Rec, Dict) ->
+ Values = lists:zip(Dict:'#info-'(diameter_base_CER, fields),
+ tl(tuple_to_list(Rec))),
+ Dict:'#new-'(diameter_base_CER, [{K, map(K, V, Dict)}
+ || {K,V} <- Values]).
-%% rCER/2
+%% map/3
+%%
+%% Deal with differerences in common dictionary AVP's to make changes
+%% transparent in service/transport config. In particular, one
+%% annoying difference between RFC 3588 and RFC 6733.
+%%
+%% RFC 6773 changes the definition of Vendor-Specific-Application-Id,
+%% giving Vendor-Id arity 1 instead of 3588's 1*. This causes woe
+%% since the corresponding dictionaries expect different values for a
+%% 'Vendor-Id': a list for 3588, an integer for 6733.
+
+map('Vendor-Specific-Application-Id', L, Dict) ->
+ Rec = Dict:'#new-'('diameter_base_Vendor-Specific-Application-Id', []),
+ Def = Dict:'#get-'('Vendor-Id', Rec),
+ [vsa(V, Def) || V <- L];
+map(_, V, _) ->
+ V.
+
+vsa({_, N, _, _} = Rec, [])
+ when is_integer(N) ->
+ setelement(2, Rec, [N]);
+
+vsa({_, [N], _, _} = Rec, undefined)
+ when is_integer(N) ->
+ setelement(2, Rec, N);
+
+vsa([_|_] = L, Def) ->
+ [vid(T, Def) || T <- L];
+
+vsa(T, _) ->
+ T.
+
+vid({'Vendor-Id' = K, N}, [])
+ when is_integer(N) ->
+ {K, [N]};
+
+vid({'Vendor-Id' = K, [N]}, undefined) ->
+ {K, N};
+
+vid(T, _) ->
+ T.
+
+%% rCER/3
%%
%% Build a CEA record to send to a remote peer in response to an
%% incoming CER. RFC 3588 gives no guidance on what should be sent
@@ -214,12 +261,9 @@ bCER(#diameter_caps{} = Rec) ->
%% TLS 1
%% This node supports TLS security, as defined by [TLS].
-rCER(CER, #diameter_service{capabilities = LCaps} = Svc) ->
- #diameter_base_CEA{}
- = CEA
- = cea_from_cer(bCER(LCaps)),
-
- RCaps = capx_to_caps(CER),
+rCER(CER, #diameter_service{capabilities = LCaps} = Svc, Dict) ->
+ CEA = cea_from_cer(bCER(LCaps, Dict), Dict),
+ RCaps = capx_to_caps(CER, Dict),
SApps = common_applications(LCaps, RCaps, Svc),
{SApps,
@@ -227,17 +271,18 @@ rCER(CER, #diameter_service{capabilities = LCaps} = Svc) ->
build_CEA(SApps,
LCaps,
RCaps,
- CEA#diameter_base_CEA{'Result-Code' = ?SUCCESS})}.
+ Dict,
+ Dict:'#set-'({'Result-Code', ?SUCCESS}, CEA))}.
-build_CEA([], _, _, CEA) ->
- CEA#diameter_base_CEA{'Result-Code' = ?NOAPP};
+build_CEA([], _, _, Dict, CEA) ->
+ Dict:'#set-'({'Result-Code', ?NOAPP}, CEA);
-build_CEA(_, LCaps, RCaps, CEA) ->
+build_CEA(_, LCaps, RCaps, Dict, CEA) ->
case common_security(LCaps, RCaps) of
[] ->
- CEA#diameter_base_CEA{'Result-Code' = ?NOSECURITY};
+ Dict:'#set-'({'Result-Code', ?NOSECURITY}, CEA);
[_] = IS ->
- CEA#diameter_base_CEA{'Inband-Security-Id' = IS}
+ Dict:'#set-'({'Inband-Security-Id', IS}, CEA)
end.
%% common_security/2
@@ -275,46 +320,41 @@ cs(LS, RS) ->
%% practice something there may be a need for more synchronization
%% than notification by way of an event subscription offers.
-%% cea_from_cer/1
+%% cea_from_cer/2
%% CER is a subset of CEA, the latter adding Result-Code and a few
%% more AVP's.
-cea_from_cer(#diameter_base_CER{} = CER) ->
- lists:foldl(fun(F,A) -> to_cea(CER, F, A) end,
- #diameter_base_CEA{},
- record_info(fields, diameter_base_CER)).
-
-to_cea(CER, Field, CEA) ->
- try ?BASE:'#get-'(Field, CER) of
- V -> ?BASE:'#set-'({Field, V}, CEA)
- catch
- error: _ -> CEA
- end.
-
-%% rCEA/2
+cea_from_cer(CER, Dict) ->
+ [diameter_base_CER | Values] = Dict:'#get-'(CER),
+ Dict:'#set-'(Values, Dict:'#new-'(diameter_base_CEA)).
-rCEA(CEA, #diameter_service{capabilities = LCaps} = Svc) ->
- RCaps = capx_to_caps(CEA),
+%% rCEA/3
+
+rCEA(CEA, #diameter_service{capabilities = LCaps} = Svc, Dict) ->
+ RCaps = capx_to_caps(CEA, Dict),
SApps = common_applications(LCaps, RCaps, Svc),
IS = common_security(LCaps, RCaps),
{SApps, IS, RCaps}.
-%% capx_to_caps/1
-
-capx_to_caps(#diameter_base_CEA{'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'Host-IP-Address' = IP,
- 'Vendor-Id' = VId,
- 'Product-Name' = PN,
- 'Origin-State-Id' = OSI,
- 'Supported-Vendor-Id' = SV,
- 'Auth-Application-Id' = Auth,
- 'Inband-Security-Id' = IS,
- 'Acct-Application-Id' = Acct,
- 'Vendor-Specific-Application-Id' = VSA,
- 'Firmware-Revision' = FR,
- 'AVP' = X}) ->
+%% capx_to_caps/2
+
+capx_to_caps(CEX, Dict) ->
+ [OH, OR, IP, VId, PN, OSI, SV, Auth, IS, Acct, VSA, FR, X]
+ = Dict:'#get-'(['Origin-Host',
+ 'Origin-Realm',
+ 'Host-IP-Address',
+ 'Vendor-Id',
+ 'Product-Name',
+ 'Origin-State-Id',
+ 'Supported-Vendor-Id',
+ 'Auth-Application-Id',
+ 'Inband-Security-Id',
+ 'Acct-Application-Id',
+ 'Vendor-Specific-Application-Id',
+ 'Firmware-Revision',
+ 'AVP'],
+ CEX),
#diameter_caps{origin_host = OH,
origin_realm = OR,
vendor_id = VId,
@@ -327,10 +367,7 @@ capx_to_caps(#diameter_base_CEA{'Origin-Host' = OH,
acct_application_id = Acct,
vendor_specific_application_id = VSA,
firmware_revision = FR,
- avp = X};
-
-capx_to_caps(#diameter_base_CER{} = CER) ->
- capx_to_caps(cea_from_cer(CER)).
+ avp = X}.
%% ---------------------------------------------------------------------------
%% ---------------------------------------------------------------------------
@@ -365,13 +402,12 @@ app_union(#diameter_caps{auth_application_id = U,
vendor_specific_application_id = V}) ->
set_list(U ++ C ++ lists:flatmap(fun vsa_apps/1, V)).
-vsa_apps(#'diameter_base_Vendor-Specific-Application-Id'
- {'Auth-Application-Id' = U,
- 'Acct-Application-Id' = C}) ->
- U ++ C;
-vsa_apps(L) ->
- Rec = ?BASE:'#new-'('diameter_base_Vendor-Specific-Application-Id', L),
- vsa_apps(Rec).
+vsa_apps([_ | [_,_] = Ids]) ->
+ lists:append(Ids);
+vsa_apps(Rec)
+ when is_tuple(Rec) ->
+ [_|T] = tuple_to_list(Rec),
+ vsa_apps(T).
%% It's a configuration error for a locally advertised application not
%% to be represented in Apps. Don't just match on lists:keyfind/3 in
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index a94d37f7a8..e446a0209c 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-2012. 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
@@ -26,7 +26,7 @@
decode_header/1,
sequence_numbers/1,
hop_by_hop_id/2,
- msg_name/1,
+ msg_name/2,
msg_id/1]).
%% Towards generated encoders (from diameter_gen.hrl).
@@ -99,13 +99,13 @@ e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
Eid:32,
Avps/binary>>};
-e(Mod0, #diameter_packet{header = Hdr, msg = Msg} = Pkt) ->
+e(Mod, #diameter_packet{header = Hdr, msg = Msg} = Pkt) ->
#diameter_header{version = Vsn,
hop_by_hop_id = Hid,
end_to_end_id = Eid}
= Hdr,
- {Mod, MsgName} = rec2msg(Mod0, Msg),
+ MsgName = rec2msg(Mod, Msg),
{Code, Flags0, Aid} = msg_header(Mod, MsgName, Hdr),
Flags = make_flags(Flags0, Hdr),
@@ -192,32 +192,24 @@ encode_avps(Avps) ->
%% msg_header/3
msg_header(Mod, 'answer-message' = MsgName, Header) ->
- ?BASE = Mod,
- #diameter_header{cmd_code = Code} = Header,
- {_, Flags, ApplId} = ?BASE:msg_header(MsgName),
- {Code, Flags, ApplId};
+ 0 = Mod:id(), %% assert
+ #diameter_header{application_id = Aid,
+ cmd_code = Code}
+ = Header,
+ {-1, Flags, ?DIAMETER_APP_ID_COMMON} = Mod:msg_header(MsgName),
+ {Code, Flags, Aid};
msg_header(Mod, MsgName, _) ->
Mod:msg_header(MsgName).
%% rec2msg/2
-rec2msg(_, ['answer-message' = M | _]) ->
- {?BASE, M};
-
-rec2msg(Mod, [MsgName|_])
- when is_atom(MsgName) ->
- {Mod, MsgName};
+rec2msg(_, [Name|_])
+ when is_atom(Name) ->
+ Name;
rec2msg(Mod, Rec) ->
- R = element(1, Rec),
- A = 'answer-message',
- case ?BASE:msg2rec(A) of
- R ->
- {?BASE, A};
- _ ->
- {Mod, Mod:rec2msg(R)}
- end.
+ Mod:rec2msg(element(1, Rec)).
%%% ---------------------------------------------------------------------------
%%% # decode/2
@@ -241,20 +233,19 @@ decode(?APP_ID_RELAY, _, #diameter_packet{} = Pkt) ->
end;
%% Otherwise decode using the dictionary.
-decode(_, Mod, #diameter_packet{header = Hdr} = Pkt)
- when is_atom(Mod) ->
+decode(_, Mod, #diameter_packet{header = Hdr} = Pkt) ->
#diameter_header{cmd_code = CmdCode,
is_request = IsRequest,
is_error = IsError}
= Hdr,
- {M, MsgName} = if IsError andalso not IsRequest ->
- {?BASE, 'answer-message'};
- true ->
- {Mod, Mod:msg_name(CmdCode, IsRequest)}
- end,
+ MsgName = if IsError andalso not IsRequest ->
+ 'answer-message';
+ true ->
+ Mod:msg_name(CmdCode, IsRequest)
+ end,
- decode_avps(MsgName, M, Pkt, collect_avps(Pkt));
+ decode_avps(MsgName, Mod, Pkt, collect_avps(Pkt));
decode(Id, Mod, Bin)
when is_bitstring(Bin) ->
@@ -358,15 +349,15 @@ hop_by_hop_id(Id, <<H:12/binary, _:32, T/binary>>) ->
<<H/binary, Id:32, T/binary>>.
%%% ---------------------------------------------------------------------------
-%%% # msg_name/1
+%%% # msg_name/2
%%% ---------------------------------------------------------------------------
-msg_name(#diameter_header{application_id = ?APP_ID_COMMON,
- cmd_code = C,
- is_request = R}) ->
- ?BASE:msg_name(C,R);
+msg_name(Dict0, #diameter_header{application_id = ?APP_ID_COMMON,
+ cmd_code = C,
+ is_request = R}) ->
+ Dict0:msg_name(C,R);
-msg_name(Hdr) ->
+msg_name(_, Hdr) ->
msg_id(Hdr).
%% Note that messages in different applications could have the same
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 63d28f25a2..9f73815756 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-2012. 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
@@ -113,15 +113,22 @@
-define(VALUES(Rec), tl(tuple_to_list(Rec))).
+%% The RFC 3588 common dictionary is used to validate capabilities
+%% configuration. That a given transport may use the RFC 6733
+%% dictionary is of no consequence.
+-define(BASE, diameter_gen_base_rfc3588).
+
%%% The return values below assume the server diameter_config is started.
%%% The functions will exit if it isn't.
%% --------------------------------------------------------------------------
-%% # start_service(SvcName, Opts)
-%%
-%% Output: ok | {error, Reason}
+%% # start_service/2
%% --------------------------------------------------------------------------
+-spec start_service(diameter:service_name(), [diameter:service_opt()])
+ -> ok
+ | {error, term()}.
+
start_service(SvcName, Opts)
when is_list(Opts) ->
start_rc(sync(SvcName, {start_service, SvcName, Opts})).
@@ -134,22 +141,23 @@ start_rc(timeout) ->
{error, application_not_started}.
%% --------------------------------------------------------------------------
-%% # stop_service(SvcName)
-%%
-%% Output: ok
+%% # stop_service/1
%% --------------------------------------------------------------------------
+-spec stop_service(diameter:service_name())
+ -> ok.
+
stop_service(SvcName) ->
sync(SvcName, {stop_service, SvcName}).
%% --------------------------------------------------------------------------
-%% # add_transport(SvcName, {Type, Opts})
-%%
-%% Input: Type = connect | listen
-%%
-%% Output: {ok, Ref} | {error, Reason}
+%% # add_transport/2
%% --------------------------------------------------------------------------
+-spec add_transport(diameter:service_name(), {connect|listen, [diameter:transport_opt()]})
+ -> {ok, diameter:transport_ref()}
+ | {error, term()}.
+
add_transport(SvcName, {T, Opts})
when is_list(Opts), (T == connect orelse T == listen) ->
sync(SvcName, {add, SvcName, T, Opts}).
@@ -171,6 +179,10 @@ add_transport(SvcName, {T, Opts})
%% Output: ok | {error, Reason}
%% --------------------------------------------------------------------------
+-spec remove_transport(diameter:service_name(), diameter:transport_pred())
+ -> ok
+ | {error, term()}.
+
remove_transport(SvcName, Pred) ->
try
sync(SvcName, {remove, SvcName, pred(Pred)})
@@ -473,6 +485,10 @@ stop(SvcName) ->
%% add/3
+%% Can't check for a single common dictionary since a transport may
+%% restrict applications so that that there's one while the service
+%% has many.
+
add(SvcName, Type, Opts) ->
%% Ensure usable capabilities. diameter_service:merge_service/2
%% depends on this.
@@ -545,7 +561,7 @@ make_config(SvcName, Opts) ->
[] == Apps andalso ?THROW(no_apps),
%% Use the fact that diameter_caps has the same field names as CER.
- Fields = diameter_gen_base_rfc3588:'#info-'(diameter_base_CER) -- ['AVP'],
+ Fields = ?BASE:'#info-'(diameter_base_CER) -- ['AVP'],
COpts = [T || {K,_} = T <- Opts, lists:member(K, Fields)],
Caps = make_caps(#diameter_caps{}, COpts),
@@ -608,14 +624,14 @@ opt(sequence = K, F) ->
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}).
@@ -629,7 +645,8 @@ make_caps(Caps, Opts) ->
%% Validate types by encoding a CER.
encode_CER(Opts) ->
- {ok, CER} = diameter_capx:build_CER(make_caps(?EXAMPLE_CAPS, Opts)),
+ {ok, CER} = diameter_capx:build_CER(make_caps(?EXAMPLE_CAPS, Opts),
+ ?BASE),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
end_to_end_id = 0,
@@ -653,15 +670,17 @@ app_acc({application, Opts}, Acc) ->
[Dict, Mod] = get_opt([dictionary, module], Opts),
Alias = get_opt(alias, Opts, Dict),
ModS = get_opt(state, Opts, Alias),
- M = get_opt(call_mutates_state, Opts, false),
- A = get_opt(answer_errors, Opts, report),
+ M = get_opt(call_mutates_state, Opts, false, [true]),
+ A = get_opt(answer_errors, Opts, report, [callback, discard]),
+ P = get_opt(request_errors, Opts, answer_3xxx, [answer, callback]),
[#diameter_app{alias = Alias,
dictionary = Dict,
id = cb(Dict, id),
module = init_mod(Mod),
init_state = ModS,
- mutable = init_mutable(M),
- options = [{answer_errors, init_answers(A)}]}
+ mutable = M,
+ options = [{answer_errors, A},
+ {request_errors, P}]}
| Acc];
app_acc(_, Acc) ->
Acc.
@@ -690,20 +709,16 @@ init_cb(List) ->
V <- [proplists:get_value(F, List, D)]],
#diameter_callback{} = list_to_tuple([diameter_callback | Values]).
-init_mutable(M)
- when M == true;
- M == false ->
- M;
-init_mutable(M) ->
- ?THROW({call_mutates_state, M}).
-
-init_answers(A)
- when callback == A;
- report == A;
- discard == A ->
- A;
-init_answers(A) ->
- ?THROW({answer_errors, A}).
+%% Retreive and validate.
+get_opt(Key, List, Def, Other) ->
+ init_opt(Key, get_opt(Key, List, Def), [Def|Other]).
+
+init_opt(_, V, [V|_]) ->
+ V;
+init_opt(Name, V, [_|Vals]) ->
+ init_opt(Name, V, Vals);
+init_opt(Name, V, []) ->
+ ?THROW({Name, V}).
%% Get a single value at the specified key.
get_opt(Keys, List)
diff --git a/lib/diameter/src/base/diameter_internal.hrl b/lib/diameter/src/base/diameter_internal.hrl
index 63b35550a8..4b672aa071 100644
--- a/lib/diameter/src/base/diameter_internal.hrl
+++ b/lib/diameter/src/base/diameter_internal.hrl
@@ -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
@@ -58,8 +58,6 @@
-define(APP_ID_COMMON, 0).
-define(APP_ID_RELAY, 16#FFFFFFFF).
--define(BASE, diameter_gen_base_rfc3588).
-
%%% ---------------------------------------------------------
%%% RFC 3588, ch 2.6 Peer table
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 1b2f32ddff..130bedda84 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-2012. 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
@@ -33,9 +33,6 @@
abort/1,
notify/2]).
-%% Old interface only called from old code.
--export([start/3]). %% < diameter-1.2 (R15B02)
-
%% Server start.
-export([start_link/0]).
@@ -73,14 +70,6 @@ notify(SvcName, T) ->
rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}).
%%% ---------------------------------------------------------------------------
-%%% # start/3
-%%% ---------------------------------------------------------------------------
-
-%% From old code: make it restart.
-start(_T, _Opts, #diameter_service{}) ->
- {error, restart}.
-
-%%% ---------------------------------------------------------------------------
%%% # start/1
%%% ---------------------------------------------------------------------------
@@ -134,7 +123,7 @@ pair([_ | Rest], Mods, Acc) ->
pair(Rest, Mods, Acc);
%% No transport_module or transport_config: defaults.
-pair([], [], []) ->
+pair([], [], []) ->
[{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}];
%% One transport_module, one transport_config.
@@ -283,7 +272,7 @@ handle_cast(Msg, State) ->
%% Remote service is distributing a message.
handle_info({notify, SvcName, T}, S) ->
- bang(diameter_service:whois(SvcName), T),
+ diameter_service:notify(SvcName, T),
{noreply, S};
handle_info(Info, State) ->
@@ -315,13 +304,6 @@ code_change(_OldVsn, State, _Extra) ->
ifc_send(Pid, T) ->
Pid ! {diameter, T}.
-%% bang/2
-
-bang(undefined = No, _) ->
- No;
-bang(Pid, T) ->
- Pid ! T.
-
%% 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 c4320fcb99..66342f7b62 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-2012. 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,10 +18,10 @@
%%
%%
-%% This module implements (as a process) the RFC 3588 Peer State
+%% This module implements (as a process) the RFC 3588/6733 Peer State
%% Machine modulo the necessity of adapting the peer election to the
-%% fact that we don't know the identity of a peer until we've
-%% received a CER/CEA from it.
+%% fact that we don't know the identity of a peer until we've received
+%% a CER/CEA from it.
%%
-module(diameter_peer_fsm).
@@ -46,16 +46,19 @@
-include_lib("diameter/include/diameter.hrl").
-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(GOAWAY, 2). %% DO_NOT_WANT_TO_TALK_TO_YOU
+-define(BUSY, 1). %% BUSY
+-define(REBOOT, 0). %% REBOOTING
+%% Values of Inband-Security-Id.
-define(NO_INBAND_SECURITY, 0).
-define(TLS, 1).
+%% Note that the a common dictionary hrl is purposely not included
+%% since the common dictionary is an argument to start/3.
+
%% Keys in process dictionary.
-define(CB_KEY, cb). %% capabilities callback
-define(DPR_KEY, dpr). %% disconnect callback
@@ -95,18 +98,18 @@
-record(state,
{state %% of RFC 3588 Peer State Machine
- :: 'Wait-Conn-Ack' %% old code
- | {'Wait-Conn-Ack', uint32()}
+ :: {'Wait-Conn-Ack', uint32()}
| recv_CER
- | 'Wait-CEA' %% old code
| {'Wait-CEA', uint32(), uint32()}
| 'Open',
mode :: accept | connect | {connect, reference()},
- parent :: pid(), %% watchdog process
- transport :: pid(), %% transport process
+ parent :: pid(), %% watchdog process
+ transport :: pid(), %% transport process
+ dictionary :: module(), %% common dictionary
service :: #diameter_service{},
- dpr = false :: false | {uint32(), uint32()}}).
+ dpr = false :: false | {uint32(), uint32()},
%% | hop by hop and end to end identifiers
+ length_errors :: exit | handle | discard}).
%% There are non-3588 states possible as a consequence of 5.6.1 of the
%% standard and the corresponding problem for incoming CEA's: we don't
@@ -128,25 +131,18 @@
%% State Machine rather than closer to the transport. This is what we
%% now do below: connect/accept call diameter_watchdog and return the
%% pid of the watchdog process, and the watchdog in turn calls start/3
-%% below to start the process implementing the Peer State Machine. The
-%% former is a "peer" in diameter_service while the latter is a
-%% "conn". In a sense, diameter_service sees the watchdog as
-%% implementing the Peer State Machine and the process implemented
-%% here as being the transport, not being aware of the watchdog at
-%% all.
+%% below to start the process implementing the Peer State Machine.
%%
-%%% ---------------------------------------------------------------------------
-%%% # start({connect|accept, Ref}, Opts, Service)
-%%%
-%%% Output: Pid
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # start/3
+%% ---------------------------------------------------------------------------
--spec start(T, [Opt], #diameter_service{} %% from old code
- | {diameter:sequence(),
- diameter:restriction(),
+-spec start(T, [Opt], {diameter:sequence(),
+ [node()],
+ module(),
#diameter_service{}})
- -> pid()
+ -> {reference(), pid()}
when T :: {connect|accept, diameter:transport_ref()},
Opt :: diameter:transport_opt().
@@ -155,9 +151,15 @@
%% specified on the transport in question. Check here that the list is
%% still non-empty.
-start({_,_} = Type, Opts, MS) ->
- {ok, Pid} = diameter_peer_fsm_sup:start_child({self(), Type, Opts, MS}),
- Pid.
+start({_,_} = Type, Opts, S) ->
+ Ack = make_ref(),
+ T = {Ack, self(), Type, Opts, S},
+ {ok, Pid} = diameter_peer_fsm_sup:start_child(T),
+ try
+ {erlang:monitor(process, Pid), Pid}
+ after
+ Pid ! Ack
+ end.
start_link(T) ->
{ok, _} = proc_lib:start_link(?MODULE,
@@ -166,8 +168,8 @@ start_link(T) ->
infinity,
diameter_lib:spawn_opts(server, [])).
-%%% ---------------------------------------------------------------------------
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
%% init/1
@@ -175,15 +177,14 @@ init(T) ->
proc_lib:init_ack({ok, self()}),
gen_server:enter_loop(?MODULE, [], i(T)).
-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}),
+i({Ack, WPid, {M, Ref} = T, Opts, {Mask,
+ Nodes,
+ Dict0,
+ #diameter_service{capabilities = LCaps}
+ = Svc}}) ->
+ erlang:monitor(process, WPid),
+ wait(Ack, WPid),
putr(?DWA_KEY, dwa(LCaps)),
- {M, Ref} = T,
diameter_stats:reg(Ref),
{[Cs,Ds], Rest} = proplists:split(Opts, [capabilities_cb, disconnect_cb]),
putr(?CB_KEY, {Ref, [F || {_,F} <- Cs]}),
@@ -191,23 +192,39 @@ i({WPid, T, Opts, {Mask, Nodes, #diameter_service{applications = Apps,
putr(?REF_KEY, Ref),
putr(?SEQUENCE_KEY, Mask),
putr(?RESTRICT_KEY, Nodes),
- erlang:monitor(process, 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}}),
+ OnLengthErr = proplists:get_value(length_errors, Opts, exit),
+ lists:member(OnLengthErr, [exit, handle, discard])
+ orelse ?ERROR({invalid, {length_errors, OnLengthErr}}),
+
+ {TPid, Addrs} = start_transport(T, Rest, Svc),
+
#state{state = {'Wait-Conn-Ack', Tmo},
parent = WPid,
transport = TPid,
+ dictionary = Dict0,
mode = M,
- service = svc(Svc, Addrs)}.
+ service = svc(Svc, Addrs),
+ length_errors = OnLengthErr}.
%% 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
%% sending capabilities exchange messages.
%%
%% Invalid transport config may cause us to crash but note that the
-%% watchdog start (start/2) succeeds regardless so as not to crash the
-%% service.
+%% watchdog start (start/2) succeeds regardless.
+
+%% Wait for the caller to have a monitor to avoid a race with our
+%% death. (Since the exit reason is used in diameter_service.)
+wait(Ref, Pid) ->
+ receive
+ Ref ->
+ ok;
+ {'DOWN', _, process, Pid, _} = D ->
+ exit({shutdown, D})
+ end.
start_transport(T, Opts, #diameter_service{capabilities = LCaps} = Svc) ->
Addrs0 = LCaps#diameter_caps.host_ip_address,
@@ -280,13 +297,12 @@ handle_info(T, #state{} = State) ->
{noreply, S};
{stop, Reason} ->
?LOG(stop, Reason),
- x(Reason, State);
+ {stop, {shutdown, Reason}, State};
stop ->
?LOG(stop, T),
- x(T, State)
+ {stop, {shutdown, 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.
@@ -306,10 +322,6 @@ handle_info(T, #state{} = State) ->
%% 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}.
-
%% terminate/2
terminate(_, _) ->
@@ -320,8 +332,8 @@ terminate(_, _) ->
code_change(_, State, _) ->
{ok, State}.
-%%% ---------------------------------------------------------------------------
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
putr(Key, Val) ->
put({?MODULE, Key}, Val).
@@ -334,10 +346,6 @@ 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{transport = TPid,
@@ -388,8 +396,8 @@ transition({diameter, {recv, Pkt}}, S) ->
recv(Pkt, S);
%% Timeout when still in the same state ...
-transition({timeout, PS}, #state{state = PS}) ->
- {stop, {capx(PS), timeout}};
+transition({timeout = T, PS}, #state{state = PS}) ->
+ {stop, {capx(PS), T}};
%% ... or not.
transition({timeout, _}, _) ->
@@ -400,14 +408,8 @@ transition({send, Msg}, #state{transport = TPid}) ->
send(TPid, Msg),
ok;
-%% 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}) ->
@@ -467,18 +469,19 @@ start_next(#state{service = Svc0} = S) ->
send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
mode = {connect, Remote},
service = #diameter_service{capabilities = LCaps},
- transport = TPid}
+ transport = TPid,
+ dictionary = Dict}
= S) ->
OH = LCaps#diameter_caps.origin_host,
req_send_CER(OH, Remote)
orelse
- close({already_connected, Remote, LCaps}, S),
+ close({already_connected, Remote, LCaps}),
CER = build_CER(S),
?LOG(send, 'CER'),
#diameter_packet{header = #diameter_header{end_to_end_id = Eid,
hop_by_hop_id = Hid}}
= Pkt
- = encode(CER),
+ = encode(CER, Dict),
send(TPid, Pkt),
start_timer(Tmo, S#state{state = {'Wait-CEA', Hid, Eid}}).
@@ -500,51 +503,29 @@ start_timer(Tmo, #state{state = PS} = S) ->
%% build_CER/1
-build_CER(#state{service = #diameter_service{capabilities = LCaps}}) ->
- {ok, CER} = diameter_capx:build_CER(LCaps),
+build_CER(#state{service = #diameter_service{capabilities = LCaps},
+ dictionary = Dict}) ->
+ {ok, CER} = diameter_capx:build_CER(LCaps, Dict),
CER.
-%% encode/1
+%% encode/2
-encode(Rec) ->
- Seq = diameter_session:sequence(sequence()),
+encode(Rec, Dict) ->
+ Seq = diameter_session:sequence({_,_} = getr(?SEQUENCE_KEY)),
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.
+ diameter_codec:encode(Dict, #diameter_packet{header = Hdr,
+ msg = Rec}).
%% recv/2
-%% RFC 3588 has result code 5015 for an invalid length but if a
-%% transport is detecting message boundaries using the length header
-%% then a length error will likely lead to further errors.
-
-recv(#diameter_packet{header = #diameter_header{length = Len}
- = Hdr,
- bin = Bin},
- S)
- when Len < 20;
- (0 /= Len rem 4 orelse bit_size(Bin) /= 8*Len) ->
- discard(invalid_message_length, recv, [size(Bin),
- bit_size(Bin) rem 8,
- Hdr,
- S]);
-
recv(#diameter_packet{header = #diameter_header{} = Hdr}
= Pkt,
- #state{parent = Pid}
+ #state{parent = Pid,
+ dictionary = Dict0}
= S) ->
- Name = diameter_codec:msg_name(Hdr),
+ Name = diameter_codec:msg_name(Dict0, Hdr),
Pid ! {recv, self(), Name, Pkt},
diameter_stats:incr({msg_id(Name, Hdr), recv}), %% count received
rcv(Name, Pkt, S);
@@ -553,29 +534,52 @@ recv(#diameter_packet{header = undefined,
bin = Bin}
= Pkt,
S) ->
- recv(Pkt#diameter_packet{header = diameter_codec:decode_header(Bin)}, S);
+ recv(diameter_codec:decode_header(Bin), Pkt, S);
-recv(Bin, S)
- when is_binary(Bin) ->
- recv(#diameter_packet{bin = Bin}, S);
+recv(Bin, S) ->
+ recv(#diameter_packet{bin = Bin}, S).
-recv(#diameter_packet{header = false} = Pkt, S) ->
- discard(truncated_header, recv, [Pkt, S]).
+%% recv/3
-msg_id({_,_,_} = T, _) ->
- T;
-msg_id(_, Hdr) ->
- diameter_codec:msg_id(Hdr).
+recv(#diameter_header{length = Len}
+ = H,
+ #diameter_packet{bin = Bin}
+ = Pkt,
+ #state{length_errors = E}
+ = S)
+ when E == handle;
+ 0 == Len rem 4, bit_size(Bin) == 8*Len ->
+ recv(Pkt#diameter_packet{header = H}, S);
+
+recv(#diameter_header{}
+ = H,
+ #diameter_packet{bin = Bin},
+ #state{length_errors = E}
+ = S) ->
+ invalid(E,
+ invalid_message_length,
+ recv,
+ [size(Bin), bit_size(Bin) rem 8, H, S]);
-%% Treat invalid length as a transport error and die. Especially in
-%% the TCP case, in which there's no telling where the next message
-%% begins in the incoming byte stream, keeping a crippled connection
-%% alive may just make things worse.
+recv(false, Pkt, #state{length_errors = E} = S) ->
+ invalid(E, truncated_header, recv, [Pkt, S]).
-discard(Reason, F, A) ->
+%% Note that counters here only count discarded messages.
+invalid(E, Reason, F, A) ->
diameter_stats:incr(Reason),
+ abort(E, Reason, F, A).
+
+abort(exit, Reason, F, A) ->
diameter_lib:warning_report(Reason, {?MODULE, F, A}),
- throw({?MODULE, abort, Reason}).
+ throw({?MODULE, abort, Reason});
+
+abort(_, _, _, _) ->
+ ok.
+
+msg_id({_,_,_} = T, _) ->
+ T;
+msg_id(_, Hdr) ->
+ {_,_,_} = diameter_codec:msg_id(Hdr).
%% rcv/3
@@ -584,10 +588,8 @@ 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}}
+ #state{state = {'Wait-CEA', 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
@@ -631,13 +633,13 @@ send(Pid, Msg) ->
%% handle_request/3
-handle_request(Type, #diameter_packet{} = Pkt, S) ->
+handle_request(Type, #diameter_packet{} = Pkt, #state{dictionary = D} = S) ->
?LOG(recv, Type),
- send_answer(Type, diameter_codec:decode(?BASE, Pkt), S).
+ send_answer(Type, diameter_codec:decode(D, Pkt), S).
%% send_answer/3
-send_answer(Type, ReqPkt, #state{transport = TPid} = S) ->
+send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
#diameter_packet{header = H,
transport_data = TD}
= ReqPkt,
@@ -654,13 +656,15 @@ send_answer(Type, ReqPkt, #state{transport = TPid} = S) ->
msg = Msg,
transport_data = TD},
- send(TPid, diameter_codec:encode(?BASE, Pkt)),
+ send(TPid, diameter_codec:encode(Dict, Pkt)),
eval(PostF, S).
eval([F|A], S) ->
apply(F, A ++ [S]);
eval(ok, S) ->
- S.
+ S;
+eval(T, _) ->
+ close(T).
%% build_answer/3
@@ -671,11 +675,11 @@ build_answer('CER',
is_error = false},
errors = []}
= Pkt,
- S) ->
- {SupportedApps, RCaps, #diameter_base_CEA{'Result-Code' = RC,
- 'Inband-Security-Id' = IS}
- = CEA}
- = recv_CER(CER, S),
+ #state{dictionary = Dict0}
+ = S) ->
+ {SupportedApps, RCaps, CEA} = recv_CER(CER, S),
+
+ [RC, IS] = Dict0:'#get-'(['Result-Code', 'Inband-Security-Id'], CEA),
#diameter_caps{origin_host = {OH, DH}}
= Caps
@@ -688,10 +692,10 @@ build_answer('CER',
orelse ?THROW(4003), %% DIAMETER_ELECTION_LOST
caps_cb(Caps)
of
- N -> {cea(CEA, N), [fun open/5, Pkt,
- SupportedApps,
- Caps,
- {accept, hd([_] = IS)}]}
+ N -> {cea(CEA, N, Dict0), [fun open/5, Pkt,
+ SupportedApps,
+ Caps,
+ {accept, hd([_] = IS)}]}
catch
?FAILURE(Reason) ->
rejected(Reason, {'CER', Reason, Caps, Pkt}, S)
@@ -708,25 +712,25 @@ build_answer(Type,
RC = rc(H, Es),
{answer(Type, RC, Es, S), post(Type, RC, Pkt, S)}.
-cea(CEA, ok) ->
+cea(CEA, ok, _) ->
CEA;
-cea(CEA, 2001) ->
+cea(CEA, 2001, _) ->
CEA;
-cea(CEA, RC) ->
- CEA#diameter_base_CEA{'Result-Code' = RC}.
+cea(CEA, RC, Dict0) ->
+ Dict0:'#set-'({'Result-Code', RC}, CEA).
post('CER' = T, RC, Pkt, S) ->
- [fun close/2, {T, caps(S), {RC, Pkt}}];
+ {T, caps(S), {RC, Pkt}};
post(_, _, _, _) ->
ok.
rejected({capabilities_cb, _F, Reason}, T, S) ->
rejected(Reason, T, S);
-rejected(discard, T, S) ->
- close(T, S);
+rejected(discard, T, _) ->
+ close(T);
rejected({N, Es}, T, S) ->
- {answer('CER', N, Es, S), [fun close/2, T]};
+ {answer('CER', N, Es, S), T};
rejected(N, T, S) ->
rejected({N, []}, T, S).
@@ -752,7 +756,7 @@ is_origin({N, _}) ->
orelse N == 'Origin-State-Id'.
%% failed_avp/1
-
+
failed_avp([] = No) ->
No;
failed_avp(Avps) ->
@@ -841,22 +845,23 @@ a('DPR', #diameter_caps{origin_host = {Host, _},
%% recv_CER/2
-recv_CER(CER, #state{service = Svc}) ->
- {ok, T} = diameter_capx:recv_CER(CER, Svc),
+recv_CER(CER, #state{service = Svc, dictionary = Dict}) ->
+ {ok, T} = diameter_capx:recv_CER(CER, Svc, Dict),
T.
%% handle_CEA/1
handle_CEA(#diameter_packet{bin = Bin}
= Pkt,
- #state{service = #diameter_service{capabilities = LCaps}}
+ #state{dictionary = Dict0,
+ service = #diameter_service{capabilities = LCaps}}
= S)
when is_binary(Bin) ->
?LOG(recv, 'CEA'),
#diameter_packet{msg = CEA}
= DPkt
- = diameter_codec:decode(?BASE, Pkt),
+ = diameter_codec:decode(Dict0, Pkt),
{SApps, IS, RCaps} = recv_CEA(DPkt, S),
@@ -864,8 +869,7 @@ handle_CEA(#diameter_packet{bin = Bin}
= Caps
= capz(LCaps, RCaps),
- #diameter_base_CEA{'Result-Code' = RC}
- = CEA,
+ RC = Dict0:'#get-'('Result-Code', CEA),
%% Ensure that we don't already have a connection to the peer in
%% question. This isn't the peer election of 3588 except in the
@@ -886,7 +890,7 @@ handle_CEA(#diameter_packet{bin = Bin}
of
_ -> open(DPkt, SApps, Caps, {connect, hd([_] = IS)}, S)
catch
- ?FAILURE(Reason) -> close({'CEA', Reason, Caps, DPkt}, S)
+ ?FAILURE(Reason) -> close({'CEA', Reason, Caps, DPkt})
end.
%% Check more than the result code since the peer could send success
%% regardless. If not 2001 then a peer_up callback could do anything
@@ -901,12 +905,13 @@ recv_CEA(#diameter_packet{header = #diameter_header{version
is_error = false},
msg = CEA,
errors = []},
- #state{service = Svc}) ->
- {ok, T} = diameter_capx:recv_CEA(CEA, Svc),
+ #state{service = Svc,
+ dictionary = Dict}) ->
+ {ok, T} = diameter_capx:recv_CEA(CEA, Svc, Dict),
T;
recv_CEA(Pkt, S) ->
- close({'CEA', caps(S), Pkt}, S).
+ close({'CEA', caps(S), Pkt}).
caps(#diameter_service{capabilities = Caps}) ->
Caps;
@@ -959,14 +964,14 @@ open(Pkt, SupportedApps, Caps, {Type, IS}, #state{parent = Pid,
%% We've advertised TLS support: tell the transport the result
%% and expect a reply when the handshake is complete.
-tls_ack(true, Caps, Type, IS, #state{transport = TPid} = S) ->
+tls_ack(true, Caps, Type, IS, #state{transport = TPid}) ->
Ref = make_ref(),
TPid ! {diameter, {tls, Ref, Type, IS == ?TLS}},
receive
{diameter, {tls, Ref}} ->
ok;
{'DOWN', _, process, TPid, Reason} ->
- close({tls_ack, Reason, Caps}, S)
+ close({tls_ack, Reason, Caps})
end;
%% Or not. Don't send anything to the transport so that transports
@@ -979,25 +984,11 @@ capz(#diameter_caps{} = L, #diameter_caps{} = R) ->
= list_to_tuple([diameter_caps | lists:zip(tl(tuple_to_list(L)),
tl(tuple_to_list(R)))]).
-%% close/2
+%% close/1
-%% Tell the watchdog that our death isn't due to transport failure.
-close(Reason, #state{parent = Pid}) ->
- close_wd(Reason, Pid),
+close(Reason) ->
throw({?MODULE, close, Reason}).
-%% close_wd/2
-
-%% Ensure the watchdog dies if DPR has been sent ...
-close_wd(_, #state{dpr = false}) ->
- ok;
-close_wd(Reason, #state{parent = Pid}) ->
- close_wd(Reason, Pid);
-
-%% ... or otherwise
-close_wd(Reason, Pid) ->
- Pid ! {close, self(), Reason}.
-
%% dwa/1
dwa(#diameter_caps{origin_host = OH,
@@ -1020,14 +1011,10 @@ 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;
+ CBs = getr(?DPR_KEY),
+ Ref = getr(?REF_KEY),
+ Peer = {self(), Caps},
+ dpr(CBs, [Reason, Ref, Peer], S);
%% Connection is open, DPR already sent.
dpr(_, #state{state = 'Open'}) ->
@@ -1063,13 +1050,14 @@ dpr([CB|Rest], [Reason | _] = Args, S) ->
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,
+ dictionary = Dict,
service = #diameter_service{capabilities = Caps}}
= S) ->
#opts{cause = Cause, timeout = Tmo}
@@ -1089,7 +1077,8 @@ send_dpr(Reason, Opts, #state{transport = TPid,
= Pkt
= encode(['DPR', {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Disconnect-Cause', Cause}]),
+ {'Disconnect-Cause', Cause}],
+ Dict),
send(TPid, Pkt),
dpa_timer(Tmo),
?LOG(send, 'DPR'),
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 619b12ecad..3197c1aee1 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-2012. 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
@@ -138,7 +138,7 @@ del(T) ->
%% associations removed.)
%% ===========================================================================
--spec match(tuple())
+-spec match(any())
-> [{term(), pid()}].
match(Pat) ->
@@ -208,10 +208,6 @@ init(_) ->
%% # handle_call/3
%% ----------------------------------------------------------
-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),
@@ -281,9 +277,6 @@ code_change(_OldVsn, State, _Extra) ->
%% ===========================================================================
-upgrade(S) ->
- #state{} = list_to_tuple(tuple_to_list(S) ++ [[]]).
-
monitor(true, Pid) ->
ets:insert(?TABLE, ?MONITOR(Pid, erlang:monitor(process, Pid)));
monitor(false, _) ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 66c526b379..c971646861 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -24,33 +24,38 @@
-module(diameter_service).
-behaviour(gen_server).
+%% towards diameter_service_sup
+-export([start_link/1]).
+
+%% towards diameter
+-export([subscribe/1,
+ unsubscribe/1,
+ services/0,
+ info/2]).
+
+%% towards diameter_config
-export([start/1,
stop/1,
start_transport/2,
- stop_transport/2,
- info/2,
- call/4]).
+ stop_transport/2]).
-%% towards diameter_watchdog
--export([receive_message/3]).
+%% towards diameter_peer
+-export([notify/2]).
-%% service supervisor
--export([start_link/1]).
+%% towards diameter_traffic
+-export([find_incoming_app/4,
+ pick_peer/3]).
--export([subscribe/1,
- unsubscribe/1,
+%% test/debug
+-export([services/1,
subscriptions/1,
subscriptions/0,
- services/0,
- services/1,
- whois/1]).
-
-%% test/debug
--export([call_module/3,
+ call_module/3,
+ whois/1,
state/1,
uptime/1]).
-%%% gen_server callbacks
+%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
@@ -58,21 +63,10 @@
terminate/2,
code_change/3]).
-%% Other callbacks.
--export([send/1]).
-
-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.
+%% RFC 3539 watchdog states.
-define(WD_INITIAL, initial).
-define(WD_OKAY, okay).
-define(WD_SUSPECT, suspect).
@@ -86,11 +80,8 @@
| ?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
@@ -98,10 +89,6 @@
%% a ?Dict don't change the handle to it.
-define(Dict, diameter_dict).
-%% Table containing outgoing requests for which a reply has yet to be
-%% received.
--define(REQUEST_TABLE, diameter_request).
-
%% Maintains state in a table. In contrast to previously, a service's
%% stat is not constant and is accessed outside of the service
%% process.
@@ -115,83 +102,58 @@
%% Workaround for dialyzer's lack of understanding of match specs.
-type match(T)
- :: T | '_' | '$1' | '$2' | '$3' | '$4'.
-
-%% State of service gen_server.
+ :: T | '_' | '$1' | '$2'.
+
+%% State of service gen_server. Note that the state term itself
+%% doesn't change, which is relevant for the stateless application
+%% callbacks since the state is retrieved from ?STATE_TABLE from
+%% outside the service process. The pid in the service record is used
+%% to determine whether or not we need to call the process for a
+%% pick_peer callback in the statefull case.
-record(state,
{id = now(),
- service_name, %% as passed to start_service/2, key in ?STATE_TABLE
+ service_name :: diameter:service_name(), %% 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/reopen
- shared_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
- local_peers = ?Dict:new(), %% Alias -> [{TPid, Caps}, ...]
+ watchdogT = ets_new(watchdogs) %% #watchdog{} at start
+ :: ets:tid(),
+ peerT = ets_new(peers) %% #peer{pid = TPid} at okay/reopen
+ :: ets:tid(),
+ shared_peers = ?Dict:new() %% Alias -> [{TPid, Caps}, ...]
+ :: ets:tid(),
+ local_peers = ?Dict:new() %% Alias -> [{TPid, Caps}, ...]
+ :: ets:tid(),
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
-%% from ?STATE_TABLE from outside the service process. The pid in the
-%% 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 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,
+%% shared_peers reflects the peers broadcast from remote nodes.
+
+%% Record representing an RFC 3539 watchdog process implemented by
+%% diameter_watchdog.
+-record(watchdog,
{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, ?WD_INITIAL}
- :: match(op_state() | {op_state(), wd_state()}),
+ state = ?WD_INITIAL :: match(wd_state()),
started = now(), %% at process start
- conn = false :: match(boolean() | pid())}).
- %% 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,
+ peer = false :: match(boolean() | pid())}).
+ %% true at accepted, pid() at okay/reopen
+
+%% Record representing an Peer State Machine processes implemented by
+%% diameter_peer_fsm.
+-record(peer,
{pid :: pid(),
apps :: [{0..16#FFFFFFFF, diameter:app_alias()}], %% {Id, Alias}
caps :: #diameter_caps{},
started = now(), %% at process start
- peer :: pid()}). %% key into peerT
-
-%% Record stored in diameter_request for each outgoing request.
--record(request,
- {from, %% arg 2 of handle_call/3
- 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
- filter :: match(diameter:peer_filter()),
- packet :: match(#diameter_packet{})}).
-
-%% Record call/4 options are parsed into.
--record(options,
- {filter = none :: diameter:peer_filter(),
- extra = [] :: list(),
- timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
- detach = false :: boolean()}).
-
-%%% ---------------------------------------------------------------------------
-%%% # start(SvcName)
-%%% ---------------------------------------------------------------------------
+ watchdog :: pid()}). %% key into watchdogT
+
+%% ---------------------------------------------------------------------------
+%% # start/1
+%% ---------------------------------------------------------------------------
start(SvcName) ->
diameter_service_sup:start_child(SvcName).
@@ -202,9 +164,9 @@ start_link(SvcName) ->
%% Put the arbitrary term SvcName in a list in case we ever want to
%% send more than this and need to distinguish old from new.
-%%% ---------------------------------------------------------------------------
-%%% # stop(SvcName)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # stop/1
+%% ---------------------------------------------------------------------------
stop(SvcName) ->
case whois(SvcName) of
@@ -220,183 +182,43 @@ stop(ok, Pid) ->
stop(No, _) ->
No.
-%%% ---------------------------------------------------------------------------
-%%% # start_transport(SvcName, {Ref, Type, Opts})
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # start_transport/3
+%% ---------------------------------------------------------------------------
-start_transport(SvcName, {_,_,_} = T) ->
+start_transport(SvcName, {_Ref, _Type, _Opts} = T) ->
call_service_by_name(SvcName, {start, T}).
-%%% ---------------------------------------------------------------------------
-%%% # stop_transport(SvcName, Refs)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # stop_transport/2
+%% ---------------------------------------------------------------------------
stop_transport(_, []) ->
ok;
stop_transport(SvcName, [_|_] = Refs) ->
call_service_by_name(SvcName, {stop, Refs}).
-%%% ---------------------------------------------------------------------------
-%%% # info(SvcName, Item)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # info/2
+%% ---------------------------------------------------------------------------
info(SvcName, Item) ->
- case find_state(SvcName) of
- #state{} = S ->
+ case lookup_state(SvcName) of
+ [#state{} = S] ->
service_info(Item, S);
- false ->
+ [] ->
undefined
end.
-%%% ---------------------------------------------------------------------------
-%%% # receive_message(TPid, Pkt, MessageData)
-%%% ---------------------------------------------------------------------------
-
-%% 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) ->
- #diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
- recv(R, (not R) andalso lookup_request(Pkt, TPid), TPid, Pkt, T).
-
-%% Incoming request ...
-recv(true, false, TPid, Pkt, T) ->
- try
- spawn(fun() -> recv_request(TPid, Pkt, T) end)
- catch
- error: system_limit = E -> %% discard
- ?LOG({error, E}, now())
- end;
-
-%% ... answer to known request ...
-recv(false, #request{from = {_, Ref}, handler = Pid} = Req, _, Pkt, _) ->
- Pid ! {answer, Ref, Req, Pkt};
-%% Note that failover could have happened prior to this message being
-%% received and triggering failback. That is, both a failover message
-%% and answer may be on their way to the handler process. In the worst
-%% case the request process gets notification of the failover and
-%% sends to the alternate peer before an answer arrives, so it's
-%% always the case that we can receive more than one answer after
-%% failover. The first answer received by the request process wins,
-%% any others are discarded.
-
-%% ... or not.
-recv(false, false, _, _, _) ->
- ok.
-
-%%% ---------------------------------------------------------------------------
-%%% # call(SvcName, App, Msg, Options)
-%%% ---------------------------------------------------------------------------
-
-call(SvcName, App, Msg, Options)
- when is_list(Options) ->
- Rec = make_options(Options),
- Ref = make_ref(),
- Caller = {self(), Ref},
- Fun = fun() -> exit({Ref, call(SvcName, App, Msg, Rec, Caller)}) end,
- try spawn_monitor(Fun) of
- {_, MRef} ->
- recv(MRef, Ref, Rec#options.detach, false)
- catch
- error: system_limit = E ->
- {error, E}
- end.
-
-%% Don't rely on gen_server:call/3 for the timeout handling since it
-%% makes no guarantees about not leaving a reply message in the
-%% mailbox if we catch its exit at timeout. It currently *can* do so,
-%% which is also undocumented.
-
-recv(MRef, _, true, true) ->
- erlang:demonitor(MRef, [flush]),
- ok;
-
-recv(MRef, Ref, Detach, Sent) ->
- receive
- Ref -> %% send has been attempted
- recv(MRef, Ref, Detach, true);
- {'DOWN', MRef, process, _, Reason} ->
- call_rc(Reason, Ref, Sent)
- end.
-
-%% call/5 has returned ...
-call_rc({Ref, Ans}, Ref, _) ->
- Ans;
-
-%% ... or not. In this case failure/encode are documented.
-call_rc(_, _, Sent) ->
- {error, choose(Sent, failure, encode)}.
-
-%% call/5
-%%
-%% In the process spawned for the outgoing request.
-
-call(SvcName, App, Msg, Opts, Caller) ->
- c(find_state(SvcName), 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, Mask, Msg, Opts, Caller, Svc);
- false ->
- {error, no_connection};
- {error, _} = No ->
- No
- end;
-
-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) ->
- lists:foldl(fun mo/2, #options{}, Options).
-
-mo({timeout, T}, Rec)
- when is_integer(T), 0 =< T ->
- Rec#options{timeout = T};
+%% lookup_state/1
-mo({filter, F}, #options{filter = none} = Rec) ->
- Rec#options{filter = F};
-mo({filter, F}, #options{filter = {all, Fs}} = Rec) ->
- Rec#options{filter = {all, [F | Fs]}};
-mo({filter, F}, #options{filter = F0} = Rec) ->
- Rec#options{filter = {all, [F0, F]}};
+lookup_state(SvcName) ->
+ ets:lookup(?STATE_TABLE, SvcName).
-mo({extra, L}, #options{extra = X} = Rec)
- when is_list(L) ->
- Rec#options{extra = X ++ L};
-
-mo(detach, Rec) ->
- Rec#options{detach = true};
-
-mo(T, _) ->
- ?ERROR({invalid_option, T}).
-
-%%% ---------------------------------------------------------------------------
-%%% # subscribe(SvcName)
-%%% # unsubscribe(SvcName)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # subscribe/1
+%% # unsubscribe/1
+%% ---------------------------------------------------------------------------
subscribe(SvcName) ->
diameter_reg:add({?MODULE, subscriber, SvcName}).
@@ -413,9 +235,9 @@ subscriptions() ->
pmap(Props) ->
lists:map(fun({{?MODULE, _, Name}, Pid}) -> {Name, Pid} end, Props).
-%%% ---------------------------------------------------------------------------
-%%% # services(Pattern)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # services/1
+%% ---------------------------------------------------------------------------
services(Pat) ->
pmap(diameter_reg:match({?MODULE, service, Pat})).
@@ -431,6 +253,86 @@ whois(SvcName) ->
undefined
end.
+%% ---------------------------------------------------------------------------
+%% # pick_peer/3
+%% ---------------------------------------------------------------------------
+
+-spec pick_peer(SvcName, AppOrAlias, Opts)
+ -> {{TPid, Caps, App}, Mask}
+ | false
+ | {error, term()}
+ when SvcName :: diameter:service_name(),
+ AppOrAlias :: {alias, diameter:app_alias()} | #diameter_app{},
+ Opts :: tuple(),
+ TPid :: pid(),
+ Caps :: #diameter_caps{},
+ App :: #diameter_app{},
+ Mask :: diameter:sequence().
+
+pick_peer(SvcName, App, Opts) ->
+ pick(lookup_state(SvcName), App, Opts).
+
+pick([], _, _) ->
+ {error, no_service};
+
+pick([S], App, Opts) ->
+ pick(S, App, Opts);
+
+pick(#state{service = #diameter_service{applications = Apps}}
+ = S,
+ {alias, Alias},
+ Opts) -> %% initial call from diameter:call/4
+ pick(S, find_outgoing_app(Alias, Apps), Opts);
+
+pick(_, false, _) ->
+ false;
+
+pick(#state{options = [{_, Mask} | _]}
+ = S,
+ #diameter_app{module = ModX, dictionary = Dict}
+ = App0,
+ {DestF, Filter, Xtra}) ->
+ App = App0#diameter_app{module = ModX ++ Xtra},
+ [_,_] = RealmAndHost = diameter_lib:eval([DestF, Dict]),
+ case pick_peer(App, RealmAndHost, Filter, S) of
+ {TPid, Caps} ->
+ {{TPid, Caps, App}, Mask};
+ false = No ->
+ No
+ end.
+
+%% ---------------------------------------------------------------------------
+%% # find_incoming_app/4
+%% ---------------------------------------------------------------------------
+
+-spec find_incoming_app(PeerT, TPid, Id, Apps)
+ -> {#diameter_app{}, #diameter_caps{}} %% connection and suitable app
+ | #diameter_caps{} %% connection but no suitable app
+ | false %% no connection
+ when PeerT :: ets:tid(),
+ TPid :: pid(),
+ Id :: non_neg_integer(),
+ Apps :: [#diameter_app{}].
+
+find_incoming_app(PeerT, TPid, Id, Apps) ->
+ try ets:lookup(PeerT, TPid) of
+ [#peer{} = P] ->
+ find_incoming_app(P, Id, Apps);
+ [] -> %% transport has gone down
+ false
+ catch
+ error: badarg -> %% service has gone down (and taken table with it)
+ false
+ end.
+
+%% ---------------------------------------------------------------------------
+%% # notify/2
+%% ---------------------------------------------------------------------------
+
+notify(SvcName, Msg) ->
+ Pid = whois(SvcName),
+ is_pid(Pid) andalso (Pid ! Msg).
+
%% ===========================================================================
%% ===========================================================================
@@ -445,9 +347,9 @@ uptime(Svc) ->
call_module(Service, AppMod, Request) ->
call_service(Service, {call_module, AppMod, Request}).
-%%% ---------------------------------------------------------------------------
-%%% # init([SvcName])
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # init/1
+%% ---------------------------------------------------------------------------
init([SvcName]) ->
process_flag(trap_exit, true), %% ensure terminate(shutdown, _)
@@ -458,13 +360,9 @@ i(SvcName, true) ->
i(_, false) ->
{stop, {shutdown, already_started}}.
-%%% ---------------------------------------------------------------------------
-%%% # handle_call(Req, From, State)
-%%% ---------------------------------------------------------------------------
-
-handle_call(T, From, S)
- when not is_record(S, state) ->
- handle_call(T, From, upgrade(S));
+%% ---------------------------------------------------------------------------
+%% # handle_call/3
+%% ---------------------------------------------------------------------------
handle_call(state, _, S) ->
{reply, S, S};
@@ -489,10 +387,6 @@ 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(service, S),
{stop, normal, ok, S};
@@ -500,29 +394,21 @@ handle_call(stop, _From, S) ->
%% 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}.
-%%% ---------------------------------------------------------------------------
-%%% # handle_cast(Req, State)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # handle_cast/2
+%% ---------------------------------------------------------------------------
handle_cast(Req, S) ->
unexpected(handle_cast, [Req], S),
{noreply, S}.
-%%% ---------------------------------------------------------------------------
-%%% # handle_info(Req, State)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # handle_info/2
+%% ---------------------------------------------------------------------------
handle_info(T, #state{} = S) ->
case transition(T,S) of
@@ -530,10 +416,7 @@ handle_info(T, #state{} = S) ->
{noreply, S};
{stop, Reason} ->
{stop, {shutdown, Reason}, S}
- end;
-
-handle_info(T, S) ->
- handle_info(T, upgrade(S)).
+ end.
%% transition/2
@@ -542,67 +425,39 @@ transition({accepted, Pid, TPid}, S) ->
accepted(Pid, TPid, S),
ok;
-%% Peer process has a new open connection.
-transition({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;
-
-%% Watchdog has left state OKAY.
-transition({connection_down, Pid}, S) ->
- connection_down(Pid, S),
- ok;
-
-%% Watchdog has returned to state OKAY.
-transition({connection_up, Pid}, S) ->
- connection_up(Pid, S),
- ok;
-
-%% Accepting transport has lost connectivity.
-transition({close, Pid}, S) ->
- close(Pid, S),
- ok;
-
%% Connecting transport is being restarted by watchdog.
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}}),
+%% Watchdog is sending notification of transport death.
+transition({close, Pid, Reason}, #state{service_name = SvcName,
+ watchdogT = WatchdogT}) ->
+ #watchdog{state = WS,
+ ref = Ref,
+ type = Type,
+ options = Opts}
+ = fetch(WatchdogT, Pid),
+ WS /= ?WD_OKAY
+ andalso
+ send_event(SvcName, {closed, Ref, Reason, {type(Type), Opts}}),
+ ok;
+
+%% Watchdog is sending notification of a state transition.
+transition({watchdog, Pid, {[TPid | Data], From, To}},
+ #state{service_name = SvcName,
+ watchdogT = WatchdogT}
+ = S) ->
+ #watchdog{ref = Ref, type = T, options = Opts}
+ = Wd
+ = fetch(WatchdogT, Pid),
+ watchdog(TPid, Data, From, To, Wd, S),
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.
+%% Death of a watchdog process (#watchdog.pid) results in the removal of
+%% it's peer and any associated conn record when 'DOWN' is received.
+%% Death of a peer process process (#peer.pid, #watchdog.peer) results in
+%% ?WD_DOWN.
%% Monitor process has died. Just die with a reason that tells
%% diameter_config about the happening. If a cleaner shutdown is
@@ -611,9 +466,9 @@ transition({'DOWN', MRef, process, _, Reason}, #state{monitor = MRef}) ->
{stop, {monitor, Reason}};
%% Local watchdog process has died.
-transition({'DOWN', _, process, Pid, Reason}, S)
+transition({'DOWN', _, process, Pid, _Reason}, S)
when node(Pid) == node() ->
- peer_down(Pid, Reason, S),
+ watchdog_down(Pid, S),
ok;
%% Remote service wants to know about shared peers.
@@ -636,49 +491,13 @@ transition({tc_timeout, T}, S) ->
tc_timeout(T, S),
ok;
-%% Request process is telling us it may have missed a failover message
-%% after a transport went down and the service process looked up
-%% outstanding requests.
-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/2
+%% ---------------------------------------------------------------------------
terminate(Reason, #state{service_name = Name} = S) ->
send_event(Name, stop),
@@ -686,9 +505,9 @@ terminate(Reason, #state{service_name = Name} = S) ->
shutdown == Reason %% application shutdown
andalso shutdown(application, S).
-%%% ---------------------------------------------------------------------------
-%%% # code_change(FromVsn, State, Extra)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # code_change/3
+%% ---------------------------------------------------------------------------
code_change(FromVsn,
#state{service_name = SvcName,
@@ -714,30 +533,20 @@ code_change(FromVsn, SvcName, Extra, #diameter_app{alias = Alias} = A) ->
unexpected(F, A, #state{service_name = Name}) ->
?UNEXPECTED(F, A ++ [Name]).
-cb([_|_] = M, F, A) ->
- eval(M, F, A);
-cb(Rec, F, A) ->
- {_, M} = app(Rec),
+cb(#diameter_app{module = [_|_] = M}, F, A) ->
eval(M, F, A).
-app(#request{app = A, module = M}) ->
- {A,M};
-app(#diameter_app{alias = A, module = M}) ->
- {A,M}.
-
eval([M|X], F, A) ->
apply(M, F, A ++ X).
%% Callback with state.
-state_cb(#diameter_app{mutable = false, init_state = S}, {ModX, F, A}) ->
+state_cb(#diameter_app{module = ModX, mutable = false, init_state = S},
+ pick_peer = F,
+ A) ->
eval(ModX, F, A ++ [S]);
-state_cb(#diameter_app{mutable = true, alias = Alias}, {_,_,_} = MFA) ->
- state_cb(MFA, Alias);
-
-state_cb({ModX,F,A}, Alias)
- when is_list(ModX) ->
+state_cb(#diameter_app{module = ModX, alias = Alias}, F, A) ->
eval(ModX, F, A ++ [mod_state(Alias)]).
choose(true, X, _) -> X;
@@ -763,57 +572,38 @@ mod_state(Alias) ->
mod_state(Alias, ModS) ->
put({?MODULE, mod_state, Alias}, ModS).
-%%% ---------------------------------------------------------------------------
-%%% # shutdown/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # shutdown/2
+%% ---------------------------------------------------------------------------
-%% remove_transport: ask watchdogs to terminate their transport.
-shutdown(Refs, #state{peerT = PeerT})
+%% remove_transport
+shutdown(Refs, #state{watchdogT = WatchdogT})
when is_list(Refs) ->
- ets:foldl(fun(P,ok) -> sp(P, Refs), ok end, ok, 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.
- shutdown(conn, Reason, PeerT),
- %% Kill the watchdogs explicitly in case there was no transport.
- shutdown(peer, Reason, PeerT).
+ ets:foldl(fun(P,ok) -> st(P, Refs), ok end, ok, WatchdogT);
-%% sp/2
-
-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,
+%% application/service shutdown
+shutdown(Reason, #state{watchdogT = WatchdogT})
+ when Reason == application;
+ Reason == service ->
+ diameter_lib:wait(ets:foldl(fun(P,A) -> st(P, Reason, A) end,
[],
- T)).
+ WatchdogT)).
-shutdown(conn = Who, #peer{op_state = {OS,_}} = P, Reason, Acc) ->
- shutdown(Who, P#peer{op_state = OS}, Reason, Acc);
+%% st/2
-shutdown(conn,
- #peer{pid = Pid, op_state = ?STATE_UP, conn = TPid},
- Reason,
- Acc) ->
- TPid ! {shutdown, Pid, Reason},
- [TPid | Acc];
+st(#watchdog{ref = Ref, pid = Pid}, Refs) ->
+ lists:member(Ref, Refs)
+ andalso (Pid ! {shutdown, self(), transport}). %% 'DOWN' cleans up
-shutdown(peer, #peer{pid = Pid}, _Reason, Acc)
- when is_pid(Pid) ->
- exit(Pid, shutdown),
- [Pid | Acc];
+%% st/3
-shutdown(_, #peer{}, _, Acc) ->
- Acc.
+st(#watchdog{pid = Pid}, Reason, Acc) ->
+ Pid ! {shutdown, self(), Reason},
+ [Pid | Acc].
-%%% ---------------------------------------------------------------------------
-%%% # call_service/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # call_service/2
+%% ---------------------------------------------------------------------------
call_service(Pid, Req)
when is_pid(Pid) ->
@@ -836,11 +626,9 @@ cs(Pid, Req)
cs(undefined, _) ->
{error, no_service}.
-%%% ---------------------------------------------------------------------------
-%%% # i/1
-%%%
-%%% Output: #state{}
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # i/1
+%% ---------------------------------------------------------------------------
%% Intialize the state of a service gen_server.
@@ -859,10 +647,10 @@ 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),
- send_event(SvcName, start),
S.
cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
@@ -910,9 +698,9 @@ get_value(Key, Vs) ->
{_, V} = lists:keyfind(Key, 1, Vs),
V.
-%%% ---------------------------------------------------------------------------
-%%% # start/3
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # start/3
+%% ---------------------------------------------------------------------------
%% If the initial start/3 at service/transport start succeeds then
%% subsequent calls to start/4 on the same service will also succeed
@@ -942,20 +730,28 @@ type(connect = T) -> T.
%% start/4
-start(Ref, Type, Opts, #state{peerT = PeerT,
- connT = ConnT,
+start(Ref, Type, Opts, #state{watchdogT = WatchdogT,
+ peerT = PeerT,
+ options = SvcOpts,
service_name = SvcName,
- service = Svc})
+ service = Svc0})
when Type == connect;
Type == accept ->
- Pid = s(Type, Ref, {ConnT,
+ #diameter_service{applications = Apps}
+ = Svc
+ = merge_service(Opts, Svc0),
+ {_,_} = Mask = proplists:get_value(sequence, SvcOpts),
+ Pid = s(Type, Ref, {diameter_traffic:make_recvdata([SvcName,
+ PeerT,
+ Apps,
+ Mask]),
Opts,
- SvcName,
- merge_service(Opts, Svc)}),
- insert(PeerT, #peer{pid = Pid,
- type = Type,
- ref = Ref,
- options = Opts}),
+ SvcOpts,
+ Svc}),
+ insert(WatchdogT, #watchdog{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts}),
Pid.
%% Note that the service record passed into the watchdog is the merged
@@ -963,13 +759,8 @@ start(Ref, Type, Opts, #state{peerT = PeerT,
%% record is what is passed back into application callbacks.
s(Type, Ref, T) ->
- case diameter_watchdog:start({Type, Ref}, T) of
- {_MRef, Pid} ->
- Pid;
- Pid when is_pid(Pid) -> %% from old code
- erlang:monitor(process, Pid),
- Pid
- end.
+ {_MRef, Pid} = diameter_watchdog:start({Type, Ref}, T),
+ Pid.
%% merge_service/2
@@ -1003,100 +794,115 @@ ms({capabilities, Opts}, #diameter_service{capabilities = Caps0} = Svc)
ms(_, Svc) ->
Svc.
-%%% ---------------------------------------------------------------------------
-%%% # accepted/3
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # accepted/3
+%% ---------------------------------------------------------------------------
-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 as started
- start(Ref, T, Opts, S). %% start new watchdog
+accepted(Pid, _TPid, #state{watchdogT = WatchdogT} = S) ->
+ #watchdog{ref = Ref, type = accept = T, peer = false, options = Opts}
+ = Wd
+ = fetch(WatchdogT, Pid),
+ insert(WatchdogT, Wd#watchdog{peer = true}),%% mark replacement as started
+ start(Ref, T, Opts, S). %% start new watchdog
fetch(Tid, Key) ->
[T] = ets:lookup(Tid, Key),
- 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.
+ T.
+
+%% ---------------------------------------------------------------------------
+%% # watchdog/6
+%%
+%% React to a watchdog state transition.
+%% ---------------------------------------------------------------------------
-%%% ---------------------------------------------------------------------------
-%%% # connection_up/3
-%%% ---------------------------------------------------------------------------
+%% Watchdog has a new open connection.
+watchdog(TPid, [T], _, ?WD_OKAY, Wd, State) ->
+ connection_up({TPid, T}, Wd, State);
-%% Watchdog process has reached state OKAY.
+%% Watchdog has a new connection that will be opened after DW[RA]
+%% exchange.
+watchdog(TPid, [T], _, ?WD_REOPEN, Wd, State) ->
+ reopen({TPid, T}, Wd, State);
+
+%% Watchdog has recovered a suspect connection.
+watchdog(TPid, [], ?WD_SUSPECT, ?WD_OKAY, Wd, State) ->
+ #watchdog{peer = TPid} = Wd, %% assert
+ connection_up(Wd, State);
+
+%% Watchdog has an unresponsive connection.
+watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) ->
+ #watchdog{peer = TPid} = Wd, %% assert
+ connection_down(Wd, To, State);
+
+%% Watchdog has lost its connection.
+watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) ->
+ close(Wd, S),
+ connection_down(Wd, To, S),
+ ets:delete(PeerT, TPid);
+
+watchdog(_, [], _, _, _, _) ->
+ ok.
-connection_up(Pid, {TPid, {Caps, SApps, Pkt}}, #state{peerT = PeerT,
- connT = ConnT}
- = S) ->
- P = fetch(PeerT, Pid),
- C = #conn{pid = TPid,
- apps = SApps,
- caps = Caps,
- peer = Pid},
-
- insert(ConnT, C),
- 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
-%%% ---------------------------------------------------------------------------
-
-%% Peer process has transitioned back into the open state. Note that there
-%% has been no new capabilties exchange in this case.
-
-connection_up(Pid, #state{peerT = PeerT,
- connT = ConnT}
- = S) ->
- #peer{conn = TPid} = P = fetch(PeerT, Pid),
- C = fetch(ConnT, TPid),
- connection_up([], P, C, S).
+%% ---------------------------------------------------------------------------
+%% # connection_up/3
+%% ---------------------------------------------------------------------------
-%% connection_up/4
+%% Watchdog process has reached state OKAY.
-connection_up(T, P, C, #state{peerT = PeerT,
- local_peers = LDict,
- service_name = SvcName,
- service
- = #diameter_service{applications = Apps}}
- = S) ->
- #peer{conn = TPid, op_state = {?STATE_DOWN, _}}
- = P,
- #conn{apps = SApps, caps = Caps}
- = C,
+connection_up({TPid, {Caps, SupportedApps, Pkt}},
+ #watchdog{pid = Pid}
+ = Wd,
+ #state{peerT = PeerT}
+ = S) ->
+ Pr = #peer{pid = TPid,
+ apps = SupportedApps,
+ caps = Caps,
+ watchdog = Pid},
+ insert(PeerT, Pr),
+ connection_up([Pkt], Wd#watchdog{peer = TPid}, Pr, S).
+
+%% ---------------------------------------------------------------------------
+%% # reopen/3
+%% ---------------------------------------------------------------------------
+
+reopen({TPid, {Caps, SupportedApps, _Pkt}},
+ #watchdog{pid = Pid}
+ = Wd,
+ #state{watchdogT = WatchdogT,
+ peerT = PeerT}) ->
+ insert(PeerT, #peer{pid = TPid,
+ apps = SupportedApps,
+ caps = Caps,
+ watchdog = Pid}),
+ insert(WatchdogT, Wd#watchdog{state = ?WD_REOPEN,
+ peer = TPid}).
+
+%% ---------------------------------------------------------------------------
+%% # connection_up/2
+%% ---------------------------------------------------------------------------
+
+%% Watchdog has recovered as suspect connection. Note that there has
+%% been no new capabilties exchange in this case.
+
+connection_up(#watchdog{peer = TPid} = Wd, #state{peerT = PeerT} = S) ->
+ connection_up([], Wd, fetch(PeerT, TPid), S).
- insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}),
+%% connection_up/4
- request_peer_up(TPid),
+connection_up(Extra,
+ #watchdog{peer = TPid}
+ = Wd,
+ #peer{apps = SApps, caps = Caps}
+ = Pr,
+ #state{watchdogT = WatchdogT,
+ local_peers = LDict,
+ service_name = SvcName,
+ service = #diameter_service{applications = Apps}}
+ = S) ->
+ insert(WatchdogT, Wd#watchdog{state = ?WD_OKAY}),
+ diameter_traffic:peer_up(TPid),
insert_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
- report_status(up, P, C, S, T).
+ report_status(up, Wd, Pr, S, Extra).
insert_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> ilp(A, T, D) end, LDict, SApps).
@@ -1106,72 +912,109 @@ ilp({Id, Alias}, {TC, SA}, LDict) ->
?Dict:append(Alias, TC, LDict).
init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) ->
- #diameter_app{module = ModX,
- id = Id} %% assert
+ #diameter_app{id = Id} %% assert
+ = App
= find_app(Alias, Apps),
- peer_cb({ModX, peer_up, [SvcName, TC]}, Alias)
+ peer_cb(App, peer_up, [SvcName, TC])
orelse exit(TPid, kill). %% fake transport failure
-%% find_app/2
+%% ---------------------------------------------------------------------------
+%% # find_incoming_app/3
+%% ---------------------------------------------------------------------------
-find_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}]};
+%% No one should be sending the relay identifier.
+find_incoming_app(#peer{caps = Caps}, ?APP_ID_RELAY, _) ->
+ Caps;
+
+find_incoming_app(Peer, Id, Apps)
+ when is_integer(Id) ->
+ find_incoming_app(Peer, [Id, ?APP_ID_RELAY], Apps);
+
+%% Note that the apps represented in SApps may be a strict subset of
+%% those in Apps.
+find_incoming_app(#peer{apps = SApps, caps = Caps}, Ids, Apps) ->
+ case keyfind(Ids, 1, SApps) of
+ {_Id, Alias} ->
+ {#diameter_app{} = find_app(Alias, Apps), Caps};
+ false ->
+ Caps
+ end.
+
+%% keyfind/3
+
+keyfind([], _, _) ->
+ false;
+keyfind([Key | Rest], Pos, L) ->
+ case lists:keyfind(Key, Pos, L) of
+ false ->
+ keyfind(Rest, Pos, L);
+ T ->
+ T
+ end.
+
+%% find_outgoing_app/2
+
+find_outgoing_app(Alias, Apps) ->
+ case find_app(Alias, Apps) of
+ #diameter_app{id = ?APP_ID_RELAY} ->
+ false;
A ->
A
end.
+%% find_app/2
+
+find_app(Alias, Apps) ->
+ lists:keyfind(Alias, #diameter_app.alias, Apps).
+
%% Don't bring down the service (and all associated connections)
%% regardless of what happens.
-peer_cb(MFA, Alias) ->
- try state_cb(MFA, Alias) of
+peer_cb(App, F, A) ->
+ try state_cb(App, F, A) of
ModS ->
- mod_state(Alias, ModS),
+ mod_state(App#diameter_app.alias, ModS),
true
catch
E:R ->
- diameter_lib:error_report({failure, {E, R, Alias, ?STACK}}, MFA),
+ diameter_lib:error_report({failure, {E, R, ?STACK}},
+ {App, F, A}),
false
end.
-%%% ---------------------------------------------------------------------------
-%%% # connection_down/2
-%%% ---------------------------------------------------------------------------
-
-%% Watchdog has transitioned out of state OKAY.
-
-connection_down(Pid, #state{peerT = PeerT,
- connT = ConnT}
- = S) ->
- #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, WS}}),
- connection_down(P,C,S).
+%% ---------------------------------------------------------------------------
+%% # connection_down/3
+%% ---------------------------------------------------------------------------
-%% connection_down/3
-
-connection_down(#peer{op_state = {?STATE_DOWN, _}}, _, _) ->
- ok;
-
-connection_down(#peer{conn = TPid,
- op_state = {?STATE_UP, _}}
- = P,
- #conn{caps = Caps,
+connection_down(#watchdog{state = ?WD_OKAY,
+ peer = TPid}
+ = Wd,
+ #peer{caps = Caps,
apps = SApps}
- = C,
+ = Pr,
#state{service_name = SvcName,
service = #diameter_service{applications = Apps},
local_peers = LDict}
= S) ->
- report_status(down, P, C, S, []),
+ report_status(down, Wd, Pr, S, []),
remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
- request_peer_down(TPid, S).
+ diameter_traffic:peer_down(TPid);
+
+connection_down(#watchdog{}, #peer{}, _) ->
+ ok;
+
+connection_down(#watchdog{state = WS,
+ peer = TPid}
+ = Wd,
+ To,
+ #state{watchdogT = WatchdogT,
+ peerT = PeerT}
+ = S)
+ when is_atom(To) ->
+ insert(WatchdogT, Wd#watchdog{state = To}),
+ ?WD_OKAY == WS
+ andalso
+ connection_down(Wd, fetch(PeerT, TPid), S).
remove_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
@@ -1182,71 +1025,58 @@ rlp({Id, Alias}, {TC, SA}, LDict) ->
?Dict:store(Alias, lists:delete(TC, L), LDict).
down_conn(Id, Alias, TC, {SvcName, Apps}) ->
- #diameter_app{module = ModX,
- id = Id} %% assert
+ #diameter_app{id = Id} %% assert
+ = App
= find_app(Alias, Apps),
- peer_cb({ModX, peer_down, [SvcName, TC]}, Alias).
+ peer_cb(App, peer_down, [SvcName, TC]).
-%%% ---------------------------------------------------------------------------
-%%% # peer_down/3
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # watchdog_down/2
+%% ---------------------------------------------------------------------------
%% Watchdog process has died.
-peer_down(Pid, Reason, #state{peerT = PeerT} = S) ->
- P = fetch(PeerT, Pid),
- ets:delete_object(PeerT, P),
- closed(Reason, P, S),
- restart(P,S),
- peer_down(P,S).
-
-%% Send an event at connection establishment failure.
-closed({shutdown, {close, _TPid, Reason}},
- #peer{op_state = {?STATE_DOWN, _},
- ref = Ref,
- type = Type,
- options = Opts},
- #state{service_name = SvcName}) ->
- send_event(SvcName, {closed, Ref, Reason, {type(Type), Opts}});
-closed(_, _, _) ->
- ok.
+watchdog_down(Pid, #state{watchdogT = WatchdogT} = S) ->
+ Wd = fetch(WatchdogT, Pid),
+ ets:delete_object(WatchdogT, Wd),
+ restart(Wd,S),
+ wd_down(Wd,S).
-%% The watchdog has never reached OKAY ...
-peer_down(#peer{conn = B}, _)
+%% Watchdog has never reached OKAY ...
+wd_down(#watchdog{peer = B}, _)
when is_boolean(B) ->
ok;
%% ... or maybe it has.
-peer_down(#peer{conn = TPid} = P, #state{connT = ConnT} = S) ->
- #conn{} = C = fetch(ConnT, TPid),
- ets:delete_object(ConnT, C),
- connection_down(P,C,S).
+wd_down(#watchdog{peer = TPid} = Wd, #state{peerT = PeerT} = S) ->
+ connection_down(Wd, ?WD_DOWN, S),
+ ets:delete(PeerT, TPid).
%% restart/2
-restart(P,S) ->
- q_restart(restart(P), S).
+restart(Wd, S) ->
+ q_restart(restart(Wd), S).
%% restart/1
%% Always try to reconnect.
-restart(#peer{ref = Ref,
- type = connect = T,
- options = Opts,
- started = Time}) ->
+restart(#watchdog{ref = Ref,
+ type = connect = T,
+ options = Opts,
+ started = Time}) ->
{Time, {Ref, T, Opts}};
%% Transport connection hasn't yet been accepted ...
-restart(#peer{ref = Ref,
- type = accept = T,
- options = Opts,
- conn = false,
- started = Time}) ->
+restart(#watchdog{ref = Ref,
+ type = accept = T,
+ options = Opts,
+ peer = false,
+ started = Time}) ->
{Time, {Ref, T, Opts}};
%% ... or it has: a replacement has already been spawned.
-restart(#peer{type = accept}) ->
+restart(#watchdog{type = accept}) ->
false.
%% q_restart/2
@@ -1296,9 +1126,9 @@ tc(true, {Ref, Type, Opts}, #state{service_name = SvcName}
tc(false = No, _, _) -> %% removed
No.
-%%% ---------------------------------------------------------------------------
-%%% # close/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # close/2
+%% ---------------------------------------------------------------------------
%% The watchdog doesn't start a new fsm in the accept case, it
%% simply stays alive until someone tells it to die in order for
@@ -1307,14 +1137,13 @@ tc(false = No, _, _) -> %% removed
%% the accepting watchdog upon reception of a CER from the previously
%% connected peer, or us after reconnect_timer timeout.
-close(Pid, #state{service_name = SvcName,
- peerT = PeerT}) ->
- #peer{pid = Pid,
- type = accept,
- ref = Ref,
- options = Opts}
- = fetch(PeerT, Pid),
-
+close(#watchdog{type = connect}, _) ->
+ ok;
+close(#watchdog{type = accept,
+ pid = Pid,
+ ref = Ref,
+ options = Opts},
+ #state{service_name = SvcName}) ->
c(Pid, diameter_config:have_transport(SvcName, Ref), Opts).
%% Tell watchdog to (maybe) die later ...
@@ -1332,21 +1161,21 @@ c(Pid, false, _Opts) ->
%% which a new connection attempt is expected of a connecting peer.
%% The value should be greater than the peer's Tc + jitter.
-%%% ---------------------------------------------------------------------------
-%%% # reconnect/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # reconnect/2
+%% ---------------------------------------------------------------------------
reconnect(Pid, #state{service_name = SvcName,
- peerT = PeerT}) ->
- #peer{ref = Ref,
- type = connect,
- options = Opts}
- = fetch(PeerT, Pid),
+ watchdogT = WatchdogT}) ->
+ #watchdog{ref = Ref,
+ type = connect,
+ options = Opts}
+ = fetch(WatchdogT, Pid),
send_event(SvcName, {reconnect, Ref, Opts}).
-%%% ---------------------------------------------------------------------------
-%%% # call_module/4
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # call_module/4
+%% ---------------------------------------------------------------------------
%% Backwards compatibility and never documented/advertised. May be
%% removed.
@@ -1368,10 +1197,10 @@ call_module(Mod, Req, From, #state{service
{reply, {error, Reason}, S}
end.
-cm([#diameter_app{module = ModX, alias = Alias}], Req, From, Svc) ->
- MFA = {ModX, handle_call, [Req, From, Svc]},
+cm([#diameter_app{alias = Alias} = App], Req, From, Svc) ->
+ Args = [Req, From, Svc],
- try state_cb(MFA, Alias) of
+ try state_cb(App, handle_call, Args) of
{noreply = T, ModS} ->
mod_state(Alias, ModS),
T;
@@ -1379,11 +1208,13 @@ cm([#diameter_app{module = ModX, alias = Alias}], Req, From, Svc) ->
mod_state(Alias, ModS),
{T, RC};
T ->
- diameter_lib:error_report({invalid, T}, MFA),
+ diameter_lib:error_report({invalid, T},
+ {App, handle_call, Args}),
invalid
catch
E: Reason ->
- diameter_lib:error_report({failure, {E, Reason, ?STACK}}, MFA),
+ diameter_lib:error_report({failure, {E, Reason, ?STACK}},
+ {App, handle_call, Args}),
failure
end;
@@ -1393,1258 +1224,16 @@ cm([], _, _, _) ->
cm([_,_|_], _, _, _) ->
multiple.
-%%% ---------------------------------------------------------------------------
-%%% # send_request/6
-%%% ---------------------------------------------------------------------------
-
-%% Send an outgoing request in its dedicated process.
-%%
-%% Note that both encode of the outgoing request and of the received
-%% answer happens in this process. It's also this process that replies
-%% to the caller. The service process only handles the state-retaining
-%% callbacks.
-%%
-%% The mod field of the #diameter_app{} here includes any extra
-%% arguments passed to diameter:call/2.
-
-send_request({TPid, Caps, App} = T, Mask, Msg, Opts, Caller, SvcName) ->
- #diameter_app{module = ModX}
- = App,
-
- 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]);
-
-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_prepare_packet(_, Bin)
- when is_binary(Bin) ->
- #diameter_packet{header = diameter_codec:decode_header(Bin),
- bin = Bin};
-
-make_prepare_packet(Mask, #diameter_packet{msg = [#diameter_header{} = Hdr
- | Avps]}
- = Pkt) ->
- Pkt#diameter_packet{msg = [make_prepare_header(Mask, Hdr) | Avps]};
-
-make_prepare_packet(Mask, #diameter_packet{header = Hdr} = Pkt) ->
- Pkt#diameter_packet{header = make_prepare_header(Mask, Hdr)};
-
-make_prepare_packet(Mask, Msg) ->
- make_prepare_packet(Mask, #diameter_packet{msg = Msg}).
-
-%% make_prepare_header/1
-
-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_prepare_header(Mask, #diameter_header{end_to_end_id = undefined,
- hop_by_hop_id = undefined}) ->
- Seq = diameter_session:sequence(Mask),
- make_prepare_header(#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_prepare_header/1
-
-make_prepare_header(#diameter_header{version = undefined} = Hdr) ->
- make_prepare_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
-
-make_prepare_header(#diameter_header{} = Hdr) ->
- Hdr;
-
-make_prepare_header(T) ->
- ?ERROR({invalid_header, T}).
-
-%% make_request_packet/2
-%%
-%% Reconstruct a diameter_packet from the return value of
-%% prepare_request or prepare_retransmit callback.
-
-make_request_packet(Bin, _)
- when is_binary(Bin) ->
- make_prepare_packet(false, Bin);
-
-make_request_packet(#diameter_packet{msg = [#diameter_header{} | _]}
- = Pkt,
- _) ->
- Pkt;
-
-%% Returning a diameter_packet with no header from a prepare_request
-%% or prepare_retransmit callback retains the header passed into it.
-%% 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}) ->
- Pkt#diameter_packet{header = fold_record(Hdr0, Hdr)};
-
-make_request_packet(Msg, Pkt) ->
- Pkt#diameter_packet{msg = Msg}.
-
-%% fold_record/2
-
-fold_record(undefined, R) ->
- R;
-fold_record(Rec, R) ->
- diameter_lib:fold_tuple(2, Rec, R).
-
-%% send_req/6
-
-send_req(Pkt, {TPid, Caps, App}, Opts, Caller, SvcName, Fs) ->
- #diameter_app{alias = Alias,
- dictionary = Dict,
- module = ModX,
- options = [{answer_errors, AE} | _]}
- = App,
-
- EPkt = encode(Dict, Pkt, Fs),
-
- #options{filter = Filter,
- timeout = Timeout}
- = Opts,
-
- Req = #request{packet = Pkt,
- from = Caller,
- handler = self(),
- transport = TPid,
- caps = Caps,
- app = Alias,
- filter = Filter,
- dictionary = Dict,
- module = ModX},
-
- try
- TRef = send_request(TPid, EPkt, Req, Timeout),
- ack(Caller),
- handle_answer(SvcName, AE, recv_answer(Timeout, SvcName, {TRef, Req}))
- after
- erase_request(EPkt)
- end.
-
-%% Tell caller a send has been attempted.
-ack({Pid, Ref}) ->
- Pid ! Ref.
-
-%% recv_answer/3
-
-recv_answer(Timeout,
- SvcName,
- {TRef, #request{from = {_, Ref}, packet = RPkt} = Req}
- = T) ->
-
- %% Matching on TRef below ensures we ignore messages that pertain
- %% to a previous transport prior to failover. The answer message
- %% includes the #request{} since it's not necessarily Req; that
- %% is, from the last peer to which we've transmitted.
-
- receive
- {answer = A, Ref, Rq, Pkt} -> %% Answer from peer
- {A, Rq, Pkt};
- {timeout = Reason, TRef, _} -> %% No timely reply
- {error, Req, Reason};
- {failover = Reason, TRef, false} -> %% No alternate peer
- {error, Req, Reason};
- {failover, TRef, Transport} -> %% Resend to alternate peer
- try_retransmit(Timeout, SvcName, Req, Transport);
- {failover, TRef} -> %% May have missed failover notification
- Seqs = diameter_codec:sequence_numbers(RPkt),
- Pid = whois(SvcName),
- is_pid(Pid) andalso (Pid ! {failover, TRef, Seqs}),
- recv_answer(Timeout, SvcName, T)
- end.
-%% Note that failover starts a new timer and that expiry of an old
-%% timer value is ignored. This means that an answer could be accepted
-%% from a peer after timeout in the case of failover.
-
-try_retransmit(Timeout, SvcName, Req, Transport) ->
- try retransmit(Transport, Req, SvcName, Timeout) of
- T -> recv_answer(Timeout, SvcName, T)
- catch
- ?FAILURE(Reason) -> {error, Req, Reason}
- end.
-
-%% handle_error/3
-
-handle_error(Req, Reason, SvcName) ->
- #request{module = ModX,
- packet = Pkt,
- transport = TPid,
- caps = Caps}
- = Req,
- cb(ModX, handle_error, [Reason, msg(Pkt), SvcName, {TPid, Caps}]).
-
-msg(#diameter_packet{msg = undefined, bin = 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
-%% header or transport_data. Even allow the returned record to contain
-%% an encoded binary. This isn't the usual case but could some in
-%% handy, for test at least. (For example, to send garbage.)
-
-%% The normal case: encode the returned message.
-encode(Dict, #diameter_packet{msg = Msg, bin = undefined} = Pkt) ->
- D = pick_dictionary([Dict, ?BASE], Msg),
- diameter_codec:encode(D, Pkt);
-
-%% Callback has returned an encoded binary: just send.
-encode(_, #diameter_packet{} = Pkt) ->
- Pkt.
-
-%% pick_dictionary/2
-
-%% Pick the first dictionary that declares the application id in the
-%% specified header.
-pick_dictionary(Ds, [#diameter_header{application_id = Id} | _]) ->
- pd(Ds, fun(D) -> Id = D:id() end);
-
-%% Pick the first dictionary that knows the specified message name.
-pick_dictionary(Ds, [MsgName|_]) ->
- pd(Ds, fun(D) -> D:msg2rec(MsgName) end);
-
-%% Pick the first dictionary that knows the name of the specified
-%% message record.
-pick_dictionary(Ds, Rec) ->
- Name = element(1, Rec),
- pd(Ds, fun(D) -> D:rec2msg(Name) end).
-
-pd([D|Ds], F) ->
- try
- F(D),
- D
- catch
- error:_ ->
- pd(Ds, F)
- end;
-
-pd([], _) ->
- ?ERROR(no_dictionary).
-
-%% send_request/4
-
-send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, Timeout)
- when node() == node(TPid) ->
- %% Store the outgoing request before sending to avoid a race with
- %% reply reception.
- TRef = store_request(TPid, Bin, Req, Timeout),
- send(TPid, Pkt),
- TRef;
-
-%% Send using a remote transport: spawn a process on the remote node
-%% to relay the answer.
-send_request(TPid, #diameter_packet{} = Pkt, Req, Timeout) ->
- TRef = erlang:start_timer(Timeout, self(), timeout),
- T = {TPid, Pkt, Req, Timeout, TRef},
- spawn(node(TPid), ?MODULE, send, [T]),
- TRef.
-
-%% send/1
-
-send({TPid, Pkt, #request{handler = Pid} = Req, Timeout, TRef}) ->
- Ref = send_request(TPid, Pkt, Req#request{handler = self()}, Timeout),
- Pid ! reref(receive T -> T end, Ref, TRef).
-
-reref({T, Ref, R}, Ref, TRef) ->
- {T, TRef, R};
-reref(T, _, _) ->
- T.
-
-%% send/2
-
-send(Pid, Pkt) ->
- Pid ! {send, Pkt}.
-
-%% retransmit/4
-
-retransmit({TPid, Caps, #diameter_app{alias = Alias} = App} = T,
- #request{app = Alias, packet = Pkt}
- = Req,
- SvcName,
- Timeout) ->
- have_request(Pkt, TPid) %% Don't failover to a peer we've
- andalso ?THROW(timeout), %% already sent to.
-
- resend_req(cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]),
- T,
- Req,
- 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);
-
-resend_req(discard, _, _, _, _) ->
- ?THROW(discarded);
-
-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}).
-
-%% retransmit/6
-
-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
-
-store_request(TPid, Bin, Req, Timeout) ->
- Seqs = diameter_codec:sequence_numbers(Bin),
- TRef = erlang:start_timer(Timeout, self(), timeout),
- ets:insert(?REQUEST_TABLE, {Seqs, Req, TRef}),
- ets:member(?REQUEST_TABLE, TPid)
- orelse (self() ! {failover, TRef}), %% possibly missed failover
- TRef.
-
-%% lookup_request/2
-
-lookup_request(Msg, TPid)
- when is_pid(TPid) ->
- lookup(Msg, TPid, '_');
-
-lookup_request(Msg, TRef)
- when is_reference(TRef) ->
- lookup(Msg, '_', TRef).
-
-lookup(Msg, TPid, TRef) ->
- Seqs = diameter_codec:sequence_numbers(Msg),
- Spec = [{{Seqs, #request{transport = TPid, _ = '_'}, TRef},
- [],
- ['$_']}],
- case ets:select(?REQUEST_TABLE, Spec) of
- [{_, Req, _}] ->
- Req;
- [] ->
- false
- end.
-
-%% erase_request/1
-
-erase_request(Pkt) ->
- ets:delete(?REQUEST_TABLE, diameter_codec:sequence_numbers(Pkt)).
-
-%% match_requests/1
-
-match_requests(TPid) ->
- Pat = {'_', #request{transport = TPid, _ = '_'}, '_'},
- ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]).
-
-%% have_request/2
-
-have_request(Pkt, TPid) ->
- Seqs = diameter_codec:sequence_numbers(Pkt),
- Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'},
- '$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1).
-
-%% request_peer_up/1
-
-request_peer_up(TPid) ->
- ets:insert(?REQUEST_TABLE, {TPid}).
-
-%% request_peer_down/2
-
-request_peer_down(TPid, S) ->
- ets:delete(?REQUEST_TABLE, TPid),
- lists:foreach(fun(T) -> failover(T,S) end, match_requests(TPid)).
-%% Note that a request process can store its request after failover
-%% notifications are sent here: store_request/4 sends the notification
-%% in that case. Note also that we'll send as many notifications to a
-%% given handler as there are peers its sent to. All but one of these
-%% will be ignored.
-
-%%% ---------------------------------------------------------------------------
-%%% recv_request/3
-%%% ---------------------------------------------------------------------------
-
-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, Mask);
- [] -> %% transport has gone down
- ok
- catch
- error: badarg -> %% service has gone down (and taken table with it)
- ok
- end.
-
-%% recv_request/5
-
-recv_request(#conn{apps = SApps, caps = Caps},
- TPid,
- Pkt,
- SvcName,
- Apps,
- Mask) ->
- #diameter_caps{origin_host = {OH,_},
- origin_realm = {OR,_}}
- = Caps,
-
- #diameter_packet{header = #diameter_header{application_id = Id}}
- = Pkt,
-
- recv_request(find_recv_app(Id, SApps),
- {SvcName, OH, OR},
- TPid,
- Apps,
- Mask,
- Caps,
- Pkt).
-
-%% find_recv_app/2
-
-%% No one should be sending the relay identifier.
-find_recv_app(?APP_ID_RELAY, _) ->
- false;
-
-%% With any other id we either support it locally or as a relay.
-find_recv_app(Id, SApps) ->
- keyfind([Id, ?APP_ID_RELAY], 1, SApps).
-
-%% keyfind/3
-
-keyfind([], _, _) ->
- false;
-keyfind([Key | Rest], Pos, L) ->
- case lists:keyfind(Key, Pos, L) of
- false ->
- keyfind(Rest, Pos, L);
- T ->
- T
- end.
-
-%% recv_request/7
-
-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,
- 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) ->
- As = collect_avps(Pkt),
- protocol_error(3007, T, TPid, Pkt#diameter_packet{avps = As}).
-
-collect_avps(Pkt) ->
- case diameter_codec:collect_avps(Pkt) of
- {_Bs, As} ->
- As;
- As ->
- As
- end.
-
-%% recv_request/5
-
-%% Wrong number of bits somewhere in the message: reply.
-%%
-%% 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.
-%%
-recv_request(T, {TPid, _}, _, _, #diameter_packet{errors = [Bs | _]} = Pkt)
- when is_bitstring(Bs) ->
- protocol_error(3009, T, TPid, Pkt);
-
-%% Either we support this application but don't recognize the command
-%% or we're a relay and the command isn't proxiable.
-%%
-%% DIAMETER_COMMAND_UNSUPPORTED 3001
-%% The Request contained a Command-Code that the receiver did not
-%% recognize or support. This MUST be used when a Diameter node
-%% receives an experimental command that it does not understand.
-%%
-recv_request(T,
- {TPid, _},
- #diameter_app{id = Id},
- _,
- #diameter_packet{header = #diameter_header{is_proxiable = P},
- msg = M}
- = Pkt)
- when ?APP_ID_RELAY /= Id, undefined == M;
- ?APP_ID_RELAY == Id, not P ->
- protocol_error(3001, T, TPid, Pkt);
-
-%% Error bit was set on a request.
-%%
-%% DIAMETER_INVALID_HDR_BITS 3008
-%% 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.
-%%
-recv_request(T,
- {TPid, _},
- _,
- _,
- #diameter_packet{header = #diameter_header{is_error = true}}
- = Pkt) ->
- protocol_error(3008, T, TPid, Pkt);
-
-%% A message in a locally supported application or a proxiable message
-%% 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, 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, Mask, Pkt) ->
- request_cb(cb(App, handle_request, [Pkt, SvcName, TC]),
- App,
- Mask,
- T,
- TC,
- [],
- Pkt).
-
-%% examine/1
-%%
-%% Look for errors in a decoded message. Length errors result in
-%% decode failure in diameter_codec.
-
-examine(#diameter_packet{header = #diameter_header{version
- = ?DIAMETER_VERSION}}
- = Pkt) ->
- Pkt;
-
-%% DIAMETER_UNSUPPORTED_VERSION 5011
-%% This error is returned when a request was received, whose version
-%% number is unsupported.
-
-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/7
-
-%% A reply may be an answer-message, constructed either here or by
-%% the handle_request callback. The header from the incoming request
-%% is passed into the encode so that it can retrieve the relevant
-%% command code in this case. It will also then ignore Dict and use
-%% the base encoder.
-request_cb({reply, Ans},
- #diameter_app{dictionary = Dict},
- _,
- _,
- {TPid, _},
- Fs,
- 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, _}, Fs, Pkt)
- when 3000 =< RC, RC < 4000 ->
- 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, _}, 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
-%% will be detected by examining Route-Record AVP's, (3) a
-%% Route-Record AVP will be added to the outgoing request and (3) the
-%% End-to-End Identifier will default to that in the
-%% #diameter_header{} without the need for an end_to_end_identifier
-%% option.
-%%
-%% relay and proxy are similar in that they require the same handling
-%% with respect to Route-Record and End-to-End identifier. The
-%% difference is that a proxy advertises specific applications, while
-%% a relay advertises the relay application. If a callback doesn't
-%% want to distinguish between the cases in the callback return value
-%% then 'resend' is a neutral alternative.
-%%
-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, Mask, T, TC, Fs, Pkt);
-
-request_cb(discard, _, _, _, _, _, _) ->
- ok;
-
-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/5
-
-protocol_error(RC, {_, OH, OR}, TPid, Fs, Pkt) ->
- #diameter_packet{avps = Avps} = Pkt,
- ?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Fs, Pkt).
-
-%% protocol_error/4
-
-protocol_error(RC, T, TPid, Pkt) ->
- protocol_error(RC, T, TPid, [], Pkt).
-
-%% 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, Mask, T, TC, Fs, Pkt).
-
-%% DIAMETER_LOOP_DETECTED 3005
-%% An agent detected a loop while trying to get the message to the
-%% intended recipient. The message MAY be sent to an alternate peer,
-%% if one is available, but the peer reporting the error has
-%% identified a configuration problem.
-
-resend(true, _, _, _, T, {TPid, _}, Fs, Pkt) -> %% Route-Record loop
- protocol_error(3005, T, TPid, Fs, Pkt);
-
-%% 6.1.8. 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.
-
-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(Mask),
- Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
- Msg = [Hdr, Route | Avps],
- 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
-%% handle_answer callback of the application module in question.
-%%
-%% Note that there's nothing stopping the request from being relayed
-%% back to the sender. A pick_peer callback may want to avoid this but
-%% a smart peer might recognize the potential loop and choose another
-%% route. A less smart one will probably just relay the request back
-%% again and force us to detect the loop. A pick_peer that wants to
-%% avoid this can specify filter to avoid the possibility.
-%% Eg. {neg, {host, OH} where #diameter_caps{origin_host = {OH, _}}.
-%%
-%% RFC 6.3 says that a relay agent does not modify Origin-Host but
-%% says nothing about a proxy. Assume it should behave the same way.
-
-%% resend/4
-%%
-%% Relay a reply to a relayed request.
-
-%% Answer from the peer: reset the hop by hop identifier and send.
-resend(#diameter_packet{bin = B}
- = Pkt,
- _,
- TPid,
- Fs,
- #diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
- 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, Fs, Pkt) ->
- protocol_error(3002, T, TPid, Fs, Pkt).
-
-%% is_loop/4
-%%
-%% Is there a Route-Record AVP with our Origin-Host?
-
-is_loop(Code,
- Vid,
- Bin,
- [#diameter_avp{code = Code, vendor_id = Vid, data = Bin} | _]) ->
- true;
-
-is_loop(_, _, _, []) ->
- false;
-
-is_loop(Code, Vid, OH, [_ | Avps])
- when is_binary(OH) ->
- is_loop(Code, Vid, OH, Avps);
-
-is_loop(Code, Vid, OH, Avps) ->
- is_loop(Code, Vid, ?BASE:avp(encode, OH, 'Route-Record'), Avps).
-
-%% reply/5
-%%
-%% Send a locally originating reply.
-
-%% Skip the setting of Result-Code and Failed-AVP's below.
-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, Fs, #diameter_packet{errors = Es} = ReqPkt)
- when [] == Es;
- is_record(hd(Msg), diameter_header) ->
- Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)),
- 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, 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, #diameter_packet{transport_data = TD})
- when is_binary(Bin) ->
- #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
-
-rc({RC, _}) ->
- RC;
-rc(RC) ->
- RC.
-
-%% rc/4
-
-rc(Rec, RC, Failed, Dict)
- when is_integer(RC) ->
- 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, _) ->
- Ans ++ Avps; %% Values nearer tail take precedence.
-
-%% ... or record.
-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, _) ->
- No;
-
-failed_avp(Rec, Failed, Dict) ->
- [fa(Rec, [{'AVP', Failed}], Dict)].
-
-%% Reply as name and tuple list ...
-fa([MsgName | Values], FailedAvp, Dict) ->
- R = Dict:msg2rec(MsgName),
- try
- Dict:'#info-'(R, {index, 'Failed-AVP'}),
- {'Failed-AVP', [FailedAvp]}
- catch
- error: _ ->
- Avps = proplists:get_value('AVP', Values, []),
- A = #diameter_avp{name = 'Failed-AVP',
- value = FailedAvp},
- {'AVP', [A|Avps]}
- end;
-
-%% ... or record.
-fa(Rec, FailedAvp, Dict) ->
- try
- {'Failed-AVP', [FailedAvp]}
- catch
- error: _ ->
- Avps = Dict:'get-'('AVP', Rec),
- A = #diameter_avp{name = 'Failed-AVP',
- value = FailedAvp},
- {'AVP', [A|Avps]}
- end.
-
-%% 3. Diameter Header
-%%
-%% E(rror) - If set, the message contains a protocol error,
-%% and the message will not conform to the ABNF
-%% 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.
-
-%% 3.2. Command Code ABNF specification
-%%
-%% 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.
-
-%% 7.1.3. Protocol Errors
-%%
-%% 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 and only these errors MUST
-%% only be used in answer messages whose 'E' bit is set.
-
-%% Thus, only construct answers to protocol errors. Other errors
-%% require an message-specific answer and must be handled by the
-%% application.
-
-%% 6.2. Diameter Answer Processing
-%%
-%% When a request is locally processed, the following procedures MUST be
-%% applied to create the associated answer, in addition to any
-%% additional procedures that MAY be discussed in the Diameter
-%% application defining the command:
-%%
-%% - The same Hop-by-Hop identifier in the request is used in the
-%% answer.
-%%
-%% - The local host's identity is encoded in the Origin-Host AVP.
-%%
-%% - The Destination-Host and Destination-Realm AVPs MUST NOT be
-%% present in the answer message.
-%%
-%% - The Result-Code AVP is added with its value indicating success or
-%% failure.
-%%
-%% - If the Session-Id is present in the request, it MUST be included
-%% in the answer.
-%%
-%% - Any Proxy-Info AVPs in the request MUST be added to the answer
-%% message, in the same order they were present in the request.
-%%
-%% - The 'P' bit is set to the same value as the one in the request.
-%%
-%% - 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
-%% the above processing rules.
-
-%% 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
-%% Error-Message AVP is not intended to be useful in real-time, and
-%% SHOULD NOT be expected to be parsed by network entities.
-
-%% answer_message/2
-
-answer_message({OH, OR, RC}, Avps) ->
- {Code, _, Vid} = ?BASE:avp_header('Session-Id'),
- ['answer-message', {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Result-Code', RC}
- | session_id(Code, Vid, Avps)].
-
-session_id(Code, Vid, Avps)
- when is_list(Avps) ->
- try
- {value, #diameter_avp{data = D}} = find_avp(Code, Vid, Avps),
- [{'Session-Id', [?BASE:avp(decode, D, 'Session-Id')]}]
- catch
- error: _ ->
- []
- end.
-
-%% find_avp/3
-
-find_avp(Code, Vid, Avps)
- when is_integer(Code), (undefined == Vid orelse is_integer(Vid)) ->
- find(fun(A) -> is_avp(Code, Vid, A) end, Avps).
-
-%% The final argument here could be a list of AVP's, depending on the case,
-%% but we're only searching at the top level.
-is_avp(Code, Vid, #diameter_avp{code = Code, vendor_id = Vid}) ->
- true;
-is_avp(_, _, _) ->
- false.
-
-find(_, []) ->
- false;
-find(Pred, [H|T]) ->
- case Pred(H) of
- true ->
- {value, H};
- false ->
- find(Pred, T)
- end.
-
-%% 7. Error Handling
-%%
-%% 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:
-%%
-%% - 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 containing the
-%% offending AVP.
-%%
-%% - An AVP that is received with an unrecognized value causes an
-%% answer to be returned with the Result-Code AVP set to
-%% DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
-%% AVP causing the error.
-%%
-%% - A command is received with an AVP that is omitted, yet is
-%% mandatory according to the command's ABNF. The receiver issues an
-%% answer with the Result-Code set to 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.
-%%
-%% 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
-%% specific errors that can be described by this AVP are described in
-%% the following section.
-
-%% 7.5. Failed-AVP AVP
-%%
-%% The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
-%% 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.
-%%
-%% 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
-%% occurrences.
-%%
-%% A Diameter message MAY 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
-%% required length for the omitted AVP will be added.
-
-%%% ---------------------------------------------------------------------------
-%%% # handle_answer/3
-%%% ---------------------------------------------------------------------------
-
-%% Process an answer message in call-specific process.
-
-handle_answer(SvcName, _, {error, Req, Reason}) ->
- handle_error(Req, Reason, SvcName);
-
-handle_answer(SvcName,
- AnswerErrors,
- {answer, #request{dictionary = Dict} = Req, Pkt}) ->
- 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?
-
-answer(Pkt, SvcName, AE, #request{transport = TPid,
- dictionary = Dict}
- = Req) ->
- try
- incr(recv, Pkt, Dict, TPid)
- of
- _ -> a(Pkt, SvcName, AE, Req)
- catch
- exit: {invalid_error_bit, _} = E ->
- a(Pkt#diameter_packet{errors = [E]}, SvcName, AE, Req)
- end.
-
-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}]);
-
-a(Pkt, SvcName, report, Req) ->
- x(errors, handle_answer, [SvcName, Req, Pkt]);
-
-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
-%% header is what we expect. (TODO: Does the rfc says anything about
-%% this?)
-
-%% incr/4
-%%
-%% Increment a stats counter for an incoming or outgoing message.
-
-%% TODO: fix
-incr(_, #diameter_packet{msg = undefined}, _, _) ->
- ok;
-
-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(Dict, 'Result-Code', Rec)),
- PE = is_protocol_error(RC),
-
- %% Check that the E bit is set only for 3xxx result codes.
- (not (E orelse PE))
- orelse (E andalso PE)
- orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
-
- 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
-%% applications MUST include either one Result-Code AVP or one
-%% Experimental-Result AVP.
-%%
-%% Maintain statistics assuming one or the other, not both, which is
-%% surely the intent of the RFC.
-
-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.
-
-er([{_,_,N} = T | _])
- when is_integer(N) ->
- T;
-er({_,_,N} = T)
- when is_integer(N) ->
- T;
-er(_) ->
- undefined.
-
-%% 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)
- when is_integer(N) ->
- N;
-int(_) ->
- undefined.
-
-is_protocol_error(RC) ->
- 3000 =< RC andalso RC < 4000.
-
--spec x(any(), atom(), list()) -> no_return().
-
-%% Warn and exit request process on errors in an incoming answer.
-x(Reason, F, A) ->
- diameter_lib:warning_report(Reason, {?MODULE, F, A}),
- x(Reason).
-
-x(T) ->
- exit(T).
-
-%%% ---------------------------------------------------------------------------
-%%% # failover/[23]
-%%% ---------------------------------------------------------------------------
-
-%% Failover as a consequence of request_peer_down/2.
-failover({_, #request{handler = Pid} = Req, TRef}, S) ->
- Pid ! {failover, TRef, rt(Req, S)}.
-
-%% Failover as a consequence of store_request/4.
-failover(TRef, Seqs, S)
- when is_reference(TRef) ->
- case lookup_request(Seqs, TRef) of
- #request{} = Req ->
- failover({Seqs, Req, TRef}, S);
- false ->
- ok
- end.
-
-%% prepare_request returned a binary ...
-rt(#request{packet = #diameter_packet{msg = undefined}}, _) ->
- false; %% TODO: Not what we should do.
-
-%% ... or not.
-rt(#request{packet = #diameter_packet{msg = Msg},
- dictionary = Dict}
- = Req,
- S) ->
- find_transport(get_destination(Dict, Msg), Req, S).
-
-%%% ---------------------------------------------------------------------------
-%%% # report_status/5
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # report_status/5
+%% ---------------------------------------------------------------------------
report_status(Status,
- #peer{ref = Ref,
- conn = TPid,
- type = Type,
- options = Opts},
- #conn{apps = [_|_] = As,
+ #watchdog{ref = Ref,
+ peer = TPid,
+ type = Type,
+ options = Opts},
+ #peer{apps = [_|_] = As,
caps = Caps},
#state{service_name = SvcName}
= S,
@@ -2662,9 +1251,9 @@ send_event(SvcName, Info) ->
send_event(#diameter_event{service = SvcName} = E) ->
lists:foreach(fun({_, Pid}) -> Pid ! E end, subscriptions(SvcName)).
-%%% ---------------------------------------------------------------------------
-%%% # share_peer/5
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # share_peer/5
+%% ---------------------------------------------------------------------------
share_peer(up, Caps, Aliases, TPid, #state{options = [_, {_, true} | _],
service_name = Svc}) ->
@@ -2673,9 +1262,9 @@ share_peer(up, Caps, Aliases, TPid, #state{options = [_, {_, true} | _],
share_peer(_, _, _, _, _) ->
ok.
-%%% ---------------------------------------------------------------------------
-%%% # share_peers/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # share_peers/2
+%% ---------------------------------------------------------------------------
share_peers(Pid, #state{options = [_, {_, true} | _],
local_peers = PDict}) ->
@@ -2687,9 +1276,9 @@ share_peers(_, _) ->
sp(Pid, Alias, Peers) ->
lists:foreach(fun({P,C}) -> Pid ! {peer, P, [Alias], C} end, Peers).
-%%% ---------------------------------------------------------------------------
-%%% # remote_peer_up/4
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # remote_peer_up/4
+%% ---------------------------------------------------------------------------
remote_peer_up(Pid, Aliases, Caps, #state{options = [_, _, {_, true} | _],
service = Svc,
@@ -2709,9 +1298,9 @@ rpu(Pid, Caps, PDict, Aliases) ->
T = {Pid, Caps},
lists:foreach(fun(A) -> ?Dict:append(A, T, PDict) end, Aliases).
-%%% ---------------------------------------------------------------------------
-%%% # remote_peer_down/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # remote_peer_down/2
+%% ---------------------------------------------------------------------------
remote_peer_down(Pid, #state{options = [_, _, {_, true} | _],
shared_peers = PDict}) ->
@@ -2720,164 +1309,20 @@ remote_peer_down(Pid, #state{options = [_, _, {_, true} | _],
rpd(Pid, Alias, PDict) ->
?Dict:update(Alias, fun(Ps) -> lists:keydelete(Pid, 1, Ps) end, PDict).
-%%% ---------------------------------------------------------------------------
-%%% find_transport/[34]
-%%%
-%%% Output: {TransportPid, #diameter_caps{}, #diameter_app{}}
-%%% | false
-%%% | {error, Reason}
-%%% ---------------------------------------------------------------------------
-
-%% Initial call, from an arbitrary process.
-find_transport({alias, Alias}, Msg, Opts, #state{service = Svc} = S) ->
- #diameter_service{applications = Apps} = Svc,
- ft(find_send_app(Alias, Apps), Msg, Opts, S);
-
-%% Relay or proxy send.
-find_transport(#diameter_app{} = App, Msg, Opts, S) ->
- ft(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(Dict, Msg),
- Filter,
- S);
-ft(false = No, _, _, _) ->
- No.
-
-%% This can't be used if we're a relay and sending a message
-%% in an application not known locally. (TODO)
-find_send_app(Alias, Apps) ->
- case lists:keyfind(Alias, #diameter_app.alias, Apps) of
- #diameter_app{id = ?APP_ID_RELAY} ->
- false;
- T ->
- T
- end.
-
-%% Retransmission, in the service process.
-find_transport([_,_] = RH,
- Req,
- #state{service = #diameter_service{pid = Pid,
- applications = Apps}}
- = S)
- when self() == Pid ->
- #request{app = Alias,
- filter = Filter,
- module = ModX}
- = Req,
- #diameter_app{}
- = App
- = lists:keyfind(Alias, #diameter_app.alias, Apps),
-
- pick_peer(App#diameter_app{module = ModX},
- RH,
- Filter,
- S).
-
-%% get_destination/2
-
-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
-%% than the list of no values we treat it as by mapping to undefined.
-%% This behaviour is documented.
-str([]) ->
- undefined;
-str(T) ->
- T.
-
-%% get_avp_value/3
-%%
-%% Find an AVP in a message of one of three forms:
-%%
-%% - a message record (as generated from a .dia spec) or
-%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
-%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
-%%
-%% In the first two forms a dictionary module is used at encode to
-%% identify the type of the AVP and its arity in the message in
-%% 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),
- [A|_] = lists:dropwhile(fun(#diameter_avp{code = C, vendor_id = V}) ->
- C /= Code orelse V /= VId
- end,
- Avps),
- avp_decode(Dict, Name, A)
- catch
- error: _ ->
- undefined
- end;
-
-%% Outgoing message as a name/values list.
-get_avp_value(_, Name, [_MsgName | Avps]) ->
- case lists:keyfind(Name, 1, Avps) of
- {_, V} ->
- V;
- _ ->
- 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) ->
- try
- Dict:'#get-'(Name, Rec)
- catch
- error:_ ->
- undefined
- end.
-
-avp_decode(Dict, Name, #diameter_avp{value = undefined,
- data = Bin}) ->
- Dict:avp(decode, Bin, Name);
-avp_decode(_, _, #diameter_avp{value = V}) ->
- V.
-
-%%% ---------------------------------------------------------------------------
-%%% # pick_peer(App, [DestRealm, DestHost], Filter, #state{})
-%%%
-%%% Output: {TransportPid, #diameter_caps{}, App}
-%%% | false
-%%% | {error, Reason}
-%%% ---------------------------------------------------------------------------
-
-%% Find transports to a given realm/host.
+%% ---------------------------------------------------------------------------
+%% pick_peer/4
+%% ---------------------------------------------------------------------------
pick_peer(#diameter_app{alias = Alias}
= App,
- [_,_] = RH,
+ RealmAndHost,
Filter,
#state{local_peers = L,
shared_peers = S,
service_name = SvcName,
service = #diameter_service{pid = Pid}}) ->
- pick_peer(peers(Alias, RH, Filter, L),
- peers(Alias, RH, Filter, S),
+ pick_peer(peers(Alias, RealmAndHost, Filter, L),
+ peers(Alias, RealmAndHost, Filter, S),
Pid,
SvcName,
App).
@@ -2890,7 +1335,12 @@ pick_peer([], [], _, _, _) ->
%% App state is mutable but we're not in the service process: go there.
pick_peer(Local, Remote, Pid, _SvcName, #diameter_app{mutable = true} = App)
when self() /= Pid ->
- call_service(Pid, {pick_peer, Local, Remote, App});
+ case call_service(Pid, {pick_peer, Local, Remote, App}) of
+ {TPid, _} = T when is_pid(TPid) ->
+ T;
+ {error, _} ->
+ false
+ end;
%% App state isn't mutable or it is and we're in the service process:
%% do the deed.
@@ -2898,19 +1348,18 @@ pick_peer(Local,
Remote,
_Pid,
SvcName,
- #diameter_app{module = ModX,
- alias = Alias,
+ #diameter_app{alias = Alias,
init_state = S,
mutable = M}
= App) ->
- MFA = {ModX, pick_peer, [Local, Remote, SvcName]},
+ Args = [Local, Remote, SvcName],
- try state_cb(App, MFA) of
- {ok, {TPid, #diameter_caps{} = Caps}} when is_pid(TPid) ->
- {TPid, Caps, App};
- {{TPid, #diameter_caps{} = Caps}, ModS} when is_pid(TPid), M ->
+ try state_cb(App, pick_peer, Args) of
+ {ok, {TPid, #diameter_caps{}} = T} when is_pid(TPid) ->
+ T;
+ {{TPid, #diameter_caps{}} = T, ModS} when is_pid(TPid), M ->
mod_state(Alias, ModS),
- {TPid, Caps, App};
+ T;
{false = No, ModS} when M ->
mod_state(Alias, ModS),
No;
@@ -2918,15 +1367,17 @@ pick_peer(Local,
No;
false = No ->
No;
- {{TPid, #diameter_caps{} = Caps}, S} when is_pid(TPid) ->
- {TPid, Caps, App}; %% Accept returned state in the immutable
+ {{TPid, #diameter_caps{}} = T, S} when is_pid(TPid) ->
+ T; %% Accept returned state in the immutable
{false = No, S} -> %% case as long it isn't changed.
No;
T ->
- diameter_lib:error_report({invalid, T, App}, MFA)
+ diameter_lib:error_report({invalid, T, App},
+ {App, pick_peer, Args})
catch
E: Reason ->
- diameter_lib:error_report({failure, {E, Reason, ?STACK}}, MFA)
+ diameter_lib:error_report({failure, {E, Reason, ?STACK}},
+ {App, pick_peer, Args})
end.
%% peers/4
@@ -3013,14 +1464,14 @@ eq(Any, Id, PeerId) ->
%% transports/1
-transports(#state{peerT = PeerT}) ->
- ets:select(PeerT, [{#peer{conn = '$1', _ = '_'},
+transports(#state{watchdogT = WatchdogT}) ->
+ ets:select(WatchdogT, [{#watchdog{peer = '$1', _ = '_'},
[{'is_pid', '$1'}],
['$1']}]).
-%%% ---------------------------------------------------------------------------
-%%% # service_info/2
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # service_info/2
+%% ---------------------------------------------------------------------------
%% The config passed to diameter:start_service/2.
-define(CAP_INFO, ['Origin-Host',
@@ -3068,11 +1519,12 @@ tagged_info(Item, S)
undefined
end;
-tagged_info(TPid, #state{peerT = PT, connT = CT})
+tagged_info(TPid, #state{watchdogT = WatchdogT, peerT = PeerT})
when is_pid(TPid) ->
try
- [#conn{peer = Pid}] = ets:lookup(CT, TPid),
- [#peer{ref = Ref, type = Type, options = Opts}] = ets:lookup(PT, Pid),
+ [#peer{watchdog = Pid}] = ets:lookup(PeerT, TPid),
+ [#watchdog{ref = Ref, type = Type, options = Opts}]
+ = ets:lookup(WatchdogT, Pid),
[{ref, Ref},
{type, Type},
{options, Opts}]
@@ -3155,11 +1607,11 @@ complete(Pre) ->
%% info_stats/1
-info_stats(#state{peerT = PeerT}) ->
- MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'},
+info_stats(#state{watchdogT = WatchdogT}) ->
+ MatchSpec = [{#watchdog{ref = '$1', peer = '$2', _ = '_'},
[{'is_pid', '$2'}],
[['$1', '$2']]}],
- try ets:select(PeerT, MatchSpec) of
+ try ets:select(WatchdogT, MatchSpec) of
L ->
diameter_stats:read(lists:append(L))
catch
@@ -3169,20 +1621,15 @@ info_stats(#state{peerT = PeerT}) ->
%% info_transport/1
%%
%% One entry per configured transport. Statistics for each entry are
-%% the accumulated values for the ref and associated peer pids.
+%% the accumulated values for the ref and associated watchdog/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),
+ Stats = diameter_stats:sum(dict:fetch_keys(PeerD)),
dict:fold(fun(R, Ls, A) ->
- Ps = dict:fetch(R, RefsD),
- [[{ref, R} | transport(Ls)] ++ [stats([R|Ps], Stats)]
- | A]
+ Cs = proplists:get_value(R, Stats, []),
+ [[{ref, R} | transport(Ls)] ++ [{statistics, Cs}] | A]
end,
[],
PeerD).
@@ -3202,43 +1649,42 @@ 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.)
+%% efficient.
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
+peer_dict(#state{watchdogT = WatchdogT, peerT = PeerT}, Dict0) ->
+ try ets:tab2list(WatchdogT) of
L ->
- lists:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, L)
+ lists:foldl(fun(T,A) -> peer_acc(PeerT, 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),
+peer_acc(PeerT, Acc, #watchdog{pid = Pid,
+ type = Type,
+ ref = Ref,
+ options = Opts,
+ state = WS,
+ started = At,
+ peer = TPid}) ->
dict:append(Ref,
[{type, Type},
{options, Opts},
- {watchdog, {Pid, T, WS}}
- | info_conn(ConnT, TPid, WS /= ?WD_DOWN)],
+ {watchdog, {Pid, At, WS}}
+ | info_peer(PeerT, TPid, WS)],
Acc).
-info_conn(ConnT, TPid, true)
- when is_pid(TPid) ->
- try ets:lookup(ConnT, TPid) of
- T -> info_conn(T)
+info_peer(PeerT, TPid, WS)
+ when is_pid(TPid), WS /= ?WD_DOWN ->
+ try ets:lookup(PeerT, TPid) of
+ T -> info_peer(T)
catch
error: badarg -> [] %% service has gone down
end;
-info_conn(_, _, _) ->
+info_peer(_, _, _) ->
[].
%% The point of extracting the config here is so that 'transport' info
@@ -3257,19 +1703,12 @@ config_acc({Ref, T, 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}]) ->
+info_peer([#peer{pid = Pid, apps = SApps, caps = Caps, started = T}]) ->
[{peer, {Pid, T}},
{apps, SApps},
{caps, info_caps(Caps)}
| try [{port, info_port(Pid)}] catch _:_ -> [] end];
-info_conn([] = No) ->
+info_peer([] = No) ->
No.
%% Extract information that the processes involved are expected to
@@ -3303,22 +1742,7 @@ mk_app(#diameter_app{} = A) ->
%% One entry for each outgoing request whose answer is outstanding.
info_pending(#state{} = S) ->
- MatchSpec = [{{'$1',
- #request{transport = '$2',
- from = '$3',
- app = '$4',
- _ = '_'},
- '_'},
- [?ORCOND([{'==', T, '$2'} || T <- transports(S)])],
- [{{'$1', [{{app, '$4'}},
- {{transport, '$2'}},
- {{from, '$3'}}]}}]}],
-
- try
- ets:select(?REQUEST_TABLE, MatchSpec)
- catch
- error: badarg -> [] %% service has gone down
- end.
+ diameter_traffic:pending(transports(S)).
%% info_connections/1
%%
diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl
index 70727d068e..b68d4af11f 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-2012. 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
@@ -28,6 +28,7 @@
-export([reg/2, reg/1,
incr/3, incr/1,
read/1,
+ sum/1,
flush/1]).
%% supervisor callback
@@ -77,10 +78,14 @@
reg(Pid, Ref)
when is_pid(Pid) ->
- call({reg, Pid, Ref}).
+ try
+ call({reg, Pid, Ref})
+ catch
+ exit: _ -> false
+ end.
-spec reg(ref())
- -> true.
+ -> boolean().
reg(Ref) ->
reg(self(), Ref).
@@ -111,11 +116,19 @@ incr(Ctr) ->
%% Retrieve counters for the specified contributors.
%% ---------------------------------------------------------------------------
+%% Read in the server process to ensure that counters for a dying
+%% contributor aren't folded concurrently with select.
+
-spec read([ref()])
-> [{ref(), [{counter(), integer()}]}].
-read(Refs) ->
- read(Refs, false).
+read(Refs)
+ when is_list(Refs) ->
+ try call({read, Refs, false}) of
+ L -> to_refdict(L)
+ catch
+ exit: _ -> []
+ end.
read(Refs, B) ->
MatchSpec = [{{{'_', '$1'}, '_'},
@@ -124,11 +137,52 @@ read(Refs, B) ->
['$_']}],
L = ets:select(?TABLE, MatchSpec),
B andalso delete(L),
+ L.
+
+to_refdict(L) ->
lists:foldl(fun({{C,R}, N}, D) -> orddict:append(R, {C,N}, D) end,
orddict:new(),
L).
%% ---------------------------------------------------------------------------
+%% # sum(Refs)
+%%
+%% Retrieve counters summed over all contributors for each term.
+%% ---------------------------------------------------------------------------
+
+-spec sum([ref()])
+ -> [{ref(), [{counter(), integer()}]}].
+
+sum(Refs)
+ when is_list(Refs) ->
+ try call({read, Refs}) of
+ L -> [{R, to_ctrdict(Cs)} || {R, [_|_] = Cs} <- L]
+ catch
+ exit: _ -> []
+ end.
+
+read_refs(Refs) ->
+ [{R, readr(R)} || R <- Refs].
+
+readr(Ref) ->
+ MatchSpec = [{{{'_', '$1'}, '_'},
+ [?ORCOND([{'=:=', '$1', {const, R}}
+ || R <- [Ref | pids(Ref)]])],
+ ['$_']}],
+ ets:select(?TABLE, MatchSpec).
+
+pids(Ref) ->
+ MatchSpec = [{{'$1', '$2'},
+ [{'=:=', '$2', {const, Ref}}],
+ ['$1']}],
+ ets:select(?TABLE, MatchSpec).
+
+to_ctrdict(L) ->
+ lists:foldl(fun({{C,_}, N}, D) -> orddict:update_counter(C, N, D) end,
+ orddict:new(),
+ L).
+
+%% ---------------------------------------------------------------------------
%% # flush(Refs)
%%
%% Retrieve and delete statistics for the specified contributors.
@@ -136,13 +190,12 @@ read(Refs, B) ->
-spec flush([ref()])
-> [{ref(), {counter(), integer()}}].
-
+
flush(Refs) ->
- try
- call({flush, Refs})
+ try call({read, Refs, true}) of
+ L -> to_refdict(L)
catch
- exit: _ ->
- []
+ exit: _ -> []
end.
%% ===========================================================================
@@ -186,8 +239,14 @@ handle_call({reg, Pid, Ref}, _From, State) ->
B andalso erlang:monitor(process, Pid),
{reply, B, State};
-handle_call({flush, Refs}, _From, State) ->
- {reply, read(Refs, true), State};
+handle_call({read, Refs, Del}, _From, State) ->
+ {reply, read(Refs, Del), State};
+
+handle_call({read, Refs}, _, State) ->
+ {reply, read_refs(Refs), State};
+
+handle_call({flush, Refs}, _From, State) -> %% from old code
+ {reply, to_refdict(read(Refs, true)), State};
handle_call(Req, From, State) ->
?UNEXPECTED([Req, From]),
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
new file mode 100644
index 0000000000..f527f7c754
--- /dev/null
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -0,0 +1,1705 @@
+%%
+%% %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%
+%%
+
+%%
+%% Implements the handling of incoming and outgoing Diameter messages
+%% except CER/CEA, DWR/DWA and DPR/DPA. That is, the messages that a
+%% diameter client sends and receives.
+%%
+
+-module(diameter_traffic).
+
+%% towards diameter
+-export([send_request/4]).
+
+%% towards diameter_watchdog
+-export([receive_message/4]).
+
+%% towards diameter_service
+-export([make_recvdata/1,
+ peer_up/1,
+ peer_down/1,
+ failover/1,
+ pending/1]).
+
+%% towards ?MODULE
+-export([send/1]). %% send from remote node
+
+-include_lib("diameter/include/diameter.hrl").
+-include("diameter_internal.hrl").
+
+-define(RELAY, ?DIAMETER_DICT_RELAY).
+-define(BASE, ?DIAMETER_DICT_COMMON). %% Note: the RFC 3588 dictionary
+
+-define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
+
+%% Table containing outgoing requests for which a reply has yet to be
+%% received.
+-define(REQUEST_TABLE, diameter_request).
+
+%% Workaround for dialyzer's lack of understanding of match specs.
+-type match(T)
+ :: T | '_' | '$1' | '$2' | '$3' | '$4'.
+
+%% Record diameter:call/4 options are parsed into.
+-record(options,
+ {filter = none :: diameter:peer_filter(),
+ extra = [] :: list(),
+ timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
+ detach = false :: boolean()}).
+
+%% Term passed back to receive_message/4 with every incoming message.
+-record(recvdata,
+ {peerT :: ets:tid(),
+ service_name :: diameter:service_name(),
+ apps :: [#diameter_app{}],
+ sequence :: diameter:sequence()}).
+
+%% Record stored in diameter_request for each outgoing request.
+-record(request,
+ {ref :: match(reference()), %% used to receive answer
+ caller :: match(pid()), %% calling process
+ handler :: match(pid()), %% request process
+ transport :: match(pid()), %% peer process
+ caps :: match(#diameter_caps{}), %% of connection
+ packet :: match(#diameter_packet{})}). %% of request
+
+%% ---------------------------------------------------------------------------
+%% # make_recvdata/1
+%% ---------------------------------------------------------------------------
+
+make_recvdata([SvcName, PeerT, Apps, Mask | _]) ->
+ #recvdata{service_name = SvcName,
+ peerT = PeerT,
+ apps = Apps,
+ sequence = Mask}.
+%% Take a list so that the caller (diameter_service) can be upgraded
+%% first if new members are added. Note that receive_message/4 might
+%% still get an old term from any watchdog started in old code.
+
+%% ---------------------------------------------------------------------------
+%% peer_up/1
+%% ---------------------------------------------------------------------------
+
+%% Insert an element that is used to detect whether or not there has
+%% been a failover when inserting an outgoing request.
+peer_up(TPid) ->
+ ets:insert(?REQUEST_TABLE, {TPid}).
+
+%% ---------------------------------------------------------------------------
+%% peer_down/1
+%% ---------------------------------------------------------------------------
+
+peer_down(TPid) ->
+ ets:delete(?REQUEST_TABLE, TPid),
+ failover(TPid).
+
+%% ---------------------------------------------------------------------------
+%% pending/1
+%% ---------------------------------------------------------------------------
+
+pending(TPids) ->
+ MatchSpec = [{{'$1',
+ #request{caller = '$2',
+ handler = '$3',
+ transport = '$4',
+ _ = '_'},
+ '_'},
+ [?ORCOND([{'==', T, '$4'} || T <- TPids])],
+ [{{'$1', [{{caller, '$2'}},
+ {{handler, '$3'}},
+ {{transport, '$4'}}]}}]}],
+
+ try
+ ets:select(?REQUEST_TABLE, MatchSpec)
+ catch
+ error: badarg -> [] %% service has gone down
+ end.
+
+%% ---------------------------------------------------------------------------
+%% # receive_message/4
+%%
+%% Handle an incoming Diameter message.
+%% ---------------------------------------------------------------------------
+
+%% 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, Dict0, RecvData)
+ when is_pid(TPid) ->
+ #diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
+ recv(R,
+ (not R) andalso lookup_request(Pkt, TPid),
+ TPid,
+ Pkt,
+ Dict0,
+ RecvData).
+
+%% Incoming request ...
+recv(true, false, TPid, Pkt, Dict0, RecvData) ->
+ try
+ spawn(fun() -> recv_request(TPid, Pkt, Dict0, RecvData) end)
+ catch
+ error: system_limit = E -> %% discard
+ ?LOG({error, E}, now())
+ end;
+
+%% ... answer to known request ...
+recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) ->
+ Pid ! {answer, Ref, Req, Dict0, Pkt};
+%% Note that failover could have happened prior to this message being
+%% received and triggering failback. That is, both a failover message
+%% and answer may be on their way to the handler process. In the worst
+%% case the request process gets notification of the failover and
+%% sends to the alternate peer before an answer arrives, so it's
+%% always the case that we can receive more than one answer after
+%% failover. The first answer received by the request process wins,
+%% any others are discarded.
+
+%% ... or not.
+recv(false, false, _, _, _, _) ->
+ ok.
+
+%% ---------------------------------------------------------------------------
+%% recv_request/4
+%% ---------------------------------------------------------------------------
+
+recv_request(TPid,
+ #diameter_packet{header = #diameter_header{application_id = Id}}
+ = Pkt,
+ Dict0,
+ #recvdata{peerT = PeerT, apps = Apps}
+ = RecvData) ->
+ send_A(recv_R(diameter_service:find_incoming_app(PeerT, TPid, Id, Apps),
+ TPid,
+ Pkt,
+ Dict0,
+ RecvData),
+ TPid,
+ Dict0,
+ RecvData).
+
+%% recv_R/5
+
+recv_R({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
+ TPid,
+ Pkt0,
+ Dict0,
+ RecvData) ->
+ Pkt = errors(Id, diameter_codec:decode(Id, Dict, Pkt0)),
+ {Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, 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_R(#diameter_caps{}
+ = Caps,
+ _TPid,
+ #diameter_packet{errors = Es}
+ = Pkt,
+ _Dict0,
+ _RecvData) ->
+ {Caps, Pkt#diameter_packet{avps = collect_avps(Pkt),
+ errors = [3007 | Es]}};
+
+recv_R(false = No, _, _, _, _) -> %% transport has gone down
+ No.
+
+collect_avps(Pkt) ->
+ case diameter_codec:collect_avps(Pkt) of
+ {_Bs, As} ->
+ As;
+ As ->
+ As
+ end.
+
+%% recv_R/6
+
+%% Answer errors ourselves ...
+recv_R(#diameter_app{options = [_, {request_errors, E} | _]},
+ _TPid,
+ Dict0,
+ _Caps,
+ _RecvData,
+ #diameter_packet{errors = [RC|_]}) %% a detected 3xxx is hd
+ when E == answer, (Dict0 /= ?BASE orelse 3 == RC div 1000);
+ E == answer_3xxx, 3 == RC div 1000 ->
+ {{answer_message, rc(RC)}, [], []};
+
+%% ... or make a handle_request callback. Note that
+%% Pkt#diameter_packet.msg = undefined in the 3001 case.
+recv_R(App,
+ TPid,
+ _Dict0,
+ Caps,
+ #recvdata{service_name = SvcName},
+ Pkt) ->
+ request_cb(cb(App, handle_request, [Pkt, SvcName, {TPid, Caps}]),
+ App,
+ [],
+ []).
+
+rc({N,_}) ->
+ N;
+rc(N) ->
+ N.
+
+%% errors/1
+%%
+%% Look for additional errors in a decoded message, prepending the
+%% errors field with the first detected error. It's odd/unfortunate
+%% that 501[15] aren't protocol errors. With RFC 3588 this means that
+%% a handle_request callback has to formulate the answer. With RFC
+%% 6733 it's acceptable for 5xxx to be sent in an answer-message.
+
+%% DIAMETER_INVALID_MESSAGE_LENGTH 5015
+%% This error is returned when a request is received with an invalid
+%% message length.
+
+errors(_, #diameter_packet{header = #diameter_header{length = Len},
+ bin = Bin,
+ errors = Es}
+ = Pkt)
+ when Len < 20;
+ 0 /= Len rem 4;
+ 8*Len /= bit_size(Bin) ->
+ Pkt#diameter_packet{errors = [5015 | Es]};
+
+%% DIAMETER_UNSUPPORTED_VERSION 5011
+%% This error is returned when a request was received, whose version
+%% number is unsupported.
+
+errors(_, #diameter_packet{header = #diameter_header{version = V},
+ errors = Es}
+ = Pkt)
+ when V /= ?DIAMETER_VERSION ->
+ Pkt#diameter_packet{errors = [5011 | Es]};
+
+%% 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.
+
+errors(_, #diameter_packet{errors = [Bs | Es]} = Pkt)
+ when is_bitstring(Bs) ->
+ Pkt#diameter_packet{errors = [3009 | Es]};
+
+%% DIAMETER_COMMAND_UNSUPPORTED 3001
+%% The Request contained a Command-Code that the receiver did not
+%% recognize or support. This MUST be used when a Diameter node
+%% receives an experimental command that it does not understand.
+
+errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P},
+ msg = M,
+ errors = Es}
+ = Pkt)
+ when ?APP_ID_RELAY /= Id, undefined == M; %% don't know the command
+ ?APP_ID_RELAY == Id, not P -> %% command isn't proxiable
+ Pkt#diameter_packet{errors = [3001 | Es]};
+
+%% DIAMETER_INVALID_HDR_BITS 3008
+%% 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.
+
+errors(_, #diameter_packet{header = #diameter_header{is_request = true,
+ is_error = true},
+ errors = Es}
+ = Pkt) ->
+ Pkt#diameter_packet{errors = [3008 | Es]};
+
+%% Green.
+errors(_, Pkt) ->
+ Pkt.
+
+%% request_cb/4
+
+%% A reply may be an answer-message, constructed either here or by
+%% the handle_request callback. The header from the incoming request
+%% is passed into the encode so that it can retrieve the relevant
+%% command code in this case. It will also then ignore Dict and use
+%% the base encoder.
+request_cb({reply, _Ans} = T, _App, EvalPktFs, EvalFs) ->
+ {T, EvalPktFs, EvalFs};
+
+%% An 3xxx result code, for which the E-bit is set in the header.
+request_cb({protocol_error, RC}, _App, EvalPktFs, EvalFs)
+ when 3 == RC div 1000 ->
+ {{answer_message, RC}, EvalPktFs, EvalFs};
+
+request_cb({answer_message, RC} = T, _App, EvalPktFs, EvalFs)
+ when 3 == RC div 1000;
+ 5 == RC div 1000 ->
+ {T, EvalPktFs, EvalFs};
+
+%% 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, _App, EvalPktFs, EvalFs) ->
+ {{answer_message, 3001}, EvalPktFs, EvalFs};
+
+%% 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
+%% will be detected by examining Route-Record AVP's, (3) a
+%% Route-Record AVP will be added to the outgoing request and (3) the
+%% End-to-End Identifier will default to that in the
+%% #diameter_header{} without the need for an end_to_end_identifier
+%% option.
+%%
+%% relay and proxy are similar in that they require the same handling
+%% with respect to Route-Record and End-to-End identifier. The
+%% difference is that a proxy advertises specific applications, while
+%% a relay advertises the relay application. If a callback doesn't
+%% want to distinguish between the cases in the callback return value
+%% then 'resend' is a neutral alternative.
+%%
+request_cb({A, Opts}, #diameter_app{id = Id}, EvalPktFs, EvalFs)
+ when A == relay, Id == ?APP_ID_RELAY;
+ A == proxy, Id /= ?APP_ID_RELAY;
+ A == resend ->
+ {{call, Opts}, EvalPktFs, EvalFs};
+
+request_cb(discard = No, _, _, _) ->
+ No;
+
+request_cb({eval_packet, RC, F}, App, Fs, EvalFs) ->
+ request_cb(RC, App, [F|Fs], EvalFs);
+
+request_cb({eval, RC, F}, App, EvalPktFs, Fs) ->
+ request_cb(RC, App, EvalPktFs, [F|Fs]);
+
+request_cb(T, App, _, _) ->
+ ?ERROR({invalid_return, T, handle_request, App}).
+
+%% send_A/4
+
+send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
+ #diameter_packet{errors = [RC|_]} = Pkt,
+ send_A(answer_message(RC, Caps, Dict0, Pkt),
+ TPid,
+ Dict0,
+ Pkt,
+ [],
+ []);
+
+send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
+ send_A(answer(T, Caps, Pkt, App, Dict0, RecvData),
+ TPid,
+ Dict0,
+ Pkt,
+ EvalPktFs,
+ EvalFs);
+
+send_A(_, _, _, _) ->
+ ok.
+
+%% send_A/6
+
+send_A(T, TPid, Dict0, ReqPkt, EvalPktFs, EvalFs) ->
+ reply(T, TPid, Dict0, EvalPktFs, ReqPkt),
+ lists:foreach(fun diameter_lib:eval/1, EvalFs).
+
+%% answer/6
+
+answer({reply, Ans}, _Caps, _Pkt, App, Dict0, _RecvData) ->
+ {dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
+
+answer({call, Opts}, Caps, Pkt, App, Dict0, RecvData) ->
+ #diameter_caps{origin_host = {OH,_}}
+ = Caps,
+ #diameter_packet{avps = Avps}
+ = Pkt,
+ {Code, _Flags, Vid} = Dict0:avp_header('Route-Record'),
+ resend(is_loop(Code, Vid, OH, Dict0, Avps),
+ Opts,
+ Caps,
+ Pkt,
+ App,
+ Dict0,
+ RecvData);
+
+%% RFC 3588 only allows 3xxx errors in an answer-message. RFC 6733
+%% added the possibility of setting 5xxx.
+answer({answer_message, RC} = T, Caps, Pkt, App, Dict0, _RecvData) ->
+ Dict0 /= ?BASE orelse 3 == RC div 1000
+ orelse ?ERROR({invalid_return, T, handle_request, App}),
+ answer_message(RC, Caps, Dict0, Pkt).
+
+%% dict/3
+
+%% An incoming answer, not yet decoded.
+dict(Dict, Dict0, #diameter_packet{header
+ = #diameter_header{is_request = false,
+ is_error = E},
+ msg = undefined}) ->
+ if E -> Dict0; true -> Dict end;
+
+dict(Dict, Dict0, [Msg]) ->
+ dict(Dict, Dict0, Msg);
+
+dict(Dict, Dict0, #diameter_packet{msg = Msg}) ->
+ dict(Dict, Dict0, Msg);
+
+dict(Dict, Dict0, Msg) ->
+ choose(is_answer_message(Msg, Dict0), Dict0, Dict).
+
+is_answer_message([Name | _], _) ->
+ Name == 'answer-message';
+
+is_answer_message(Rec, Dict) ->
+ try
+ 'answer-message' == Dict:rec2msg(element(1,Rec))
+ catch
+ error:_ -> false
+ end.
+
+%% answer_message/4
+
+answer_message(RC,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}},
+ Dict0,
+ #diameter_packet{avps = Avps}
+ = Pkt) ->
+ ?LOG({error, RC}, Pkt),
+ {Dict0, answer_message(OH, OR, RC, Dict0, Avps)}.
+
+%% resend/7
+
+%% DIAMETER_LOOP_DETECTED 3005
+%% An agent detected a loop while trying to get the message to the
+%% intended recipient. The message MAY be sent to an alternate peer,
+%% if one is available, but the peer reporting the error has
+%% identified a configuration problem.
+
+resend(true, _Opts, Caps, Pkt, _App, Dict0, _RecvData) ->
+ answer_message(3005, Caps, Dict0, Pkt);
+
+%% 6.1.8. 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.
+
+resend(false,
+ Opts,
+ #diameter_caps{origin_host = {_,OH}}
+ = Caps,
+ #diameter_packet{header = Hdr0,
+ avps = Avps}
+ = Pkt,
+ App,
+ Dict0,
+ #recvdata{service_name = SvcName,
+ sequence = Mask}) ->
+ Route = #diameter_avp{data = {Dict0, 'Route-Record', OH}},
+ Seq = diameter_session:sequence(Mask),
+ Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
+ Msg = [Hdr, Route | Avps],
+ resend(send_request(SvcName, App, Msg, Opts), Caps, Dict0, 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
+%% handle_answer callback of the application module in question.
+%%
+%% Note that there's nothing stopping the request from being relayed
+%% back to the sender. A pick_peer callback may want to avoid this but
+%% a smart peer might recognize the potential loop and choose another
+%% route. A less smart one will probably just relay the request back
+%% again and force us to detect the loop. A pick_peer that wants to
+%% avoid this can specify filter to avoid the possibility.
+%% Eg. {neg, {host, OH} where #diameter_caps{origin_host = {OH, _}}.
+%%
+%% RFC 6.3 says that a relay agent does not modify Origin-Host but
+%% says nothing about a proxy. Assume it should behave the same way.
+
+%% resend/4
+%%
+%% Relay a reply to a relayed request.
+
+%% Answer from the peer: reset the hop by hop identifier and send.
+resend(#diameter_packet{bin = B}
+ = Pkt,
+ _Caps,
+ _Dict0,
+ #diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
+ transport_data = TD}) ->
+ Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
+ transport_data = TD};
+%% TODO: counters
+
+%% Or not: DIAMETER_UNABLE_TO_DELIVER.
+resend(_, Caps, Dict0, Pkt) ->
+ answer_message(3002, Caps, Dict0, Pkt).
+
+%% is_loop/5
+%%
+%% Is there a Route-Record AVP with our Origin-Host?
+
+is_loop(Code,
+ Vid,
+ Bin,
+ _Dict0,
+ [#diameter_avp{code = Code, vendor_id = Vid, data = Bin} | _]) ->
+ true;
+
+is_loop(_, _, _, _, []) ->
+ false;
+
+is_loop(Code, Vid, OH, Dict0, [_ | Avps])
+ when is_binary(OH) ->
+ is_loop(Code, Vid, OH, Dict0, Avps);
+
+is_loop(Code, Vid, OH, Dict0, Avps) ->
+ is_loop(Code, Vid, Dict0:avp(encode, OH, 'Route-Record'), Dict0, Avps).
+
+%% reply/5
+
+%% Local answer ...
+reply({Dict, Ans}, TPid, Dict0, Fs, ReqPkt) ->
+ reply(Ans, Dict, TPid, Dict0, Fs, ReqPkt);
+
+%% ... or relayed.
+reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) ->
+ eval_packet(Pkt, Fs),
+ send(TPid, Pkt).
+
+%% reply/6
+%%
+%% Send a locally originating reply.
+
+%% Skip the setting of Result-Code and Failed-AVP's below. This is
+%% undocumented and shouldn't be relied on.
+reply([Msg], Dict, TPid, Dict0, Fs, ReqPkt)
+ when is_list(Msg);
+ is_tuple(Msg) ->
+ reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt#diameter_packet{errors = []});
+
+%% No errors or a diameter_header/avp list.
+reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt) ->
+ Pkt = encode(Dict, reset(make_answer_packet(Msg, ReqPkt), Dict), Fs),
+ incr(send, Pkt, Dict, TPid, Dict0), %% count outgoing result codes
+ send(TPid, Pkt).
+
+%% reset/2
+
+%% Header/avps list: send as is.
+reset(#diameter_packet{msg = [#diameter_header{} | _]} = Pkt, _) ->
+ Pkt;
+
+%% No errors to set or errors explicitly ignored.
+reset(#diameter_packet{errors = Es} = Pkt, _)
+ when Es == [];
+ Es == false ->
+ Pkt;
+
+%% Otherwise possibly set Result-Code and/or Failed-AVP.
+reset(#diameter_packet{msg = Msg, errors = Es} = Pkt, Dict) ->
+ Pkt#diameter_packet{msg = reset(Msg, Dict, Es)}.
+
+%% reset/3
+
+reset(Msg, Dict, Es)
+ when is_list(Es) ->
+ {E3, E5, Fs} = partition(Es),
+ FailedAVP = failed_avp(Msg, lists:reverse(Fs), Dict),
+ reset(set(Msg, FailedAVP, Dict),
+ Dict,
+ choose(is_answer_message(Msg, Dict), E3, E5));
+
+reset(Msg, Dict, N)
+ when is_integer(N) ->
+ ResultCode = rc(Msg, {'Result-Code', N}, Dict),
+ set(Msg, ResultCode, Dict);
+
+reset(Msg, _, _) ->
+ Msg.
+
+partition(Es) ->
+ lists:foldl(fun pacc/2, {false, false, []}, Es).
+
+%% Note that the errors list can contain not only integer() and
+%% {integer(), #diameter_avp{}} but also #diameter_avp{}. The latter
+%% isn't something that's returned by decode but can be set in a reply
+%% for encode.
+
+pacc({RC, #diameter_avp{} = A}, {E3, E5, Acc})
+ when is_integer(RC) ->
+ pacc(RC, {E3, E5, [A|Acc]});
+
+pacc(#diameter_avp{} = A, {E3, E5, Acc}) ->
+ {E3, E5, [A|Acc]};
+
+pacc(RC, {false, E5, Acc})
+ when 3 == RC div 1000 ->
+ {RC, E5, Acc};
+
+pacc(RC, {E3, false, Acc})
+ when 5 == RC div 1000 ->
+ {E3, RC, Acc};
+
+pacc(_, Acc) ->
+ Acc.
+
+eval_packet(Pkt, Fs) ->
+ lists:foreach(fun(F) -> diameter_lib:eval([F,Pkt]) end, Fs).
+
+%% make_answer_packet/2
+
+%% Use decode errors to set Result-Code and/or Failed-AVP unless the
+%% the errors field has been explicitly set. Unfortunately, the
+%% default value is the empty list rather than 'undefined' so use the
+%% atom 'false' for "set nothing". (This is historical and changing
+%% the default value would require modules including diameter.hrl to
+%% be recompiled.)
+make_answer_packet(#diameter_packet{errors = []}
+ = Pkt,
+ #diameter_packet{errors = [_|_] = Es}
+ = ReqPkt) ->
+ make_answer_packet(Pkt#diameter_packet{errors = Es}, ReqPkt);
+
+%% 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,
+ errors = Es,
+ 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,
+ errors = Es,
+ transport_data = TD};
+
+%% Binaries and header/avp lists are sent as-is.
+make_answer_packet(Bin, #diameter_packet{transport_data = TD})
+ when is_binary(Bin) ->
+ #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).
+
+%% Reply as name and tuple list ...
+set([_|_] = Ans, Avps, _) ->
+ Ans ++ Avps; %% Values nearer tail take precedence.
+
+%% ... or record.
+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, _) ->
+ No;
+
+failed_avp(Rec, Failed, Dict) ->
+ [fa(Rec, [{'AVP', Failed}], Dict)].
+
+%% Reply as name and tuple list ...
+fa([MsgName | Values], FailedAvp, Dict) ->
+ R = Dict:msg2rec(MsgName),
+ try
+ Dict:'#info-'(R, {index, 'Failed-AVP'}),
+ {'Failed-AVP', [FailedAvp]}
+ catch
+ error: _ ->
+ Avps = proplists:get_value('AVP', Values, []),
+ A = #diameter_avp{name = 'Failed-AVP',
+ value = FailedAvp},
+ {'AVP', [A|Avps]}
+ end;
+
+%% ... or record.
+fa(Rec, FailedAvp, Dict) ->
+ try
+ {'Failed-AVP', [FailedAvp]}
+ catch
+ error: _ ->
+ Avps = Dict:'get-'('AVP', Rec),
+ A = #diameter_avp{name = 'Failed-AVP',
+ value = FailedAvp},
+ {'AVP', [A|Avps]}
+ end.
+
+%% 3. Diameter Header
+%%
+%% E(rror) - If set, the message contains a protocol error,
+%% and the message will not conform to the ABNF
+%% 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.
+
+%% 3.2. Command Code ABNF specification
+%%
+%% 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.
+
+%% 7.1.3. Protocol Errors
+%%
+%% 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 and only these errors MUST
+%% only be used in answer messages whose 'E' bit is set.
+
+%% Thus, only construct answers to protocol errors. Other errors
+%% require an message-specific answer and must be handled by the
+%% application.
+
+%% 6.2. Diameter Answer Processing
+%%
+%% When a request is locally processed, the following procedures MUST be
+%% applied to create the associated answer, in addition to any
+%% additional procedures that MAY be discussed in the Diameter
+%% application defining the command:
+%%
+%% - The same Hop-by-Hop identifier in the request is used in the
+%% answer.
+%%
+%% - The local host's identity is encoded in the Origin-Host AVP.
+%%
+%% - The Destination-Host and Destination-Realm AVPs MUST NOT be
+%% present in the answer message.
+%%
+%% - The Result-Code AVP is added with its value indicating success or
+%% failure.
+%%
+%% - If the Session-Id is present in the request, it MUST be included
+%% in the answer.
+%%
+%% - Any Proxy-Info AVPs in the request MUST be added to the answer
+%% message, in the same order they were present in the request.
+%%
+%% - The 'P' bit is set to the same value as the one in the request.
+%%
+%% - 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
+%% the above processing rules.
+
+%% 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
+%% Error-Message AVP is not intended to be useful in real-time, and
+%% SHOULD NOT be expected to be parsed by network entities.
+
+%% answer_message/5
+
+answer_message(OH, OR, RC, Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Session-Id'),
+ ['answer-message', {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Result-Code', RC}
+ | session_id(Code, Vid, Dict0, Avps)].
+
+session_id(Code, Vid, Dict0, Avps)
+ when is_list(Avps) ->
+ try
+ {value, #diameter_avp{data = D}} = find_avp(Code, Vid, Avps),
+ [{'Session-Id', [Dict0:avp(decode, D, 'Session-Id')]}]
+ catch
+ error: _ ->
+ []
+ end.
+
+%% find_avp/3
+
+find_avp(Code, Vid, Avps)
+ when is_integer(Code), (undefined == Vid orelse is_integer(Vid)) ->
+ find(fun(A) -> is_avp(Code, Vid, A) end, Avps).
+
+%% The final argument here could be a list of AVP's, depending on the case,
+%% but we're only searching at the top level.
+is_avp(Code, Vid, #diameter_avp{code = Code, vendor_id = Vid}) ->
+ true;
+is_avp(_, _, _) ->
+ false.
+
+find(_, []) ->
+ false;
+find(Pred, [H|T]) ->
+ case Pred(H) of
+ true ->
+ {value, H};
+ false ->
+ find(Pred, T)
+ end.
+
+%% 7. Error Handling
+%%
+%% 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:
+%%
+%% - 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 containing the
+%% offending AVP.
+%%
+%% - An AVP that is received with an unrecognized value causes an
+%% answer to be returned with the Result-Code AVP set to
+%% DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
+%% AVP causing the error.
+%%
+%% - A command is received with an AVP that is omitted, yet is
+%% mandatory according to the command's ABNF. The receiver issues an
+%% answer with the Result-Code set to 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.
+%%
+%% 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
+%% specific errors that can be described by this AVP are described in
+%% the following section.
+
+%% 7.5. Failed-AVP AVP
+%%
+%% The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+%% 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.
+%%
+%% 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
+%% occurrences.
+%%
+%% A Diameter message MAY 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
+%% required length for the omitted AVP will be added.
+
+%% incr/4
+%%
+%% Increment a stats counter for result codes in incoming and outgoing
+%% answers.
+
+%% Outgoing message as binary: don't count. (Sending binaries is only
+%% partially supported.)
+incr(_, #diameter_packet{msg = undefined}, _, _, _) ->
+ ok;
+
+%% Incoming with decode errors.
+incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid, _) ->
+ incr(TPid, {diameter_codec:msg_id(H), D, error});
+
+%% Incoming without errors or outgoing. Outgoing with encode errors
+%% never gets here since encode fails.
+incr(Dir, Pkt, Dict, TPid, Dict0) ->
+ #diameter_packet{header = #diameter_header{is_error = E}
+ = Hdr,
+ msg = Rec}
+ = Pkt,
+
+ RC = int(get_avp_value(Dict, 'Result-Code', Rec)),
+
+ %% Exit on an improper Result-Code.
+ is_result(RC, E, Dict0)
+ orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
+
+ irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)).
+
+%% No E-bit: can't be 3xxx.
+is_result(RC, false, _Dict0) ->
+ RC < 3000 orelse 4000 =< RC;
+
+%% E-bit in RFC 3588: only 3xxx.
+is_result(RC, true, ?BASE) ->
+ 3000 =< RC andalso RC < 4000;
+
+%% E-bit in RFC 6733: 3xxx or 5xxx.
+is_result(RC, true, _) ->
+ 3000 =< RC andalso RC < 4000
+ orelse
+ 5000 =< RC andalso RC < 6000.
+
+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).
+
+%% rc_counter/2
+
+%% RFC 3588, 7.6:
+%%
+%% All Diameter answer messages defined in vendor-specific
+%% applications MUST include either one Result-Code AVP or one
+%% Experimental-Result AVP.
+%%
+%% Maintain statistics assuming one or the other, not both, which is
+%% surely the intent of the RFC.
+
+rc_counter(Dict, Rec, undefined) ->
+ rcc(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([{_,_,N} = T | _])
+ when is_integer(N) ->
+ T;
+rcc({_,_,N} = T)
+ when is_integer(N) ->
+ T;
+rcc(_) ->
+ undefined.
+
+%% 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)
+ when is_integer(N) ->
+ N;
+int(_) ->
+ undefined.
+
+-spec x(any(), atom(), list()) -> no_return().
+
+%% Warn and exit request process on errors in an incoming answer.
+x(Reason, F, A) ->
+ diameter_lib:warning_report(Reason, {?MODULE, F, A}),
+ x(Reason).
+
+x(T) ->
+ exit(T).
+
+%% ---------------------------------------------------------------------------
+%% # send_request/4
+%%
+%% Handle an outgoing Diameter request.
+%% ---------------------------------------------------------------------------
+
+send_request(SvcName, AppOrAlias, Msg, Options)
+ when is_list(Options) ->
+ Rec = make_options(Options),
+ Ref = make_ref(),
+ Caller = {self(), Ref},
+ ReqF = fun() ->
+ exit({Ref, send_R(SvcName, AppOrAlias, Msg, Rec, Caller)})
+ end,
+ try spawn_monitor(ReqF) of
+ {_, MRef} ->
+ recv_A(MRef, Ref, Rec#options.detach, false)
+ catch
+ error: system_limit = E ->
+ {error, E}
+ end.
+%% The R in send_R is because Diameter request are usually given short
+%% names of the form XXR. (eg. CER, DWR, etc.) Similarly, answers have
+%% names of the form XXA.
+
+%% Don't rely on gen_server:call/3 for the timeout handling since it
+%% makes no guarantees about not leaving a reply message in the
+%% mailbox if we catch its exit at timeout. It currently *can* do so,
+%% which is also undocumented.
+
+recv_A(MRef, _, true, true) ->
+ erlang:demonitor(MRef, [flush]),
+ ok;
+
+recv_A(MRef, Ref, Detach, Sent) ->
+ receive
+ Ref -> %% send has been attempted
+ recv_A(MRef, Ref, Detach, true);
+ {'DOWN', MRef, process, _, Reason} ->
+ answer_rc(Reason, Ref, Sent)
+ end.
+
+%% send_R/5 has returned ...
+answer_rc({Ref, Ans}, Ref, _) ->
+ Ans;
+
+%% ... or not. Note that failure/encode are documented return values.
+answer_rc(_, _, Sent) ->
+ {error, choose(Sent, failure, encode)}.
+
+%% send_R/5
+%%
+%% In the process spawned for the outgoing request.
+
+send_R(SvcName, AppOrAlias, Msg, Opts, Caller) ->
+ case pick_peer(SvcName, AppOrAlias, Msg, Opts) of
+ {{_,_,_} = Transport, Mask} ->
+ send_request(Transport, Mask, Msg, Opts, Caller, SvcName);
+ false ->
+ {error, no_connection};
+ {error, _} = No ->
+ No
+ end.
+
+%% make_options/1
+
+make_options(Options) ->
+ lists:foldl(fun mo/2, #options{}, Options).
+
+mo({timeout, T}, Rec)
+ when is_integer(T), 0 =< T ->
+ Rec#options{timeout = T};
+
+mo({filter, F}, #options{filter = none} = Rec) ->
+ Rec#options{filter = F};
+mo({filter, F}, #options{filter = {all, Fs}} = Rec) ->
+ Rec#options{filter = {all, [F | Fs]}};
+mo({filter, F}, #options{filter = F0} = Rec) ->
+ Rec#options{filter = {all, [F0, F]}};
+
+mo({extra, L}, #options{extra = X} = Rec)
+ when is_list(L) ->
+ Rec#options{extra = X ++ L};
+
+mo(detach, Rec) ->
+ Rec#options{detach = true};
+
+mo(T, _) ->
+ ?ERROR({invalid_option, T}).
+
+%% ---------------------------------------------------------------------------
+%% # send_request/6
+%% ---------------------------------------------------------------------------
+
+%% Send an outgoing request in its dedicated process.
+%%
+%% Note that both encode of the outgoing request and of the received
+%% answer happens in this process. It's also this process that replies
+%% to the caller. The service process only handles the state-retaining
+%% callbacks.
+%%
+%% The module field of the #diameter_app{} here includes any extra
+%% arguments passed to diameter:call/4.
+
+send_request({TPid, Caps, App}
+ = Transport,
+ Mask,
+ Msg,
+ Opts,
+ Caller,
+ SvcName) ->
+ Pkt = make_prepare_packet(Mask, Msg),
+
+ send_R(cb(App, prepare_request, [Pkt, SvcName, {TPid, Caps}]),
+ Pkt,
+ Transport,
+ Opts,
+ Caller,
+ SvcName,
+ []).
+
+send_R({send, Msg}, Pkt, Transport, Opts, Caller, SvcName, Fs) ->
+ send_R(make_request_packet(Msg, Pkt),
+ Transport,
+ Opts,
+ Caller,
+ SvcName,
+ Fs);
+
+send_R({discard, Reason} , _, _, _, _, _, _) ->
+ {error, Reason};
+
+send_R(discard, _, _, _, _, _, _) ->
+ {error, discarded};
+
+send_R({eval_packet, RC, F}, Pkt, T, Opts, Caller, SvcName, Fs) ->
+ send_R(RC, Pkt, T, Opts, Caller, SvcName, [F|Fs]);
+
+send_R(E, _, {_, _, App}, _, _, _, _) ->
+ ?ERROR({invalid_return, E, prepare_request, App}).
+
+%% 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_prepare_packet(_, Bin)
+ when is_binary(Bin) ->
+ #diameter_packet{header = diameter_codec:decode_header(Bin),
+ bin = Bin};
+
+make_prepare_packet(Mask, #diameter_packet{msg = [#diameter_header{} = Hdr
+ | Avps]}
+ = Pkt) ->
+ Pkt#diameter_packet{msg = [make_prepare_header(Mask, Hdr) | Avps]};
+
+make_prepare_packet(Mask, #diameter_packet{header = Hdr} = Pkt) ->
+ Pkt#diameter_packet{header = make_prepare_header(Mask, Hdr)};
+
+make_prepare_packet(Mask, Msg) ->
+ make_prepare_packet(Mask, #diameter_packet{msg = Msg}).
+
+%% make_prepare_header/2
+
+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_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_prepare_header/1
+
+make_prepare_header(#diameter_header{version = undefined} = Hdr) ->
+ make_prepare_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
+
+make_prepare_header(#diameter_header{} = Hdr) ->
+ Hdr;
+
+make_prepare_header(T) ->
+ ?ERROR({invalid_header, T}).
+
+%% make_request_packet/2
+%%
+%% Reconstruct a diameter_packet from the return value of
+%% prepare_request or prepare_retransmit callback.
+
+make_request_packet(Bin, _)
+ when is_binary(Bin) ->
+ make_prepare_packet(false, Bin);
+
+make_request_packet(#diameter_packet{msg = [#diameter_header{} | _]}
+ = Pkt,
+ _) ->
+ Pkt;
+
+%% Returning a diameter_packet with no header from a prepare_request
+%% or prepare_retransmit callback retains the header passed into it.
+%% 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}) ->
+ Pkt#diameter_packet{header = fold_record(Hdr0, Hdr)};
+
+make_request_packet(Msg, Pkt) ->
+ Pkt#diameter_packet{msg = Msg}.
+
+%% fold_record/2
+
+fold_record(undefined, R) ->
+ R;
+fold_record(Rec, R) ->
+ diameter_lib:fold_tuple(2, Rec, R).
+
+%% send_R/6
+
+send_R(Pkt0,
+ {TPid, Caps, #diameter_app{dictionary = Dict} = App},
+ Opts,
+ {Pid, Ref},
+ SvcName,
+ Fs) ->
+ Pkt = encode(Dict, Pkt0, Fs),
+
+ #options{timeout = Timeout}
+ = Opts,
+
+ Req = #request{ref = Ref,
+ caller = Pid,
+ handler = self(),
+ transport = TPid,
+ caps = Caps,
+ packet = Pkt0},
+
+ try
+ TRef = send_request(TPid, Pkt, Req, SvcName, Timeout),
+ Pid ! Ref, %% tell caller a send has been attempted
+ handle_answer(SvcName,
+ App,
+ recv_A(Timeout, SvcName, App, Opts, {TRef, Req}))
+ after
+ erase_requests(Pkt)
+ end.
+
+%% recv_A/5
+
+recv_A(Timeout, SvcName, App, Opts, {TRef, #request{ref = Ref} = Req}) ->
+ %% Matching on TRef below ensures we ignore messages that pertain
+ %% to a previous transport prior to failover. The answer message
+ %% includes the #request{} since it's not necessarily Req; that
+ %% is, from the last peer to which we've transmitted.
+ receive
+ {answer = A, Ref, Rq, Dict0, Pkt} -> %% Answer from peer
+ {A, Rq, Dict0, Pkt};
+ {timeout = Reason, TRef, _} -> %% No timely reply
+ {error, Req, Reason};
+ {failover, TRef} -> %% Service says peer has gone down
+ retransmit(pick_peer(SvcName, App, Req, Opts),
+ Req,
+ Opts,
+ SvcName,
+ Timeout)
+ end.
+
+%% handle_answer/3
+
+handle_answer(SvcName, App, {error, Req, Reason}) ->
+ handle_error(App, Req, Reason, SvcName);
+
+handle_answer(SvcName,
+ #diameter_app{dictionary = Dict,
+ id = Id}
+ = App,
+ {answer, Req, Dict0, Pkt}) ->
+ Mod = dict(Dict, Dict0, Pkt),
+ handle_A(errors(Id, diameter_codec:decode(Mod, Pkt)),
+ SvcName,
+ Mod,
+ Dict0,
+ App,
+ 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?
+
+handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
+ try
+ incr(recv, Pkt, Dict, TPid, Dict0) %% count incoming result codes
+ of
+ _ -> answer(Pkt, SvcName, App, Req)
+ catch
+ exit: {invalid_error_bit, RC} ->
+ #diameter_packet{errors = Es}
+ = Pkt,
+ E = {5004, #diameter_avp{name = 'Result-Code', value = RC}},
+ answer(Pkt#diameter_packet{errors = [E|Es]}, SvcName, App, Req)
+ end.
+
+answer(Pkt,
+ SvcName,
+ #diameter_app{module = ModX,
+ options = [{answer_errors, AE} | _]},
+ Req) ->
+ a(Pkt, SvcName, ModX, AE, Req).
+
+a(#diameter_packet{errors = Es}
+ = Pkt,
+ SvcName,
+ ModX,
+ AE,
+ #request{transport = TPid,
+ caps = Caps,
+ packet = P})
+ when [] == Es;
+ callback == AE ->
+ cb(ModX, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]);
+
+a(Pkt, SvcName, _, report, Req) ->
+ x(errors, handle_answer, [SvcName, Req, Pkt]);
+
+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
+%% header is what we expect. (TODO: Does the rfc says anything about
+%% this?)
+
+%% Note that failover starts a new timer and that expiry of an old
+%% timer value is ignored. This means that an answer could be accepted
+%% from a peer after timeout in the case of failover.
+
+retransmit({{_,_,App} = Transport, _Mask}, Req, Opts, SvcName, Timeout) ->
+ try retransmit(Transport, Req, SvcName, Timeout) of
+ T -> recv_A(Timeout, SvcName, App, Opts, T)
+ catch
+ ?FAILURE(Reason) -> {error, Req, Reason}
+ end;
+
+retransmit(_, Req, _, _, _) -> %% no alternate peer
+ {error, Req, failover}.
+
+%% pick_peer/4
+
+%% Retransmission after failover: call-specific arguments have already
+%% been appended in App.
+pick_peer(SvcName,
+ App,
+ #request{packet = #diameter_packet{msg = Msg}},
+ Opts) ->
+ pick_peer(SvcName, App, Msg, Opts#options{extra = []});
+
+pick_peer(_, _, undefined, _) ->
+ false;
+
+pick_peer(SvcName,
+ AppOrAlias,
+ Msg,
+ #options{filter = Filter, extra = Xtra}) ->
+ diameter_service:pick_peer(SvcName,
+ AppOrAlias,
+ {fun(D) -> get_destination(D, Msg) end,
+ Filter,
+ Xtra}).
+
+%% handle_error/4
+
+handle_error(App,
+ #request{packet = Pkt,
+ transport = TPid,
+ caps = Caps},
+ Reason,
+ SvcName) ->
+ cb(App, handle_error, [Reason, msg(Pkt), SvcName, {TPid, Caps}]).
+
+msg(#diameter_packet{msg = undefined, bin = 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 a
+%% header or transport_data. Even allow the returned record to contain
+%% an encoded binary. This isn't the usual case and doesn't properly
+%% support retransmission but is useful for test.
+
+%% A message to be encoded.
+encode(Dict, #diameter_packet{bin = undefined} = Pkt) ->
+ diameter_codec:encode(Dict, Pkt);
+
+%% An encoded binary: just send.
+encode(_, #diameter_packet{} = Pkt) ->
+ Pkt.
+
+%% send_request/5
+
+send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, _SvcName, Timeout)
+ when node() == node(TPid) ->
+ %% Store the outgoing request before sending to avoid a race with
+ %% reply reception.
+ TRef = store_request(TPid, Bin, Req, Timeout),
+ send(TPid, Pkt),
+ TRef;
+
+%% Send using a remote transport: spawn a process on the remote node
+%% to relay the answer.
+send_request(TPid, #diameter_packet{} = Pkt, Req, SvcName, Timeout) ->
+ TRef = erlang:start_timer(Timeout, self(), TPid),
+ T = {TPid, Pkt, Req, SvcName, Timeout, TRef},
+ spawn(node(TPid), ?MODULE, send, [T]),
+ TRef.
+
+%% send/1
+
+send({TPid, Pkt, #request{handler = Pid} = Req, SvcName, Timeout, TRef}) ->
+ Ref = send_request(TPid,
+ Pkt,
+ Req#request{handler = self()},
+ SvcName,
+ Timeout),
+ Pid ! reref(receive T -> T end, Ref, TRef).
+
+reref({T, Ref, R}, Ref, TRef) ->
+ {T, TRef, R};
+reref(T, _, _) ->
+ T.
+
+%% send/2
+
+send(Pid, Pkt) ->
+ Pid ! {send, Pkt}.
+
+%% retransmit/4
+
+retransmit({TPid, Caps, App}
+ = Transport,
+ #request{packet = Pkt0}
+ = Req,
+ SvcName,
+ Timeout) ->
+ have_request(Pkt0, TPid) %% Don't failover to a peer we've
+ andalso ?THROW(timeout), %% already sent to.
+
+ #diameter_packet{header = Hdr0} = Pkt0,
+ Hdr = Hdr0#diameter_header{is_retransmitted = true},
+ Pkt = Pkt0#diameter_packet{header = Hdr},
+
+ retransmit(cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]),
+ Transport,
+ Req#request{packet = Pkt},
+ SvcName,
+ Timeout,
+ []).
+
+retransmit({send, Msg},
+ Transport,
+ #request{packet = Pkt}
+ = Req,
+ SvcName,
+ Timeout,
+ Fs) ->
+ resend_request(make_request_packet(Msg, Pkt),
+ Transport,
+ Req,
+ SvcName,
+ Timeout,
+ Fs);
+
+retransmit({discard, Reason}, _, _, _, _, _) ->
+ ?THROW(Reason);
+
+retransmit(discard, _, _, _, _, _) ->
+ ?THROW(discarded);
+
+retransmit({eval_packet, RC, F}, Transport, Req, SvcName, Timeout, Fs) ->
+ retransmit(RC, Transport, Req, SvcName, Timeout, [F|Fs]);
+
+retransmit(T, {_, _, App}, _, _, _, _) ->
+ ?ERROR({invalid_return, T, prepare_retransmit, App}).
+
+resend_request(Pkt0,
+ {TPid, Caps, #diameter_app{dictionary = Dict}},
+ Req0,
+ SvcName,
+ Tmo,
+ Fs) ->
+ Pkt = encode(Dict, Pkt0, Fs),
+
+ Req = Req0#request{transport = TPid,
+ packet = Pkt0,
+ caps = Caps},
+
+ ?LOG(retransmission, Req),
+ TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
+ {TRef, Req}.
+
+%% store_request/4
+
+store_request(TPid, Bin, Req, Timeout) ->
+ Seqs = diameter_codec:sequence_numbers(Bin),
+ TRef = erlang:start_timer(Timeout, self(), timeout),
+ ets:insert(?REQUEST_TABLE, {Seqs, Req, TRef}),
+ ets:member(?REQUEST_TABLE, TPid)
+ orelse (self() ! {failover, TRef}), %% failover/1 may have missed
+ TRef.
+
+%% lookup_request/2
+
+lookup_request(Msg, TPid) ->
+ Seqs = diameter_codec:sequence_numbers(Msg),
+ Spec = [{{Seqs, #request{transport = TPid, _ = '_'}, '_'},
+ [],
+ ['$_']}],
+ case ets:select(?REQUEST_TABLE, Spec) of
+ [{_, Req, _}] ->
+ Req;
+ [] ->
+ false
+ end.
+
+%% erase_requests/1
+
+erase_requests(Pkt) ->
+ ets:delete(?REQUEST_TABLE, diameter_codec:sequence_numbers(Pkt)).
+
+%% match_requests/1
+
+match_requests(TPid) ->
+ Pat = {'_', #request{transport = TPid, _ = '_'}, '_'},
+ ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]).
+
+%% have_request/2
+
+have_request(Pkt, TPid) ->
+ Seqs = diameter_codec:sequence_numbers(Pkt),
+ Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'},
+ '$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1).
+
+%% ---------------------------------------------------------------------------
+%% # failover/1-2
+%% ---------------------------------------------------------------------------
+
+failover(TPid)
+ when is_pid(TPid) ->
+ lists:foreach(fun failover/1, match_requests(TPid));
+%% Note that a request process can store its request after failover
+%% notifications are sent here: store_request/4 sends the notification
+%% in that case.
+
+%% Failover as a consequence of request_peer_down/1: inform the
+%% request process.
+failover({_, Req, TRef}) ->
+ #request{handler = Pid,
+ packet = #diameter_packet{msg = M}}
+ = Req,
+ M /= undefined andalso (Pid ! {failover, TRef}).
+%% Failover is not performed when msg = binary() since sending
+%% pre-encoded binaries is only partially supported. (Mostly for
+%% test.)
+
+%% get_destination/2
+
+get_destination(Dict, Msg) ->
+ [str(get_avp_value(Dict, D, Msg)) || D <- ['Destination-Realm',
+ 'Destination-Host']].
+
+%% 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
+%% than the list of no values we treat it as by mapping to undefined.
+%% This behaviour is documented.
+str([]) ->
+ undefined;
+str(T) ->
+ T.
+
+%% get_avp_value/3
+%%
+%% Find an AVP in a message of one of three forms:
+%%
+%% - a message record (as generated from a .dia spec) or
+%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
+%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
+%%
+%% In the first two forms a dictionary module is used at encode to
+%% identify the type of the AVP and its arity in the message in
+%% 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),
+ [A|_] = lists:dropwhile(fun(#diameter_avp{code = C, vendor_id = V}) ->
+ C /= Code orelse V /= VId
+ end,
+ Avps),
+ avp_decode(Dict, Name, A)
+ catch
+ error: _ ->
+ undefined
+ end;
+
+%% Outgoing message as a name/values list.
+get_avp_value(_, Name, [_MsgName | Avps]) ->
+ case lists:keyfind(Name, 1, Avps) of
+ {_, V} ->
+ V;
+ _ ->
+ undefined
+ end;
+
+%% Message is typically a record but not necessarily.
+get_avp_value(Dict, Name, Rec) ->
+ try
+ Dict:'#get-'(Name, Rec)
+ catch
+ error:_ ->
+ undefined
+ end.
+
+avp_decode(Dict, Name, #diameter_avp{value = undefined,
+ data = Bin}) ->
+ Dict:avp(decode, Bin, Name);
+avp_decode(_, _, #diameter_avp{value = V}) ->
+ V.
+
+cb(#diameter_app{module = [_|_] = M}, F, A) ->
+ eval(M, F, A);
+cb([_|_] = M, F, A) ->
+ eval(M, F, A).
+
+eval([M|X], F, A) ->
+ apply(M, F, A ++ X).
+
+choose(true, X, _) -> X;
+choose(false, _, X) -> X.
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index 243ad0a986..82ca603cf3 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-2012. 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
@@ -45,6 +45,16 @@
-define(DEFAULT_TW_INIT, 30000). %% RFC 3539 ch 3.4.1
-define(NOMASK, {0,32}). %% default sequence mask
+-define(BASE, ?DIAMETER_DICT_COMMON).
+
+-define(IS_NATURAL(N), (is_integer(N) andalso 0 =< N)).
+
+-define(CHOOSE(B,T,F), if (B) -> T; true -> F end).
+
+-record(config,
+ {suspect = 1 :: non_neg_integer(), %% OKAY -> SUSPECT
+ okay = 3 :: non_neg_integer()}). %% REOPEN -> OKAY
+
-record(watchdog,
{%% PCB - Peer Control Block; see RFC 3539, Appendix A
status = initial :: initial | okay | suspect | down | reopen,
@@ -52,36 +62,42 @@
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
+ %% number of DWAs received in reopen,
+ %% or number of timeouts before okay -> suspect
%% end PCB
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
+ tref :: reference(), %% reference for current watchdog timer
+ dictionary :: module(), %% common dictionary
+ receive_data :: term(),
+ %% term passed into diameter_service with incoming message
sequence :: diameter:sequence(), %% mask
restrict :: {diameter:restriction(), boolean()},
- shutdown = false :: boolean()}).
+ shutdown = false :: boolean(),
+ config :: #config{}}).
+%% ---------------------------------------------------------------------------
%% start/2
%%
%% Start a monitor before the watchdog is allowed to proceed to ensure
%% that a failed capabilities exchange produces the desired exit
%% reason.
+%% ---------------------------------------------------------------------------
--spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}})
+-spec start(Type, {RecvData, [Opt], SvcOpts, #diameter_service{}})
-> {reference(), pid()}
when Type :: {connect|accept, diameter:transport_ref()},
RecvData :: term(),
Opt :: diameter:transport_opt(),
- SvcName :: diameter:service_name().
+ SvcOpts :: [diameter:service_opt()].
start({_,_} = Type, T) ->
- Ref = make_ref(),
- {ok, Pid} = diameter_watchdog_sup:start_child({Ref, {Type, self(), T}}),
+ Ack = make_ref(),
+ {ok, Pid} = diameter_watchdog_sup:start_child({Ack, Type, self(), T}),
try
{erlang:monitor(process, Pid), Pid}
after
- Pid ! Ref
+ send(Pid, Ack)
end.
start_link(T) ->
@@ -100,45 +116,116 @@ init(T) ->
proc_lib:init_ack({ok, self()}),
gen_server:enter_loop(?MODULE, [], i(T)).
-i({Ref, {_, Pid, _} = T}) ->
- MRef = erlang:monitor(process, Pid),
- receive
- Ref ->
- make_state(T);
- {'DOWN', MRef, process, _, _} = D ->
- exit({shutdown, D})
- end;
-
-i({_, Pid, _} = T) -> %% from old code
+i({Ack, T, Pid, {RecvData,
+ Opts,
+ SvcOpts,
+ #diameter_service{applications = Apps,
+ capabilities = Caps}
+ = Svc}}) ->
erlang:monitor(process, Pid),
- make_state(T).
-
-make_state({T, Pid, {RecvData,
- Opts,
- SvcName,
- #diameter_service{applications = Apps,
- capabilities = Caps}
- = Svc}}) ->
+ wait(Ack, Pid),
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),
+ {_,_} = Mask = proplists:get_value(sequence, SvcOpts),
+ Restrict = proplists:get_value(restrict_connections, SvcOpts),
Nodes = restrict_nodes(Restrict),
+ Dict0 = common_dictionary(Apps),
#watchdog{parent = Pid,
- transport = monitor(diameter_peer_fsm:start(T,
- Opts,
- {Mask, Nodes, Svc})),
+ transport = start(T, Opts, Mask, Nodes, Dict0, Svc),
tw = proplists:get_value(watchdog_timer,
Opts,
?DEFAULT_TW_INIT),
- message_data = {RecvData, SvcName, Apps, Mask},
+ receive_data = RecvData,
+ dictionary = Dict0,
sequence = Mask,
- restrict = {Restrict, lists:member(node(), Nodes)}}.
+ restrict = {Restrict, lists:member(node(), Nodes)},
+ config = config(Opts)}.
+
+wait(Ref, Pid) ->
+ receive
+ Ref ->
+ ok;
+ {'DOWN', _, process, Pid, _} = D ->
+ exit({shutdown, D})
+ end.
+
+%% config/1
+%%
+%% Could also configure counts for SUSPECT to DOWN and REOPEN to DOWN,
+%% but don't.
+
+config(Opts) ->
+ Config = proplists:get_value(watchdog_config, Opts, []),
+ is_list(Config) orelse config_error({watchdog_config, Config}),
+ lists:foldl(fun config/2, #config{}, Config).
-%% 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.
+config({suspect, N}, Rec)
+ when ?IS_NATURAL(N) ->
+ Rec#config{suspect = N};
+
+config({okay, N}, Rec)
+ when ?IS_NATURAL(N) ->
+ Rec#config{okay = N};
+
+config(T, _) ->
+ config_error(T).
+
+%% start/5
+
+start(T, Opts, Mask, Nodes, Dict0, Svc) ->
+ {_MRef, Pid}
+ = diameter_peer_fsm:start(T, Opts, {Mask, Nodes, Dict0, Svc}),
+ Pid.
+
+%% common_dictionary/1
+%%
+%% Determine the dictionary of the Diameter common application with
+%% Application Id 0. Fail on config errors.
+
+common_dictionary(Apps) ->
+ case
+ orddict:fold(fun dict0/3,
+ false,
+ lists:foldl(fun(#diameter_app{dictionary = M}, D) ->
+ orddict:append(M:id(), M, D)
+ end,
+ orddict:new(),
+ Apps))
+ of
+ {value, Mod} ->
+ Mod;
+ false ->
+ %% A transport should configure a common dictionary but
+ %% don't require it. Not configuring a common dictionary
+ %% means a user won't be able either send of receive
+ %% messages in the common dictionary: incoming request
+ %% will be answered with 3007 and outgoing requests cannot
+ %% be sent. The dictionary returned here is oly used for
+ %% messages diameter sends and receives: CER/CEA, DPR/DPA
+ %% and DWR/DWA.
+ ?BASE
+ end.
+
+%% Each application should be represented by a single dictionary.
+dict0(Id, [_,_|_] = Ms, _) ->
+ config_error({multiple_dictionaries, Ms, {application_id, Id}});
+
+%% An explicit common dictionary.
+dict0(?APP_ID_COMMON, [Mod], _) ->
+ {value, Mod};
+
+%% A pure relay, in which case the common application is implicit.
+%% This uses the fact that the common application will already have
+%% been folded.
+dict0(?APP_ID_RELAY, _, false) ->
+ {value, ?BASE};
+
+dict0(_, _, Acc) ->
+ Acc.
+
+config_error(T) ->
+ ?ERROR({configuration_error, T}).
%% handle_call/3
@@ -157,41 +244,70 @@ handle_info(T, #watchdog{} = State) ->
ok ->
{noreply, State};
#watchdog{} = S ->
- event(State, S),
+ close(T, State), %% service expects 'close' message
+ event(T, State, S), %% before 'watchdog'
{noreply, S};
stop ->
?LOG(stop, T),
- event(State, State#watchdog{status = down}),
+ event(T, State, State#watchdog{status = down}),
{stop, {shutdown, T}, State}
end;
-handle_info(T, S) ->
- handle_info(T, upgrade(S)).
+handle_info(T, State) -> %% started in old code
+ handle_info(T, upgrade(State)).
-upgrade(S) ->
- #watchdog{} = list_to_tuple(tuple_to_list(S)
- ++ [?NOMASK, {nodes, true}, false]).
+upgrade(State) ->
+ case erlang:append_element(State, #config{}) of
+ #watchdog{status = okay, config = #config{suspect = OS}} = S ->
+ S#watchdog{num_dwa = OS};
+ #watchdog{} = S ->
+ S
+ end.
-event(#watchdog{status = T}, #watchdog{status = T}) ->
+close({'DOWN', _, process, TPid, {shutdown, Reason}},
+ #watchdog{transport = TPid,
+ parent = Pid}) ->
+ send(Pid, {close, self(), Reason});
+
+close(_, _) ->
+ ok.
+
+event(_, #watchdog{status = T}, #watchdog{status = T}) ->
ok;
-event(#watchdog{transport = undefined}, #watchdog{transport = undefined}) ->
+event(_, #watchdog{transport = undefined}, #watchdog{transport = undefined}) ->
ok;
-event(#watchdog{status = From, transport = F, parent = Pid},
+event(Msg,
+ #watchdog{status = From, transport = F, parent = Pid},
#watchdog{status = To, transport = T}) ->
- E = {tpid(F,T), From, To},
- notify(Pid, E),
+ TPid = tpid(F,T),
+ E = {[TPid | data(Msg, TPid, From, To)], From, To},
+ send(Pid, {watchdog, self(), E}),
?LOG(transition, {self(), E}).
+data(Msg, TPid, reopen, okay) ->
+ {recv, TPid, 'DWA', _Pkt} = Msg, %% assert
+ {TPid, T} = eraser(open),
+ [T];
+
+data({open, TPid, _Hosts, T}, TPid, _From, To)
+ when To == okay;
+ To == reopen ->
+ [T];
+
+data(_, _, _, _) ->
+ [].
+
tpid(_, Pid)
when is_pid(Pid) ->
Pid;
+
tpid(Pid, _) ->
Pid.
-notify(Pid, E) ->
- Pid ! {watchdog, self(), E}.
+send(Pid, T) ->
+ Pid ! T.
%% terminate/2
@@ -221,15 +337,13 @@ transition(close, #watchdog{}) ->
ok;
%% Service is asking for the peer to be taken down gracefully.
-transition({shutdown, Pid}, #watchdog{parent = Pid,
- transport = undefined,
- status = S}) ->
- down = S, %% sanity check
+transition({shutdown, Pid, _}, #watchdog{parent = Pid,
+ transport = undefined}) ->
stop;
-transition({shutdown = T, Pid}, #watchdog{parent = Pid,
- transport = TPid}
- = S) ->
- TPid ! {T, self()},
+transition({shutdown = T, Pid, Reason}, #watchdog{parent = Pid,
+ transport = TPid}
+ = S) ->
+ send(TPid, {T, self(), Reason}),
S#watchdog{shutdown = true};
%% Parent process has died,
@@ -240,13 +354,9 @@ transition({'DOWN', _, process, Pid, _Reason},
%% Transport has accepted a connection.
transition({accepted = T, TPid}, #watchdog{transport = TPid,
parent = Pid}) ->
- Pid ! {T, self(), TPid},
+ send(Pid, {T, self(), TPid}),
ok;
-%% Transport is telling us that its impending death isn't failure.
-transition({close, TPid, _Reason}, #watchdog{transport = TPid}) ->
- stop;
-
%% STATE Event Actions New State
%% ===== ------ ------- ----------
%% INITIAL Connection up SetWatchdog() OKAY
@@ -261,16 +371,16 @@ transition({close, TPid, _Reason}, #watchdog{transport = TPid}) ->
%% know the identity of the peer (ie. now) that we know that we're in
%% state down rather than initial.
-transition({open, TPid, Hosts, T} = Open,
+transition({open, TPid, Hosts, _} = Open,
#watchdog{transport = TPid,
status = initial,
- parent = Pid,
- restrict = {_, R}}
+ restrict = {_,R},
+ config = #config{suspect = OS}}
= S) ->
case okay(getr(restart), Hosts, R) of
okay ->
- open(Pid, {TPid, T}),
- set_watchdog(S#watchdog{status = okay});
+ set_watchdog(S#watchdog{status = okay,
+ num_dwa = OS});
reopen ->
transition(Open, S#watchdog{status = down})
end;
@@ -280,19 +390,24 @@ transition({open, TPid, Hosts, T} = Open,
%% SetWatchdog()
%% Pending = TRUE REOPEN
-transition({open = P, TPid, _Hosts, T},
+transition({open = Key, TPid, _Hosts, T},
#watchdog{transport = TPid,
- parent = Pid,
- status = down}
+ status = down,
+ config = #config{suspect = OS,
+ okay = RO}}
= 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). 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}));
+ case RO of
+ 0 -> %% non-standard: skip REOPEN
+ set_watchdog(S#watchdog{status = okay,
+ num_dwa = OS});
+ _ ->
+ %% 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).
+ putr(Key, {TPid, T}),
+ set_watchdog(send_watchdog(S#watchdog{status = reopen,
+ num_dwa = 0}))
+ end;
%% OKAY Connection down CloseConnection()
%% Failover()
@@ -302,26 +417,18 @@ transition({open = P, TPid, _Hosts, T},
%% REOPEN Connection down CloseConnection()
%% SetWatchdog() DOWN
-transition({'DOWN', _, process, TPid, _},
+transition({'DOWN', _, process, TPid, _Reason},
#watchdog{transport = TPid,
- status = S,
- shutdown = D})
- when S == initial;
- D ->
+ shutdown = true}) ->
stop;
-transition({'DOWN', _, process, TPid, _},
- #watchdog{transport = TPid}
+transition({'DOWN', _, process, TPid, _Reason},
+ #watchdog{transport = TPid,
+ status = T}
= S) ->
- failover(S),
- close(S),
- set_watchdog(S#watchdog{status = down,
+ set_watchdog(S#watchdog{status = ?CHOOSE(initial == T, T, down),
pending = false,
transport = undefined});
-%% Any outstanding pending (or other messages from the transport) will
-%% have arrived before 'DOWN' since the message comes from the same
-%% process. Note that we could also get this message in the initial
-%% state.
%% Incoming message.
transition({recv, TPid, Name, Pkt}, #watchdog{transport = TPid} = S) ->
@@ -337,24 +444,11 @@ transition({timeout, _, tw}, #watchdog{}) ->
%% State query.
transition({state, Pid}, #watchdog{status = S}) ->
- Pid ! {self(), S},
+ send(Pid, {self(), S}),
ok.
%% ===========================================================================
-%% 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.
-
putr(Key, Val) ->
put({?MODULE, Key}, Val).
@@ -364,16 +458,16 @@ getr(Key) ->
eraser(Key) ->
erase({?MODULE, Key}).
-%% encode/2
+%% encode/3
-encode(Msg, Mask) ->
+encode(Msg, Mask, Dict) ->
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),
+ #diameter_packet{bin = Bin} = diameter_codec:encode(Dict, Pkt),
Bin.
%% okay/3
@@ -401,7 +495,7 @@ okay([{_,P}]) ->
%% ... or it has.
okay(C) ->
- [_|_] = [P ! close || {_,P} <- C, self() /= P],
+ [_|_] = [send(P, close) || {_,P} <- C, self() /= P],
reopen.
%% set_watchdog/1
@@ -423,36 +517,14 @@ tw(T)
tw({M,F,A}) ->
apply(M,F,A).
-%% open/2
-
-open(Pid, {_,_} = T) ->
- Pid ! {connection_up, self(), T}.
-
-%% failover/1
-
-failover(#watchdog{status = okay,
- parent = Pid}) ->
- Pid ! {connection_down, self()};
-
-failover(_) ->
- ok.
-
-%% close/1
-
-close(#watchdog{status = down}) ->
- ok;
-
-close(#watchdog{parent = Pid}) ->
- {{T, _}, _, _} = getr(restart),
- T == accept andalso (Pid ! {close, self()}).
-
%% send_watchdog/1
send_watchdog(#watchdog{pending = false,
transport = TPid,
+ dictionary = Dict0,
sequence = Mask}
= S) ->
- TPid ! {send, encode(getr(dwr), Mask)},
+ send(TPid, {send, encode(getr(dwr), Mask, Dict0)}),
?LOG(send, 'DWR'),
S#watchdog{pending = true}.
@@ -480,8 +552,9 @@ rcv(N, _, _)
false;
rcv(_, Pkt, #watchdog{transport = TPid,
- message_data = T}) ->
- diameter_service:receive_message(TPid, Pkt, T).
+ dictionary = Dict0,
+ receive_data = T}) ->
+ diameter_traffic:receive_message(TPid, Pkt, Dict0, T).
throwaway(S) ->
throw({?MODULE, throwaway, S}).
@@ -532,26 +605,27 @@ rcv(_, #watchdog{status = okay} = S) ->
%% SUSPECT Receive non-DWA Failback()
%% SetWatchdog() OKAY
-rcv('DWA', #watchdog{status = suspect} = S) ->
- failback(S),
+rcv('DWA', #watchdog{status = suspect, config = #config{suspect = OS}} = S) ->
set_watchdog(S#watchdog{status = okay,
+ num_dwa = OS,
pending = false});
-rcv(_, #watchdog{status = suspect} = S) ->
- failback(S),
- set_watchdog(S#watchdog{status = okay});
+rcv(_, #watchdog{status = suspect, config = #config{suspect = OS}} = S) ->
+ set_watchdog(S#watchdog{status = okay,
+ num_dwa = OS});
%% REOPEN Receive DWA & Pending = FALSE
%% NumDWA == 2 NumDWA++
%% Failback() OKAY
rcv('DWA', #watchdog{status = reopen,
- num_dwa = 2 = N,
- parent = Pid}
- = S) ->
- open(Pid, eraser(open)),
+ num_dwa = N,
+ config = #config{suspect = OS,
+ okay = RO}}
+ = S)
+ when N+1 == RO ->
S#watchdog{status = okay,
- num_dwa = N+1,
+ num_dwa = OS,
pending = false};
%% REOPEN Receive DWA & Pending = FALSE
@@ -568,11 +642,6 @@ rcv('DWA', #watchdog{status = reopen,
rcv(_, #watchdog{status = reopen} = S) ->
throwaway(S).
-%% failback/1
-
-failback(#watchdog{parent = Pid}) ->
- Pid ! {connection_up, self()}.
-
%% timeout/1
%%
%% The caller sets the watchdog on the return value.
@@ -595,10 +664,17 @@ timeout(#watchdog{status = T,
%% Pending SetWatchdog() SUSPECT
timeout(#watchdog{status = okay,
- pending = true}
- = S) ->
- failover(S),
- S#watchdog{status = suspect};
+ pending = true,
+ num_dwa = N}
+ = S) ->
+ case N of
+ 1 ->
+ S#watchdog{status = suspect};
+ 0 -> %% non-standard: never move to suspect
+ S;
+ N -> %% non-standard: more timeouts before moving
+ S#watchdog{num_dwa = N-1}
+ end;
%% SUSPECT Timer expires CloseConnection()
%% SetWatchdog() DOWN
@@ -614,7 +690,6 @@ timeout(#watchdog{status = T,
when T == suspect;
T == reopen, P, N < 0 ->
exit(TPid, {shutdown, watchdog_timeout}),
- close(S),
S#watchdog{status = down};
%% REOPEN Timer expires & NumDWA = -1
@@ -648,7 +723,9 @@ timeout(#watchdog{status = reopen,
%% process has died. We only need to handle state down since we start
%% the first watchdog when transitioning out of initial.
-timeout(#watchdog{status = down} = S) ->
+timeout(#watchdog{status = T} = S)
+ when T == initial;
+ T == down ->
restart(S).
%% restart/1
@@ -670,15 +747,15 @@ restart(S) ->
%% 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()},
+restart({{connect, _} = T, Opts, Svc},
+ #watchdog{parent = Pid,
+ sequence = Mask,
+ restrict = {R,_},
+ dictionary = Dict0}
+ = S) ->
+ send(Pid, {reconnect, self()}),
Nodes = restrict_nodes(R),
- S#watchdog{transport = monitor(diameter_peer_fsm:start(T,
- Opts,
- {Mask, Nodes, Svc})),
+ S#watchdog{transport = start(T, Opts, Mask, Nodes, Dict0, Svc),
restrict = {R, lists:member(node(), Nodes)}};
%% No restriction on the number of connections to the same peer: just
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index 1e31c40afe..80036879ea 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.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
@@ -128,8 +128,8 @@ gen(hrl, Spec, Mod, Path) ->
gen(erl, Spec, Mod, Path) ->
Forms = [{?attribute, module, Mod},
- {?attribute, compile, [{parse_transform, diameter_exprecs}]},
- {?attribute, compile, [{parse_transform, diameter_nowarn}]},
+ {?attribute, compile, {parse_transform, diameter_exprecs}},
+ {?attribute, compile, nowarn_unused_function},
{?attribute, export, [{name, 0},
{id, 0},
{vendor_id, 0},
diff --git a/lib/diameter/src/compiler/diameter_exprecs.erl b/lib/diameter/src/compiler/diameter_exprecs.erl
index 191f53f29d..9e32f53724 100644
--- a/lib/diameter/src/compiler/diameter_exprecs.erl
+++ b/lib/diameter/src/compiler/diameter_exprecs.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
@@ -18,7 +18,7 @@
%%
%%
-%% Parse transform for generating record access functions
+%% Parse transform for generating record access functions.
%%
%% This parse transform can be used to reduce compile-time
%% dependencies in large systems.
@@ -39,21 +39,21 @@
%%
%% export_records([RecName, ...])
%%
-%% causes this transform to lay out access functions for the exported
-%% records:
+%% causes this transform to insert functions for the exported records:
%%
%% -module(foo)
%% -compile({parse_transform, diameter_exprecs}).
%%
%% -record(r, {a, b, c}).
-%% -export_records([a]).
+%% -export_records([r]).
%%
%% -export(['#info-'/1, '#info-'/2,
-%% '#new-'/1, '#new-'/2,
-%% '#get-'/2, '#set-'/2,
-%% '#new-a'/0, '#new-a'/1,
-%% '#get-a'/2, '#set-a'/2,
-%% '#info-a'/1]).
+%% '#new-'/1, '#new-'/2,
+%% '#get-'/1', '#get-'/2,
+%% '#set-'/2,
+%% '#new-r'/0, '#new-r'/1,
+%% '#get-r'/2, '#set-r'/2,
+%% '#info-r'/1]).
%%
%% '#info-'(RecName) ->
%% '#info-'(RecName, fields).
@@ -61,15 +61,23 @@
%% '#info-'(r, Info) ->
%% '#info-r'(Info).
%%
+%% '#new-'([r | Vals]) -> '#new-r'(Vals);
%% '#new-'(r) -> #r{}.
-%% '#new-'(r, Vals) -> '#new-r'(Vals)
+%%
+%% '#new-'(r, Vals) -> '#new-r'(Vals).
%%
%% '#new-r'() -> #r{}.
%% '#new-r'(Vals) -> '#set-r'(Vals, #r{}).
%%
+%% '#get-'(#r{} = Rec) ->
+%% [r | '#get-r'(Rec)].
+%%
%% '#get-'(Attrs, #r{} = Rec) ->
%% '#get-r'(Attrs, Rec).
%%
+%% '#get-r'(#r{} = Rec) ->
+%% lists:zip([a,b,c], tl(tuple_to_list(Rec))).
+%%
%% '#get-r'(Attrs, Rec) when is_list(Attrs) ->
%% ['#get-r'(A, Rec) || A <- Attrs];
%% '#get-r'(a, Rec) -> Rec#r.a;
@@ -116,6 +124,7 @@ a_export(Exports) ->
{fname(info), 2},
{fname(new), 1},
{fname(new), 2},
+ {fname(get), 1},
{fname(get), 2},
{fname(set), 2}
| lists:flatmap(fun export/1, Exports)]}.
@@ -124,6 +133,7 @@ export(Rname) ->
New = fname(new, Rname),
[{New, 0},
{New, 1},
+ {fname(get, Rname), 1},
{fname(get, Rname), 2},
{fname(set, Rname), 2},
{fname(info, Rname), 1}].
@@ -135,6 +145,7 @@ f_accessors(Es, Rs) ->
'#info-/2'(Es),
'#new-/1'(Es),
'#new-/2'(Es),
+ '#get-/1'(Es),
'#get-/2'(Es),
'#set-/2'(Es)
| lists:flatmap(fun(N) -> accessors(N, fields(N, Rs)) end, Es)].
@@ -142,6 +153,7 @@ f_accessors(Es, Rs) ->
accessors(Rname, Fields) ->
['#new-X/0'(Rname),
'#new-X/1'(Rname),
+ '#get-X/1'(Rname, Fields),
'#get-X/2'(Rname, Fields),
'#set-X/2'(Rname, Fields),
'#info-X/1'(Rname, Fields)].
@@ -183,12 +195,15 @@ fname(Op, Rname) ->
'#new-/1'(Exports) ->
{?function, fname(new), 1,
- lists:map(fun 'new-'/1, Exports) ++ [?BADARG(1)]}.
+ lists:flatmap(fun 'new-'/1, Exports) ++ [?BADARG(1)]}.
'new-'(R) ->
- {?clause, [?ATOM(R)],
- [],
- [{?record, R, []}]}.
+ [{?clause, [?ATOM(R)],
+ [],
+ [{?record, R, []}]},
+ {?clause, [{?cons, ?ATOM(R), ?VAR('Vals')}],
+ [],
+ [?CALL(fname(new, R), [?VAR('Vals')])]}].
'#new-/2'(Exports) ->
{?function, fname(new), 2,
@@ -199,6 +214,15 @@ fname(Op, Rname) ->
[],
[?CALL(fname(new, R), [?VAR('Vals')])]}.
+'#get-/1'(Exports) ->
+ {?function, fname(get), 1,
+ lists:map(fun 'get--'/1, Exports) ++ [?BADARG(1)]}.
+
+'get--'(R) ->
+ {?clause, [{?match, {?record, R, []}, ?VAR('Rec')}],
+ [],
+ [{?cons, ?ATOM(R), ?CALL(fname(get, R), [?VAR('Rec')])}]}.
+
'#get-/2'(Exports) ->
{?function, fname(get), 2,
lists:map(fun 'get-'/1, Exports) ++ [?BADARG(2)]}.
@@ -245,6 +269,14 @@ fname(Op, Rname) ->
[{?record, ?VAR('Rec'), Rname,
[{?record_field, ?ATOM(Attr), ?VAR('V')}]}]}.
+'#get-X/1'(Rname, Fields) ->
+ FName = fname(get, Rname),
+ Values = ?CALL(tl, [?CALL(tuple_to_list, [?VAR('Rec')])]),
+ {?function, FName, 1,
+ [{?clause, [?VAR('Rec')],
+ [],
+ [?APPLY(lists, zip, [?TERM(Fields), Values])]}]}.
+
'#get-X/2'(Rname, Fields) ->
FName = fname(get, Rname),
{?function, FName, 2,
diff --git a/lib/diameter/src/compiler/diameter_forms.hrl b/lib/diameter/src/compiler/diameter_forms.hrl
index 4cd86c32aa..9b14c1715a 100644
--- a/lib/diameter/src/compiler/diameter_forms.hrl
+++ b/lib/diameter/src/compiler/diameter_forms.hrl
@@ -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
@@ -36,6 +36,7 @@
-define(clause, ?F(clause)).
-define(function, ?F(function)).
-define(call, ?F(call)).
+-define(cons, ?F(cons)).
-define('fun', ?F('fun')).
-define(generate, ?F(generate)).
-define(lc, ?F(lc)).
diff --git a/lib/diameter/src/compiler/diameter_nowarn.erl b/lib/diameter/src/compiler/diameter_nowarn.erl
deleted file mode 100644
index 6c17af6563..0000000000
--- a/lib/diameter/src/compiler/diameter_nowarn.erl
+++ /dev/null
@@ -1,41 +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%
-%%
-
-%%
-%% A parse transform to work around dialyzer currently not
-%% understanding nowarn_unused_function except on individual
-%% functions. The include of diameter_gen.hrl by generated dictionary
-%% modules contains code that may not be called depending on the
-%% dictionary. (The relay dictionary for example.)
-%%
-%% Even called functions may contain cases that aren't used for a
-%% particular dictionary. This also causes dialyzer to complain but
-%% there's no way to silence it in this case.
-%%
-
--module(diameter_nowarn).
-
--export([parse_transform/2]).
-
-parse_transform(Forms, _Options) ->
- [{attribute, ?LINE, compile, {nowarn_unused_function, {F,A}}}
- || {function, _, F, A, _} <- Forms]
- ++ Forms.
-%% Note that dialyzer also doesn't understand {nowarn_unused_function, FAs}
-%% with FAs a list of tuples.
diff --git a/lib/diameter/src/dict/acct_rfc6733.dia b/lib/diameter/src/dict/acct_rfc6733.dia
new file mode 100644
index 0000000000..7d6d11a71e
--- /dev/null
+++ b/lib/diameter/src/dict/acct_rfc6733.dia
@@ -0,0 +1,72 @@
+;;
+;; %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%
+;;
+
+@id 3
+@name diameter_gen_acct_rfc6733
+@prefix diameter_base_accounting
+@vendor 0 IETF
+
+@inherits diameter_gen_base_rfc6733
+
+@messages
+
+ ACR ::= < Diameter Header: 271, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Destination-Host ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ACA ::= < Diameter Header: 271, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
diff --git a/lib/diameter/src/dict/base_rfc6733.dia b/lib/diameter/src/dict/base_rfc6733.dia
new file mode 100644
index 0000000000..e1d1f18d86
--- /dev/null
+++ b/lib/diameter/src/dict/base_rfc6733.dia
@@ -0,0 +1,415 @@
+;;
+;; %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%
+;;
+
+@id 0
+@name diameter_gen_base_rfc6733
+@prefix diameter_base
+@vendor 0 IETF
+
+@avp_types
+
+ Acct-Interim-Interval 85 Unsigned32 M
+ Accounting-Realtime-Required 483 Enumerated M
+ Acct-Multi-Session-Id 50 UTF8String M
+ Accounting-Record-Number 485 Unsigned32 M
+ Accounting-Record-Type 480 Enumerated M
+ Acct-Session-Id 44 OctetString M
+ Accounting-Sub-Session-Id 287 Unsigned64 M
+ Acct-Application-Id 259 Unsigned32 M
+ Auth-Application-Id 258 Unsigned32 M
+ Auth-Request-Type 274 Enumerated M
+ Authorization-Lifetime 291 Unsigned32 M
+ Auth-Grace-Period 276 Unsigned32 M
+ Auth-Session-State 277 Enumerated M
+ Re-Auth-Request-Type 285 Enumerated M
+ Class 25 OctetString M
+ Destination-Host 293 DiamIdent M
+ Destination-Realm 283 DiamIdent M
+ Disconnect-Cause 273 Enumerated M
+ Error-Message 281 UTF8String -
+ Error-Reporting-Host 294 DiamIdent -
+ Event-Timestamp 55 Time M
+ Experimental-Result 297 Grouped M
+ Experimental-Result-Code 298 Unsigned32 M
+ Failed-AVP 279 Grouped M
+ Firmware-Revision 267 Unsigned32 -
+ Host-IP-Address 257 Address M
+ Inband-Security-Id 299 Unsigned32 M
+ Multi-Round-Time-Out 272 Unsigned32 M
+ Origin-Host 264 DiamIdent M
+ Origin-Realm 296 DiamIdent M
+ Origin-State-Id 278 Unsigned32 M
+ Product-Name 269 UTF8String -
+ Proxy-Host 280 DiamIdent M
+ Proxy-Info 284 Grouped M
+ Proxy-State 33 OctetString M
+ Redirect-Host 292 DiamURI M
+ Redirect-Host-Usage 261 Enumerated M
+ Redirect-Max-Cache-Time 262 Unsigned32 M
+ Result-Code 268 Unsigned32 M
+ Route-Record 282 DiamIdent M
+ Session-Id 263 UTF8String M
+ Session-Timeout 27 Unsigned32 M
+ Session-Binding 270 Unsigned32 M
+ Session-Server-Failover 271 Enumerated M
+ Supported-Vendor-Id 265 Unsigned32 M
+ Termination-Cause 295 Enumerated M
+ User-Name 1 UTF8String M
+ Vendor-Id 266 Unsigned32 M
+ Vendor-Specific-Application-Id 260 Grouped M
+
+@messages
+
+ CER ::= < Diameter Header: 257, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+ CEA ::= < Diameter Header: 257 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ 1* { Host-IP-Address }
+ { Vendor-Id }
+ { Product-Name }
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Failed-AVP ]
+ * [ Supported-Vendor-Id ]
+ * [ Auth-Application-Id ]
+ * [ Inband-Security-Id ]
+ * [ Acct-Application-Id ]
+ * [ Vendor-Specific-Application-Id ]
+ [ Firmware-Revision ]
+ * [ AVP ]
+
+ DPR ::= < Diameter Header: 282, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ { Disconnect-Cause }
+ * [ AVP ]
+
+ DPA ::= < Diameter Header: 282 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ [ Failed-AVP ]
+ * [ AVP ]
+
+ DWR ::= < Diameter Header: 280, REQ >
+ { Origin-Host }
+ { Origin-Realm }
+ [ Origin-State-Id ]
+ * [ AVP ]
+
+ DWA ::= < Diameter Header: 280 >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ Error-Message ]
+ [ Failed-AVP ]
+ [ Origin-State-Id ]
+ * [ AVP ]
+
+ answer-message ::= < Diameter Header: code, ERR [PXY] >
+ 0*1 < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Result-Code }
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Experimental-Result ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ RAR ::= < Diameter Header: 258, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ { Re-Auth-Request-Type }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ RAA ::= < Diameter Header: 258, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ STR ::= < Diameter Header: 275, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Auth-Application-Id }
+ { Termination-Cause }
+ [ User-Name ]
+ [ Destination-Host ]
+ * [ Class ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ STA ::= < Diameter Header: 275, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ * [ Class ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Origin-State-Id ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ ASR ::= < Diameter Header: 274, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Destination-Host }
+ { Auth-Application-Id }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ASA ::= < Diameter Header: 274, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ [ User-Name ]
+ [ Origin-State-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ * [ Redirect-Host ]
+ [ Redirect-Host-Usage ]
+ [ Redirect-Max-Cache-Time ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+ ACR ::= < Diameter Header: 271, REQ, PXY >
+ < Session-Id >
+ { Origin-Host }
+ { Origin-Realm }
+ { Destination-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Destination-Host ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ Route-Record ]
+ * [ AVP ]
+
+ ACA ::= < Diameter Header: 271, PXY >
+ < Session-Id >
+ { Result-Code }
+ { Origin-Host }
+ { Origin-Realm }
+ { Accounting-Record-Type }
+ { Accounting-Record-Number }
+ [ Acct-Application-Id ]
+ [ Vendor-Specific-Application-Id ]
+ [ User-Name ]
+ [ Accounting-Sub-Session-Id ]
+ [ Acct-Session-Id ]
+ [ Acct-Multi-Session-Id ]
+ [ Error-Message ]
+ [ Error-Reporting-Host ]
+ [ Failed-AVP ]
+ [ Acct-Interim-Interval ]
+ [ Accounting-Realtime-Required ]
+ [ Origin-State-Id ]
+ [ Event-Timestamp ]
+ * [ Proxy-Info ]
+ * [ AVP ]
+
+@enum Disconnect-Cause
+
+ REBOOTING 0
+ BUSY 1
+ DO_NOT_WANT_TO_TALK_TO_YOU 2
+
+@enum Redirect-Host-Usage
+
+ DONT_CACHE 0
+ ALL_SESSION 1
+ ALL_REALM 2
+ REALM_AND_APPLICATION 3
+ ALL_APPLICATION 4
+ ALL_HOST 5
+ ALL_USER 6
+
+@enum Auth-Request-Type
+
+ AUTHENTICATE_ONLY 1
+ AUTHORIZE_ONLY 2
+ AUTHORIZE_AUTHENTICATE 3
+
+@enum Auth-Session-State
+
+ STATE_MAINTAINED 0
+ NO_STATE_MAINTAINED 1
+
+@enum Re-Auth-Request-Type
+
+ AUTHORIZE_ONLY 0
+ AUTHORIZE_AUTHENTICATE 1
+
+@enum Termination-Cause
+
+ LOGOUT 1
+ SERVICE_NOT_PROVIDED 2
+ BAD_ANSWER 3
+ ADMINISTRATIVE 4
+ LINK_BROKEN 5
+ AUTH_EXPIRED 6
+ USER_MOVED 7
+ SESSION_TIMEOUT 8
+
+@enum Session-Server-Failover
+
+ REFUSE_SERVICE 0
+ TRY_AGAIN 1
+ ALLOW_SERVICE 2
+ TRY_AGAIN_ALLOW_SERVICE 3
+
+@enum Accounting-Record-Type
+
+ EVENT_RECORD 1
+ START_RECORD 2
+ INTERIM_RECORD 3
+ STOP_RECORD 4
+
+@enum Accounting-Realtime-Required
+
+ DELIVER_AND_GRANT 1
+ GRANT_AND_STORE 2
+ GRANT_AND_LOSE 3
+
+@define Result-Code
+
+ ;; 7.1.1. Informational
+ MULTI_ROUND_AUTH 1001
+
+ ;; 7.1.2. Success
+ SUCCESS 2001
+ LIMITED_SUCCESS 2002
+
+ ;; 7.1.3. Protocol Errors
+ COMMAND_UNSUPPORTED 3001
+ UNABLE_TO_DELIVER 3002
+ REALM_NOT_SERVED 3003
+ TOO_BUSY 3004
+ LOOP_DETECTED 3005
+ REDIRECT_INDICATION 3006
+ APPLICATION_UNSUPPORTED 3007
+ INVALID_HDR_BITS 3008
+ INVALID_AVP_BITS 3009
+ UNKNOWN_PEER 3010
+
+ ;; 7.1.4. Transient Failures
+ AUTHENTICATION_REJECTED 4001
+ OUT_OF_SPACE 4002
+ ELECTION_LOST 4003
+
+ ;; 7.1.5. Permanent Failures
+ AVP_UNSUPPORTED 5001
+ UNKNOWN_SESSION_ID 5002
+ AUTHORIZATION_REJECTED 5003
+ INVALID_AVP_VALUE 5004
+ MISSING_AVP 5005
+ RESOURCES_EXCEEDED 5006
+ CONTRADICTING_AVPS 5007
+ AVP_NOT_ALLOWED 5008
+ AVP_OCCURS_TOO_MANY_TIMES 5009
+ NO_COMMON_APPLICATION 5010
+ UNSUPPORTED_VERSION 5011
+ UNABLE_TO_COMPLY 5012
+ INVALID_BIT_IN_HEADER 5013
+ INVALID_AVP_LENGTH 5014
+ INVALID_MESSAGE_LENGTH 5015
+ INVALID_AVP_BIT_COMBO 5016
+ NO_COMMON_SECURITY 5017
+
+@grouped
+
+ Proxy-Info ::= < AVP Header: 284 >
+ { Proxy-Host }
+ { Proxy-State }
+ * [ AVP ]
+
+ Failed-AVP ::= < AVP Header: 279 >
+ 1* {AVP}
+
+ Experimental-Result ::= < AVP Header: 297 >
+ { Vendor-Id }
+ { Experimental-Result-Code }
+
+ Vendor-Specific-Application-Id ::= < AVP Header: 260 >
+ { Vendor-Id }
+ [ Auth-Application-Id ]
+ [ Acct-Application-Id ]
diff --git a/lib/diameter/src/dict/capup_rfc6737.dia b/lib/diameter/src/dict/capup_rfc6737.dia
new file mode 100644
index 0000000000..35d2a9f218
--- /dev/null
+++ b/lib/diameter/src/dict/capup_rfc6737.dia
@@ -0,0 +1,49 @@
+;;
+;; %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%
+;;
+
+@id 10
+@name diameter_gen_capup_rfc6737
+@prefix diameter_capup
+@vendor 0 IETF
+
+@inherits diameter_gen_base_rfc6733
+
+@messages
+
+ 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 ]
+
+
+ CUA ::= < Diameter Header: 328 >
+ { Origin-Host }
+ { Origin-Realm }
+ { Result-Code }
+ [ Error-Message ]
+ * [ AVP ]
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index 01f9284881..f8d3cf1d6f 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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
@@ -17,11 +17,13 @@
#
# %CopyrightEnd%
-# Runtime dictionary files in ./dict. Modules will be generated from
-# these are included in the app file.
+# Runtime dictionary files in ./dict. Modules generated from these are
+# included in the app file.
DICTS = \
base_rfc3588 \
+ base_rfc6733 \
base_accounting \
+ acct_rfc6733 \
relay
# The yecc grammar for the dictionary parser.
@@ -49,6 +51,7 @@ RT_MODULES = \
base/diameter_stats \
base/diameter_sup \
base/diameter_sync \
+ base/diameter_traffic \
base/diameter_types \
base/diameter_watchdog \
base/diameter_watchdog_sup \
@@ -67,7 +70,6 @@ CT_MODULES = \
base/diameter_info \
compiler/diameter_codegen \
compiler/diameter_exprecs \
- compiler/diameter_nowarn \
compiler/diameter_dict_scanner \
compiler/diameter_dict_util \
compiler/diameter_make
@@ -103,3 +105,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_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 79b8b851fb..8b8c2a6694 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-2012. 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
@@ -188,8 +188,6 @@ i({connect, Pid, Opts, Addrs, Ref}) ->
#transport{parent = Pid,
mode = {connect, connect(Sock, RAs, RP, [])},
socket = Sock};
-i({connect, _, _, _} = T) -> %% from old code
- x(T);
%% An accepting transport spawned by diameter.
i({accept, Pid, LPid, Sock, Ref})
@@ -201,8 +199,6 @@ i({accept, Pid, LPid, Sock, Ref})
#transport{parent = Pid,
mode = {accept, LPid},
socket = Sock};
-i({accept, _, _, _} = T) -> %% from old code
- x(T);
%% An accepting transport spawned at association establishment.
i({accept, Ref, LPid, Sock, Id}) ->
@@ -293,7 +289,7 @@ ports() ->
Ts = diameter_reg:match({?MODULE, '_', '_'}),
[{type(T), N, Pid} || {{?MODULE, T, {_, {_, S}}}, Pid} <- Ts,
{ok, N} <- [inet:port(S)]].
-
+
ports(Ref) ->
Ts = diameter_reg:match({?MODULE, '_', {Ref, '_'}}),
[{type(T), N, Pid} || {{?MODULE, T, {R, {_, S}}}, Pid} <- Ts,
@@ -488,8 +484,8 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) ->
%% TLS over SCTP is described in RFC 3436 but has limitations as
%% described in RFC 6083. The latter describes DTLS over SCTP, which
%% addresses these limitations, DTLS itself being described in RFC
-%% 4347. TLS is primarily used over TCP, which the current RFC 3588
-%% draft acknowledges by equating TLS with TLS/TCP and DTLS/SCTP.
+%% 4347. TLS is primarily used over TCP, which RFC 6733 acknowledges
+%% by equating TLS with TLS/TCP and DTLS/SCTP.
transition({diameter, {tls, _Ref, _Type, _Bool}}, _) ->
stop;
@@ -589,8 +585,7 @@ recv({_, #sctp_assoc_change{state = comm_up,
socket = Sock}
= S) ->
Ref = getr(?REF_KEY),
- is_reference(Ref) %% started in new code
- andalso publish(T, Ref, Id, Sock),
+ publish(T, Ref, Id, Sock),
up(S#transport{assoc_id = Id,
streams = {IS, OS}});
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index f3fbbee609..132088b514 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-2012. 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
@@ -52,7 +52,10 @@
-define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1
-define(LISTENER_TIMEOUT, 30000).
--define(FRAGMENT_TIMEOUT, 1000).
+-define(DEFAULT_FRAGMENT_TIMEOUT, 1000).
+
+-define(IS_UINT32(N), (is_integer(N) andalso 0 =< N andalso 0 == N bsr 32)).
+-define(IS_TIMEOUT(N), (infinity == N orelse ?IS_UINT32(N))).
%% cb_info passed to ssl.
-define(TCP_CB(Mod), {Mod, tcp, tcp_closed, tcp_error}).
@@ -72,7 +75,6 @@
{parent :: pid(),
transport = self() :: pid()}).
--type tref() :: reference(). %% timer reference
-type length() :: 0..16#FFFFFF. %% message length from Diameter header
-type size() :: non_neg_integer(). %% accumulated binary size
-type frag() :: {length(), size(), binary(), list(binary())}
@@ -80,11 +82,14 @@
%% Accepting/connecting transport process state.
-record(transport,
- {socket :: inet:socket() | ssl:sslsock(), %% accept or connect socket
+ {socket :: inet:socket() | ssl:sslsocket(), %% accept/connect socket
parent :: pid(), %% of process that started us
module :: module(), %% gen_tcp-like module
- frag = <<>> :: binary() | {tref(), frag()}, %% message fragment
- ssl :: boolean() | [term()]}). %% ssl options
+ frag = <<>> :: frag(), %% message fragment
+ ssl :: boolean() | [term()], %% ssl options
+ timeout :: infinity | 0..16#FFFFFFFF, %% fragment timeout
+ tref = false :: false | reference(), %% fragment timer reference
+ flush = false :: boolean()}). %% flush fragment at timeout?
%% The usual transport using gen_tcp can be replaced by anything
%% sufficiently gen_tcp-like by passing a 'module' option as the first
%% (for simplicity) transport option. The transport_module diameter_etcp
@@ -161,7 +166,12 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
%% that does nothing but kill us with the parent until call
%% returns.
{ok, MPid} = diameter_tcp_sup:start_child(#monitor{parent = Pid}),
- {SslOpts, Rest} = ssl(Opts),
+ {SslOpts, Rest0} = ssl(Opts),
+ {OwnOpts, Rest} = own(Rest0),
+ Tmo = proplists:get_value(fragment_timer,
+ OwnOpts,
+ ?DEFAULT_FRAGMENT_TIMEOUT),
+ ?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}),
Sock = i(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
@@ -170,7 +180,8 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
#transport{parent = Pid,
module = M,
socket = Sock,
- ssl = SslOpts};
+ ssl = SslOpts,
+ timeout = Tmo};
%% Put the reference in the process dictionary since we now use it
%% advertise the ssl socket after TLS upgrade.
@@ -196,6 +207,10 @@ i({listen, LRef, APid, {Mod, Opts, Addrs}}) ->
erlang:monitor(process, APid),
start_timer(#listener{socket = LSock}).
+own(Opts) ->
+ {Own, Rest} = proplists:split(Opts, [fragment_timer]),
+ {lists:append(Own), Rest}.
+
ssl(Opts) ->
{[SslOpts], Rest} = proplists:split(Opts, [ssl_options]),
{ssl_opts(SslOpts), Rest}.
@@ -368,8 +383,6 @@ handle_info(T, #monitor{} = S) ->
%% # code_change/3
%% ---------------------------------------------------------------------------
-code_change(_, {transport, _, _, _, _} = S, _) ->
- {ok, #transport{} = list_to_tuple(tuple_to_list(S) ++ [false])};
code_change(_, State, _) ->
{ok, State}.
@@ -452,6 +465,7 @@ t(T,S) ->
%% Initial incoming message when we might need to upgrade to TLS:
%% don't request another message until we know.
+
transition({tcp, Sock, Bin}, #transport{socket = Sock,
parent = Pid,
frag = Head,
@@ -459,13 +473,13 @@ transition({tcp, Sock, Bin}, #transport{socket = Sock,
ssl = Opts}
= S)
when is_list(Opts) ->
- case recv1(Head, Bin) of
+ case rcv(Head, Bin) of
{Msg, B} when is_binary(Msg) ->
diameter_peer:recv(Pid, Msg),
S#transport{frag = B};
Frag ->
setopts(M, Sock),
- S#transport{frag = Frag}
+ start_fragment_timer(S#transport{frag = Frag})
end;
%% Incoming message.
@@ -476,7 +490,7 @@ transition({P, Sock, Bin}, #transport{socket = Sock,
when P == tcp, not B;
P == ssl, B ->
setopts(M, Sock),
- recv(Bin, S);
+ start_fragment_timer(recv(Bin, S));
%% Capabilties exchange has decided on whether or not to run over TLS.
transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid}
@@ -487,7 +501,7 @@ transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid}
= tls_handshake(Type, B, S),
Pid ! {diameter, {tls, Ref}},
setopts(M, Sock),
- NS#transport{ssl = B};
+ start_fragment_timer(NS#transport{ssl = B});
transition({C, Sock}, #transport{socket = Sock,
ssl = B})
@@ -520,8 +534,8 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid,
stop;
%% Timeout for reception of outstanding packets.
-transition({timeout, TRef, flush}, S) ->
- flush(TRef, S);
+transition({timeout, TRef, flush}, #transport{tref = TRef} = S) ->
+ flush(S#transport{tref = false});
%% Request for the local port number.
transition({resolve_port, Pid}, #transport{socket = Sock,
@@ -559,9 +573,7 @@ tls_handshake(Type, true, #transport{socket = Sock,
= S) ->
{ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]),
Ref = getr(?REF_KEY),
- is_reference(Ref) %% started in new code
- andalso
- (true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})),
+ true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}}),
S#transport{socket = SSock,
module = ssl};
@@ -576,30 +588,25 @@ tls(accept, Sock, Opts) ->
%% recv/2
%%
-%% Reassemble fragmented messages and extract multple message sent
+%% Reassemble fragmented messages and extract multiple message sent
%% using Nagle.
recv(Bin, #transport{parent = Pid, frag = Head} = S) ->
- case recv1(Head, Bin) of
+ case rcv(Head, Bin) of
{Msg, B} when is_binary(Msg) ->
diameter_peer:recv(Pid, Msg),
recv(B, S#transport{frag = <<>>});
Frag ->
- S#transport{frag = Frag}
+ S#transport{frag = Frag,
+ flush = false}
end.
-%% recv1/2
+%% rcv/2
%% No previous fragment.
-recv1(<<>>, Bin) ->
+rcv(<<>>, Bin) ->
rcv(Bin);
-recv1({TRef, Head}, Bin) ->
- erlang:cancel_timer(TRef),
- rcv(Head, Bin).
-
-%% rcv/2
-
%% Not even the first four bytes of the header.
rcv(Head, Bin)
when is_binary(Head) ->
@@ -614,22 +621,22 @@ rcv({Len, N, Head, Acc}, Bin) ->
%% Extract a message for which we have all bytes.
rcv(Len, N, Head, Acc)
when Len =< N ->
- rcv1(Len, bin(Head, Acc));
+ recv1(Len, bin(Head, Acc));
%% Wait for more packets.
rcv(Len, N, Head, Acc) ->
- {start_timer(), {Len, N, Head, Acc}}.
+ {Len, N, Head, Acc}.
-%% rcv/2
+%% rcv/1
%% Nothing left.
rcv(<<>> = Bin) ->
Bin;
-%% Well, this isn't good. Chances are things will go south from here
-%% but if we're lucky then the bytes we have extend to an intended
-%% message boundary and we can recover by simply discarding them,
-%% which is the result of receiving them.
+%% The Message Length isn't even sufficient for a header. Chances are
+%% things will go south from here but if we're lucky then the bytes we
+%% have extend to an intended message boundary and we can recover by
+%% simply receiving them. Make it so.
rcv(<<_:1/binary, Len:24, _/binary>> = Bin)
when Len < 20 ->
{Bin, <<>>};
@@ -637,23 +644,23 @@ rcv(<<_:1/binary, Len:24, _/binary>> = Bin)
%% Enough bytes to extract a message.
rcv(<<_:1/binary, Len:24, _/binary>> = Bin)
when Len =< size(Bin) ->
- rcv1(Len, Bin);
+ recv1(Len, Bin);
%% Or not: wait for more packets.
rcv(<<_:1/binary, Len:24, _/binary>> = Head) ->
- {start_timer(), {Len, size(Head), Head, []}};
+ {Len, size(Head), Head, []};
%% Not even 4 bytes yet.
rcv(Head) ->
- {start_timer(), Head}.
+ Head.
-%% rcv1/2
+%% recv1/2
-rcv1(Len, Bin) ->
+recv1(Len, Bin) ->
<<Msg:Len/binary, Rest/binary>> = Bin,
{Msg, Rest}.
-%% bin/[12]
+%% bin/1-2
bin(Head, Acc) ->
list_to_binary([Head | lists:reverse(Acc)]).
@@ -664,7 +671,7 @@ bin(Bin)
when is_binary(Bin) ->
Bin.
-%% start_timer/0
+%% flush/1
%% An erroneously large message length may leave us with a fragment
%% that lingers if the peer doesn't have anything more to send. Start
@@ -677,14 +684,30 @@ bin(Bin)
%% since all messages with length problems are discarded this should
%% also eventually lead to watchdog failover.
-start_timer() ->
- erlang:start_timer(?FRAGMENT_TIMEOUT, self(), flush).
+%% No fragment to flush.
+flush(#transport{frag = <<>>} = S) ->
+ S;
-flush(TRef, #transport{parent = Pid, frag = {TRef, Head}} = S) ->
- diameter_peer:recv(Pid, bin(Head)),
- S#transport{frag = <<>>};
-flush(_, S) ->
- S.
+%% Messages have been received since last timer expiry.
+flush(#transport{flush = false} = S) ->
+ start_fragment_timer(S#transport{flush = true});
+
+%% No messages since last expiry.
+flush(#transport{frag = Frag, parent = Pid} = S) ->
+ diameter_peer:recv(Pid, bin(Frag)),
+ S#transport{frag = <<>>}.
+
+%% start_fragment_timer/1
+%%
+%% Start a timer only if there's none running and a message to flush.
+
+start_fragment_timer(#transport{frag = B, tref = TRef} = S)
+ when B == <<>>;
+ TRef /= false ->
+ S;
+
+start_fragment_timer(#transport{timeout = Tmo} = S) ->
+ S#transport{tref = erlang:start_timer(Tmo, self(), flush)}.
%% accept/2
diff --git a/lib/diameter/test/.gitignore b/lib/diameter/test/.gitignore
index df38dfc5e3..4f19542bbe 100644
--- a/lib/diameter/test/.gitignore
+++ b/lib/diameter/test/.gitignore
@@ -1,3 +1,4 @@
/log
/depend.mk
+/coverspec
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 866d135bd9..061f0bcbef 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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
@@ -56,26 +56,28 @@ DATA_DIRS = $(sort $(dir $(DATA)))
ERL_COMPILE_FLAGS += +warn_export_vars \
+warn_unused_vars \
-I ../include \
- -I ../src/gen
+ -I ../src/gen \
+ $(STRICT_FLAGS)
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
+all debug opt: $(TARGET_FILES)
+
+strict:
+ $(MAKE) opt STRICT_FLAGS=-Werror
+
# Require success ...
-all: opt
+run: $(SUITES)
# ... or not.
any: opt
$(MAKE) -i $(SUITES)
-run: $(SUITES)
-
-debug opt: $(TARGET_FILES)
-
clean:
rm -f $(TARGET_FILES)
- rm -f depend.mk
+ rm -f depend.mk coverspec
realclean: clean
rm -rf log
@@ -101,7 +103,10 @@ help:
@echo " Compile all test suites."
@echo
@echo " run:"
- @echo " Compile and run all test suites."
+ @echo " Compile and run all test suites, stop on failure."
+ @echo
+ @echo " any:"
+ @echo " Compile and run all test suites, ignore any failures."
@echo
@echo " $(SUITES):"
@echo " Compile and run a specific test suite."
@@ -113,7 +118,7 @@ help:
@echo " Echo some relevant variables."
@echo ========================================
-.PHONY: all any run clean debug docs help info opt realclean
+.PHONY: all any run clean debug docs help info opt realclean strict
# ----------------------------------------------------
# Special Targets
@@ -131,10 +136,21 @@ $(SUITES): log opt
| awk '{print} / FAILED /{rc=1} END{exit rc}' rc=0
# Shorter in sed but requires a GNU extension (ie. Q).
+cover: log opt coverspec
+ $(ERL) -noinput \
+ -pa $(realpath ../ebin) \
+ -sname diameter_cover \
+ -s diameter_ct cover \
+ -s init stop \
+ | awk '{print} / FAILED /{rc=1} END{exit rc}' rc=0
+
+coverspec: diameter.cover
+ sed -f [email protected] $< > $@
+
log:
mkdir $@
-.PHONY: $(SUITES)
+.PHONY: $(SUITES) cover
# ----------------------------------------------------
# Release Targets
diff --git a/lib/diameter/test/coverspec.sed b/lib/diameter/test/coverspec.sed
new file mode 100644
index 0000000000..5e81621593
--- /dev/null
+++ b/lib/diameter/test/coverspec.sed
@@ -0,0 +1,33 @@
+#
+# %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%
+#
+
+#
+# Morph diameter.cover into a legitimate cover spec. All that's being
+# retained is the list of excluded modules. This is used by Makefile
+# when running cover locally.
+#
+
+/^{incl_app,/{
+ i\
+{level, details}.\
+{incl_dirs, ["../ebin"]}.
+ d
+}
+
+/^{excl_mods,/s@ .*@@
diff --git a/lib/diameter/test/depend.sed b/lib/diameter/test/depend.sed
index 95dca44984..602d1ab497 100644
--- a/lib/diameter/test/depend.sed
+++ b/lib/diameter/test/depend.sed
@@ -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
@@ -38,4 +38,4 @@
s@^-include("@@
s@".*@@
G
-s@^\(.*\)\n\(.*\)@$(EBIN)/\2.$(EMULATOR): \1@
+s@^\(.*\)\n\(.*\)@\2.$(EMULATOR): \1@
diff --git a/lib/diameter/test/diameter.cover b/lib/diameter/test/diameter.cover
index 5fde6c7d01..6586733871 100644
--- a/lib/diameter/test/diameter.cover
+++ b/lib/diameter/test/diameter.cover
@@ -1,6 +1,8 @@
-%% -*- erlang -*-
-{exclude,
- [
- ]
-}.
+{incl_app,diameter,details}.
+{excl_mods, diameter,
+ [diameter_dbg,
+ diameter_info,
+ diameter_etcp,
+ diameter_etcp_sup]
+}.
diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl
new file mode 100644
index 0000000000..89c78d8b57
--- /dev/null
+++ b/lib/diameter/test/diameter_3xxx_SUITE.erl
@@ -0,0 +1,509 @@
+%%
+%% %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 application_opt() request_errors. There's some overlap
+%% between this suite and the traffic suite but latter exercises more
+%% config.
+%%
+
+-module(diameter_3xxx_SUITE).
+
+-export([suite/0,
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+%% testcases
+-export([start/1,
+ send_unknown_application/1,
+ send_unknown_command/1,
+ send_ok/1,
+ send_invalid_avp_bits/1,
+ send_missing_avp/1,
+ send_ignore_missing_avp/1,
+ send_double_error/1,
+ send_3xxx/1,
+ send_5xxx/1,
+ stop/1]).
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/5,
+ prepare_request/4,
+ handle_answer/5,
+ handle_error/5,
+ handle_request/3]).
+
+-include("diameter.hrl").
+-include("diameter_gen_base_rfc6733.hrl").
+%% Use the fact that STR/STA is identical in RFC's 3588 and 6733.
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+-define(testcase(), proplists:get_value(testcase, get(?MODULE))).
+-define(group(Config), begin
+ put(?MODULE, Config),
+ ?util:name(proplists:get_value(group, Config))
+ end).
+
+-define(L, atom_to_list).
+-define(A, list_to_atom).
+
+-define(CLIENT, "CLIENT").
+-define(SERVER, "SERVER").
+-define(REALM, "erlang.org").
+-define(HOST(Host, Realm), Host ++ [$.|Realm]).
+
+-define(ERRORS, [answer, answer_3xxx, callback]).
+-define(RFCS, [rfc3588, rfc6733]).
+-define(DICT(RFC), ?A("diameter_gen_base_" ++ ?L(RFC))).
+-define(DICT, ?DICT(rfc6733)).
+
+-define(COMMON, ?DIAMETER_APP_ID_COMMON).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Name, Errors, RFC),
+ [{'Origin-Host', Name ++ "." ++ ?REALM},
+ {'Origin-Realm', ?REALM},
+ {'Host-IP-Address', [{127,0,0,1}]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Auth-Application-Id', [?COMMON]},
+ {application, [{dictionary, ?DICT(RFC)},
+ {module, ?MODULE},
+ {answer_errors, callback},
+ {request_errors, Errors}]}]).
+
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [{group, ?util:name([E,D])} || E <- ?ERRORS, D <- ?RFCS].
+
+groups() ->
+ Tc = tc(),
+ [{?util:name([E,D]), [], [start] ++ Tc ++ [stop]}
+ || E <- ?ERRORS, D <- ?RFCS].
+
+init_per_suite(Config) ->
+ ok = diameter:start(),
+ Config.
+
+end_per_suite(_Config) ->
+ ok = diameter:stop().
+
+init_per_group(Group, Config) ->
+ [{group, Group} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
+init_per_testcase(Name, Config) ->
+ [{testcase, Name} | Config].
+
+end_per_testcase(_, _) ->
+ ok.
+
+tc() ->
+ [send_unknown_application,
+ send_unknown_command,
+ send_ok,
+ send_invalid_avp_bits,
+ send_missing_avp,
+ send_ignore_missing_avp,
+ send_double_error,
+ send_3xxx,
+ send_5xxx].
+
+%% ===========================================================================
+
+%% start/1
+
+start(Config) ->
+ Group = proplists:get_value(group, Config),
+ [Errors, RFC] = ?util:name(Group),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?L(Group),
+ Errors,
+ RFC)),
+ ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT,
+ callback,
+ rfc6733)),
+ LRef = ?util:listen(?SERVER, tcp),
+ ?util:connect(?CLIENT, tcp, LRef).
+
+%% stop/1
+
+stop(_Config) ->
+ ok = diameter:remove_transport(?CLIENT, true),
+ ok = diameter:remove_transport(?SERVER, true),
+ ok = diameter:stop_service(?SERVER),
+ ok = diameter:stop_service(?CLIENT).
+
+%% send_unknown_application/1
+%%
+%% Send an unknown application that a callback (which shouldn't take
+%% place) fails on.
+
+%% diameter answers.
+send_unknown_application([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3007,
+ %% UNSUPPORTED_APPLICATION
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_unknown_application(Config) ->
+ send_unknown_application(?group(Config)).
+
+%% send_unknown_command/1
+%%
+%% Send a unknown command that a callback discards.
+
+%% handle_request discards the request.
+send_unknown_command([callback, _]) ->
+ {error, timeout} = call();
+
+%% diameter answers.
+send_unknown_command([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3001,
+ %% UNSUPPORTED_COMMAND
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_unknown_command(Config) ->
+ send_unknown_command(?group(Config)).
+
+%% send_ok/1
+%%
+%% Send a correct STR that a callback answers with 5002.
+
+%% Callback answers.
+send_ok([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 5002, %% UNKNOWN_SESSION_ID
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_ok(Config) ->
+ send_ok(?group(Config)).
+
+%% send_invalid_avp_bits/1
+%%
+%% Send a request with an incorrect length on the optional
+%% Origin-State-Id that a callback ignores.
+
+%% Callback answers.
+send_invalid_avp_bits([callback, _]) ->
+ #diameter_base_STA{'Result-Code' = 2001, %% SUCCESS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+%% diameter answers.
+send_invalid_avp_bits([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3009, %% INVALID_AVP_BITS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_invalid_avp_bits(Config) ->
+ send_invalid_avp_bits(?group(Config)).
+
+%% send_missing_avp/1
+%%
+%% Send a request with a missing AVP that a callback answers.
+
+%% diameter answers.
+send_missing_avp([answer, rfc6733]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% Callback answers.
+send_missing_avp([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+send_missing_avp(Config) ->
+ send_missing_avp(?group(Config)).
+
+%% send_ignore_missing_avp/1
+%%
+%% Send a request with a missing AVP that a callback ignores.
+
+%% diameter answers.
+send_ignore_missing_avp([answer, rfc6733]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% Callback answers, ignores the error
+send_ignore_missing_avp([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 2001, %% SUCCESS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_ignore_missing_avp(Config) ->
+ send_ignore_missing_avp(?group(Config)).
+
+%% send_double_error/1
+%%
+%% Send a request with both an incorrect length on the optional
+%% Origin-State-Id and a missing AVP.
+
+%% Callback answers with STA.
+send_double_error([callback, _]) ->
+ #diameter_base_STA{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% diameter answers with answer-message.
+send_double_error([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3009, %% INVALID_AVP_BITS
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+send_double_error(Config) ->
+ send_double_error(?group(Config)).
+
+%% send_3xxx/1
+%%
+%% Send a request that's answered with a 3xxx result code.
+
+%% Callback answers.
+send_3xxx([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3999,
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_3xxx(Config) ->
+ send_3xxx(?group(Config)).
+
+%% send_5xxx/1
+%%
+%% Send a request that's answered with a 5xxx result code.
+
+%% Callback answers but fails since 5xxx isn't allowed in an RFC 3588
+%% answer-message.
+send_5xxx([_, rfc3588]) ->
+ {error, timeout} = call();
+
+%% Callback answers.
+send_5xxx([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5999,
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_5xxx(Config) ->
+ send_5xxx(?group(Config)).
+
+%% ===========================================================================
+
+call() ->
+ Name = ?testcase(),
+ diameter:call(?CLIENT,
+ ?DICT,
+ #diameter_base_STR
+ {'Termination-Cause' = ?LOGOUT,
+ 'Auth-Application-Id' = ?COMMON,
+ 'Class' = [?L(Name)]},
+ [{extra, [Name]}]).
+
+%% ===========================================================================
+%% diameter callbacks
+
+%% peer_up/3
+
+peer_up(_SvcName, _Peer, State) ->
+ State.
+
+%% peer_down/3
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%% pick_peer/5
+
+pick_peer([Peer], _, ?CLIENT, _State, _Name) ->
+ {ok, Peer}.
+
+%% prepare_request/4
+
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) ->
+ {send, prepare(Pkt, Caps, Name)}.
+
+prepare(Pkt0, Caps, send_unknown_application) ->
+ Req = sta(Pkt0, Caps),
+ #diameter_packet{bin = <<H:8/binary, 0:32, T/binary>>}
+ = Pkt
+ = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
+
+ Pkt#diameter_packet{bin = <<H/binary, 23:32, T/binary>>};
+
+prepare(Pkt0, Caps, send_unknown_command) ->
+ Req = sta(Pkt0, Caps),
+ #diameter_packet{bin = <<H:5/binary, 275:24, T/binary>>}
+ = Pkt
+ = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
+
+ Pkt#diameter_packet{bin = <<H/binary, 572:24, T/binary>>};
+
+prepare(Pkt, Caps, T)
+ when T == send_ok;
+ T == send_3xxx;
+ T == send_5xxx ->
+ sta(Pkt, Caps);
+
+prepare(Pkt0, Caps, send_invalid_avp_bits) ->
+ Req0 = sta(Pkt0, Caps),
+ %% Append an Origin-State-Id with an incorrect AVP Length in order
+ %% to force 3009.
+ Req = Req0#diameter_base_STR{'Origin-State-Id' = [7]},
+ #diameter_packet{bin = Bin}
+ = Pkt
+ = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
+ Offset = size(Bin) - 12 + 5,
+ <<H:Offset/binary, Len:24, T/binary>> = Bin,
+ Pkt#diameter_packet{bin = <<H/binary, (Len + 2):24, T/binary>>};
+
+prepare(Pkt0, Caps, send_double_error) ->
+ dehost(prepare(Pkt0, Caps, send_invalid_avp_bits));
+
+prepare(Pkt, Caps, T)
+ when T == send_missing_avp;
+ T == send_ignore_missing_avp ->
+ Req = sta(Pkt, Caps),
+ dehost(diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req})).
+
+sta(Pkt, Caps) ->
+ #diameter_packet{msg = Req}
+ = Pkt,
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, DR}}
+ = Caps,
+ Req#diameter_base_STR{'Session-Id' = diameter:session_id(OH),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Destination-Realm' = DR}.
+
+%% Strip Origin-Host.
+dehost(#diameter_packet{bin = Bin} = Pkt) ->
+ <<V, Len:24, H:16/binary, T0/binary>>
+ = Bin,
+ {SessionId, T1} = split_avp(T0),
+ {OriginHost, T} = split_avp(T1),
+ Delta = size(OriginHost),
+ Pkt#diameter_packet{bin = <<V, (Len - Delta):24, H/binary,
+ SessionId/binary,
+ T/binary>>}.
+
+%% handle_answer/5
+
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Name) ->
+ Pkt#diameter_packet.msg.
+
+%% handle_error/5
+
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) ->
+ {error, Reason}.
+
+split_avp(<<_:5/binary, Len:24, _/binary>> = Bin) ->
+ L = pad(Len),
+ <<Avp:L/binary, T/binary>> = Bin,
+ {Avp, T}.
+
+pad(N)
+ when 0 == N rem 4 ->
+ N;
+pad(N) ->
+ N - (N rem 4) + 4.
+
+%% handle_request/3
+
+handle_request(#diameter_packet{header = #diameter_header{application_id = 0},
+ msg = Msg},
+ ?SERVER,
+ {_, Caps}) ->
+ request(Msg, Caps).
+
+request(undefined, _) -> %% unknown command
+ discard;
+
+request(#diameter_base_STR{'Class' = [Name]} = Req, Caps) ->
+ request(?A(Name), Req, Caps).
+
+request(send_ok, Req, Caps) ->
+ {reply, #diameter_packet{msg = answer(Req, Caps),
+ errors = [5002]}}; %% UNKNOWN_SESSION_ID
+
+request(send_3xxx, _Req, _Caps) ->
+ {answer_message, 3999};
+
+request(send_5xxx, _Req, _Caps) ->
+ {answer_message, 5999};
+
+request(send_invalid_avp_bits, Req, Caps) ->
+ #diameter_base_STR{'Origin-State-Id' = []}
+ = Req,
+ %% Default errors field but a non-answer-message and only 3xxx
+ %% errors detected means diameter sets neither Result-Code nor
+ %% Failed-AVP.
+ {reply, #diameter_packet{msg = answer(Req, Caps)}};
+
+request(T, Req, Caps)
+ when T == send_double_error;
+ T == send_missing_avp ->
+ {reply, answer(Req, Caps)};
+
+request(send_ignore_missing_avp, Req, Caps) ->
+ {reply, #diameter_packet{msg = answer(Req, Caps),
+ errors = false}}. %% ignore errors
+
+answer(Req, Caps) ->
+ #diameter_base_STR{'Session-Id' = SId}
+ = Req,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}}
+ = Caps,
+ #diameter_base_STA{'Session-Id' = SId,
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Result-Code' = 2001}. %% SUCCESS
diff --git a/lib/diameter/test/diameter_app_SUITE.erl b/lib/diameter/test/diameter_app_SUITE.erl
index 53332af626..209f72adf1 100644
--- a/lib/diameter/test/diameter_app_SUITE.erl
+++ b/lib/diameter/test/diameter_app_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
@@ -48,7 +48,6 @@
diameter_dict_parser,
diameter_dict_util,
diameter_exprecs,
- diameter_nowarn,
diameter_make]).
-define(HELP_MODULES, [diameter_dbg,
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index ae128b8203..9e6619ecdd 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-2012. 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
@@ -27,11 +27,14 @@
-export([suite/0,
all/0,
groups/0,
+ init_per_group/2,
+ end_per_group/2,
init_per_testcase/2,
end_per_testcase/2]).
%% testcases
-export([start/1,
+ vendor_id/1,
start_services/1,
add_listeners/1,
s_no_common_application/1,
@@ -53,7 +56,6 @@
peer_down/4]).
-include("diameter.hrl").
--include("diameter_gen_base_rfc3588.hrl").
%% ===========================================================================
@@ -68,7 +70,7 @@
-define(HOST(Name), Name ++ "." ++ ?REALM).
%% Config for diameter:start_service/2.
--define(SERVICE(Name),
+-define(SERVICE,
[{'Origin-Realm', ?REALM},
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
@@ -78,8 +80,10 @@
| [{application, [{alias, A},
{dictionary, D},
{module, [?MODULE, A]}]}
- || {A,D} <- [{common, ?DIAMETER_DICT_COMMON},
- {accounting, ?DIAMETER_DICT_ACCOUNTING}]]]).
+ || {A,D} <- [{base3588, diameter_gen_base_rfc3588},
+ {acct3588, diameter_gen_base_accounting},
+ {base6733, diameter_gen_base_rfc6733},
+ {acct6733, diameter_gen_acct_rfc6733}]]]).
-define(A, list_to_atom).
-define(L, atom_to_list).
@@ -88,29 +92,29 @@
-define(caps, #diameter_caps).
-define(packet, #diameter_packet).
--define(cea, #diameter_base_CEA).
--define(answer_message, #'diameter_base_answer-message').
-
-define(fail(T), erlang:error({T, process_info(self(), messages)})).
-define(TIMEOUT, 10000).
+-define(DICTS, [rfc3588, rfc6733]).
+
%% ===========================================================================
suite() ->
[{timetrap, {seconds, 60}}].
all() -> [start,
+ vendor_id,
start_services,
- add_listeners,
- {group, all},
- {group, all, [parallel]},
- remove_listeners,
+ add_listeners]
+ ++ [{group, D, P} || D <- ?DICTS, P <- [[], [parallel]]]
+ ++ [remove_listeners,
stop_services,
stop].
groups() ->
- [{all, [], lists:flatmap(fun tc/1, tc())}].
+ Tc = lists:flatmap(fun tc/1, tc()),
+ [{D, [], Tc} || D <- ?DICTS].
%% Generate a unique hostname for each testcase so that watchdogs
%% don't prevent a connection from being brought up immediately.
@@ -118,14 +122,22 @@ init_per_testcase(Name, Config) ->
Uniq = ["." ++ integer_to_list(N) || N <- tuple_to_list(now())],
[{host, lists:flatten([?L(Name) | Uniq])} | Config].
+init_per_group(Name, Config) ->
+ [{rfc, Name} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
end_per_testcase(N, _)
when N == start;
+ N == vendor_id;
N == start_services;
N == add_listeners;
N == remove_listeners;
N == stop_services;
N == stop ->
ok;
+
end_per_testcase(Name, Config) ->
CRef = ?util:read_priv(Config, Name),
ok = diameter:remove_transport(?CLIENT, CRef).
@@ -147,22 +159,45 @@ tc() ->
start(_Config) ->
ok = diameter:start().
+%% Ensure that both integer and list-valued vendor id's can be
+%% configured in a 'Vendor-Specific-Application-Id, the arity having
+%% changed between RFC 3588 and RFC 6733.
+vendor_id(_Config) ->
+ [] = ?util:run([[fun vid/1, V] || V <- [1, [1], [1,2], x]]).
+
+vid(V) ->
+ RC = diameter:start_service(make_ref(),
+ [{'Vendor-Specific-Application-Id',
+ [[{'Vendor-Id', V}]]}
+ | ?SERVICE]),
+ vid(V, RC).
+
+vid(x, {error, _}) ->
+ ok;
+vid(_, ok) ->
+ ok.
+
start_services(_Config) ->
- ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER)),
- ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)).
+ ok = diameter:start_service(?SERVER, ?SERVICE),
+ ok = diameter:start_service(?CLIENT, ?SERVICE).
%% One server that responds only to base accounting, one that responds
%% to both this and the common application. Share a common service just
%% to simplify config, and because we can.
add_listeners(Config) ->
- Acct = listen(?SERVER,
- [{capabilities, [{'Origin-Host', ?HOST("acct-srv")},
- {'Auth-Application-Id', []}]},
- {applications, [accounting]},
- {capabilities_cb, [fun server_capx/3, acct]}]),
- Base = listen(?SERVER,
- [{capabilities, [{'Origin-Host', ?HOST("base-srv")}]},
- {capabilities_cb, [fun server_capx/3, base]}]),
+ Acct = [listen(?SERVER,
+ [{capabilities, [{'Origin-Host', ?HOST(H)},
+ {'Auth-Application-Id', []}]},
+ {applications, [A]},
+ {capabilities_cb, [fun server_capx/3, acct]}])
+ || {A,H} <- [{acct3588, "acct3588-srv"},
+ {acct6733, "acct6733-srv"}]],
+ Base = [listen(?SERVER,
+ [{capabilities, [{'Origin-Host', ?HOST(H)}]},
+ {applications, A},
+ {capabilities_cb, [fun server_capx/3, base]}])
+ || {A,H} <- [{[base3588, acct3588], "base3588-srv"},
+ {[base6733, acct6733], "base6733-srv"}]],
?util:write_priv(Config, ?MODULE, {Base, Acct}). %% lref/2 reads
remove_listeners(_Config) ->
@@ -195,8 +230,9 @@ c_no_common_application(Config) ->
client_closed(Config, "acct-srv", fun no_common_application/1, 5010).
no_common_application(Config) ->
+ [Common, _Acct] = apps(Config),
connect(Config, acct, [{capabilities, [{'Acct-Application-Id', []}]},
- {applications, [common]}]).
+ {applications, [Common]}]).
%% ====================
%% Ask the base server to speak accounting with an unknown security
@@ -209,9 +245,10 @@ c_no_common_security(Config) ->
client_closed(Config, "base-srv", fun no_common_security/1, 5017).
no_common_security(Config) ->
+ [Common, _Acct] = apps(Config),
connect(Config, base, [{capabilities, [{'Acct-Application-Id', []},
{'Inband-Security-Id', [17, 18]}]},
- {applications, [common]}]).
+ {applications, [Common]}]).
%% ====================
%% Have the base server reject a decent CER with the protocol error
@@ -221,18 +258,19 @@ s_unknown_peer(Config) ->
server_reject(Config, fun base/1, 3010).
c_unknown_peer(Config) ->
+ Dict0 = dict0(Config),
true = diameter:subscribe(?CLIENT),
- OH = ?HOST("base-srv"),
+ OH = host(Config, "base-srv"),
{CRef, _} = base(Config),
- {'CEA', ?caps{},
- ?packet{msg = ?answer_message{'Origin-Host' = OH,
- 'Result-Code' = 3010}}}
- = client_recv(CRef).
+ {'CEA', ?caps{}, ?packet{msg = Msg}} = client_recv(CRef),
+
+ ['diameter_base_answer-message' | _] = Dict0:'#get-'(Msg),
+ [OH, 3010] = Dict0:'#get-'(['Origin-Host', 'Result-Code'], Msg).
base(Config) ->
- connect(Config, base, []).
+ connect(Config, base, [{applications, apps(Config)}]).
%% ====================
%% Have the base server reject a decent CER with the non-protocol
@@ -266,18 +304,23 @@ s_client_reject(Config) ->
end.
c_client_reject(Config) ->
+ Dict0 = dict0(Config),
true = diameter:subscribe(?CLIENT),
- OH = ?HOST("acct-srv"),
+ OH = host(Config, "acct-srv"),
{CRef, _} = client_reject(Config),
{'CEA', {capabilities_cb, _, discard},
?caps{origin_host = {_, OH}},
- ?packet{msg = ?cea{'Result-Code' = 2001}}}
- = client_recv(CRef).
+ ?packet{msg = CEA}}
+ = client_recv(CRef),
+
+ [diameter_base_CEA | _] = Dict0:'#get-'(CEA),
+ [2001] = Dict0:'#get-'(['Result-Code'], CEA).
client_reject(Config) ->
- connect(Config, acct, [{capabilities_cb, fun client_capx/2}]).
+ connect(Config, acct, [{capabilities_cb, fun client_capx/2},
+ {applications, apps(Config)}]).
%% ===========================================================================
@@ -327,13 +370,21 @@ server_reject(Config, F, RC) ->
client_closed(Config, Host, F, RC) ->
true = diameter:subscribe(?CLIENT),
- OH = ?HOST(Host),
+ OH = host(Config, Host),
{CRef, _} = F(Config),
{'CEA', RC, ?caps{origin_host = {_, OH}}, ?packet{}}
= client_recv(CRef).
+srv(Config, Host) ->
+ "rfc" ++ N = atom_to_list(proplists:get_value(rfc, Config)),
+ [H, "srv" = S] = string:tokens(Host, "-"),
+ H ++ N ++ "-" ++ S.
+
+host(Config, Name) ->
+ ?HOST(srv(Config, Name)).
+
%% client_recv/1
client_recv(CRef) ->
@@ -364,6 +415,18 @@ client_capx(_, ?caps{origin_host = {[_,$_|"client_reject." ++ _], _}}) ->
%% ===========================================================================
+dict0(Config) ->
+ case proplists:get_value(rfc, Config) of
+ rfc3588 -> diameter_gen_base_rfc3588;
+ rfc6733 -> diameter_gen_base_rfc6733
+ end.
+
+apps(Config) ->
+ case proplists:get_value(rfc, Config) of
+ rfc3588 -> [base3588, acct3588];
+ rfc6733 -> [base6733, acct6733]
+ end.
+
host(Config) ->
{_, H} = lists:keyfind(host, 1, Config),
?HOST(H).
@@ -394,26 +457,32 @@ opts(PortNr, Opts) ->
{port, 0}]}
| Opts].
+lref(rfc3588, [LRef, _]) ->
+ LRef;
+lref(rfc6733, [_, LRef]) ->
+ LRef;
+
lref(Config, T) ->
- case ?util:read_priv(Config, ?MODULE) of
- {LRef, _} when T == base ->
- LRef;
- {_, LRef} when T == acct ->
- LRef
- end.
+ lref(proplists:get_value(rfc, Config),
+ case ?util:read_priv(Config, ?MODULE) of
+ {R, _} when T == base ->
+ R;
+ {_, R} when T == acct ->
+ R
+ end).
%% ===========================================================================
%% diameter callbacks
peer_up(?SERVER,
- {_, ?caps{origin_host = {"acct-srv." ++ _,
+ {_, ?caps{origin_host = {"acct" ++ _,
[_,$_|"client_reject." ++ _]}}},
State,
_) ->
State.
peer_down(?SERVER,
- {_, ?caps{origin_host = {"acct-srv." ++ _,
+ {_, ?caps{origin_host = {"acct" ++ _,
[_,$_|"client_reject." ++ _]}}},
State,
_) ->
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index fbd38067a8..dc8cbffc83 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.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
@@ -27,7 +27,8 @@
-include("diameter.hrl").
--define(BASE, diameter_gen_base_rfc3588).
+-define(RFC3588, diameter_gen_base_rfc3588).
+-define(RFC6733, diameter_gen_base_rfc6733).
-define(BOOL, [true, false]).
-define(A, list_to_atom).
@@ -158,7 +159,8 @@ gen(M, messages, {Name, Code, Flags, _, _}) ->
Name = case M:msg_name(Code, lists:member('REQ', Flags)) of
N when Name /= 'answer-message' ->
N;
- '' when Name == 'answer-message', M == ?BASE ->
+ '' when Name == 'answer-message', (M == ?RFC3588
+ orelse M == ?RFC6733) ->
Name
end,
[] = arity(M, Name, Rname);
diff --git a/lib/diameter/test/diameter_ct.erl b/lib/diameter/test/diameter_ct.erl
index ded50bf6c5..1697287a22 100644
--- a/lib/diameter/test/diameter_ct.erl
+++ b/lib/diameter/test/diameter_ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -23,16 +23,23 @@
%% Module used to run suites from Makefile.
%%
--export([run/1]).
+-export([run/1,
+ cover/0]).
%% The makefile looks for signs of failure so ignore the ct:run_test/1
%% return value.
-run([Suite]) ->
+run(Suites) ->
+ ct_run([{suite, Suites}]).
+
+cover() ->
+ ct_run([{spec, "./testspec"}]).
+
+ct_run(Opts) ->
Start = info(),
- ct:run_test([{suite, Suite},
- {logdir, "./log"},
- {auto_compile, false}]),
+ ct:run_test([{logdir, "./log"},
+ {auto_compile, false}
+ | Opts]),
info(Start , info()).
info() ->
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
new file mode 100644
index 0000000000..18bdcb1f54
--- /dev/null
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -0,0 +1,180 @@
+%%
+%% %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),
+ {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),
+ {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 ed31670031..dfd3253827 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-2012. 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,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,26 +85,27 @@
-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_LOGOUT').
+-define(MOVED, ?'DIAMETER_BASE_TERMINATION-CAUSE_USER_MOVED').
+-define(TIMEOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_SESSION_TIMEOUT').
%% ===========================================================================
@@ -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 2fde7b9fdb..51ccb1e6ec 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-2012. 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
@@ -355,7 +355,7 @@ 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_length_SUITE.erl b/lib/diameter/test/diameter_length_SUITE.erl
new file mode 100644
index 0000000000..ffb19d2288
--- /dev/null
+++ b/lib/diameter/test/diameter_length_SUITE.erl
@@ -0,0 +1,289 @@
+%%
+%% %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 transport_opt() length_errors.
+%%
+
+-module(diameter_length_SUITE).
+
+-export([suite/0,
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+%% testcases
+-export([start/1,
+ send/1,
+ stop/1]).
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/5,
+ prepare_request/4,
+ handle_answer/5,
+ handle_error/5,
+ handle_request/3]).
+
+-include("diameter.hrl").
+-include("diameter_gen_base_rfc3588.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+-define(CLIENT, "CLIENT").
+-define(SERVER, "SERVER").
+-define(REALM, "erlang.org").
+-define(HOST(Host, Realm), Host ++ [$.|Realm]).
+-define(DICT, diameter_gen_base_rfc3588).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Name),
+ [{'Origin-Host', Name ++ "." ++ ?REALM},
+ {'Origin-Realm', ?REALM},
+ {'Host-IP-Address', [{127,0,0,1}]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {application, [{dictionary, ?DICT},
+ {module, ?MODULE},
+ {answer_errors, callback}]}]).
+
+-define(SUCCESS,
+ ?'DIAMETER_BASE_RESULT-CODE_SUCCESS').
+-define(MISSING_AVP,
+ ?'DIAMETER_BASE_RESULT-CODE_MISSING_AVP').
+-define(INVALID_MESSAGE_LENGTH,
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_MESSAGE_LENGTH').
+
+-define(LOGOUT,
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
+
+-define(GROUPS, [exit, handle, discard]).
+
+-define(L, atom_to_list).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [{group, G} || G <- ?GROUPS].
+
+groups() ->
+ [{G, [], [start, send, stop]} || G <- ?GROUPS].
+
+init_per_suite(Config) ->
+ ok = diameter:start(),
+ Config.
+
+end_per_suite(_Config) ->
+ ok = diameter:stop().
+
+init_per_group(Group, Config) ->
+ [{group, Group} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
+init_per_testcase(_Name, Config) ->
+ Config.
+
+end_per_testcase(_, _) ->
+ ok.
+
+origin(exit) -> 0;
+origin(handle) -> 1;
+origin(discard) -> 2;
+
+origin(0) -> exit;
+origin(1) -> handle;
+origin(2) -> discard.
+
+%% ===========================================================================
+
+%% start/1
+
+start(Config) ->
+ Group = proplists:get_value(group, Config),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?L(Group))),
+ ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)),
+ LRef = ?util:listen(?SERVER,
+ tcp,
+ [{length_errors, Group}]),
+ ?util:connect(?CLIENT,
+ tcp,
+ LRef,
+ [{capabilities, [{'Origin-State-Id', origin(Group)}]}]).
+
+%% stop/1
+
+stop(_Config) ->
+ ok = diameter:remove_transport(?CLIENT, true),
+ ok = diameter:remove_transport(?SERVER, true),
+ ok = diameter:stop_service(?SERVER),
+ ok = diameter:stop_service(?CLIENT).
+
+%% send/1
+
+%% Server transport exits on messages of insuffient length.
+send(exit) ->
+ %% Transport exit is followed by failover but there's only one
+ %% transport to choose from.
+ {error, failover} = call(4);
+
+%% Server transport receives messages of insufficient length.
+send(handle) ->
+ %% Message Length too large: diameter_tcp flushes the request
+ %% when no additional bytes arrive.
+ #diameter_base_STA{'Result-Code' = ?INVALID_MESSAGE_LENGTH}
+ = call(4),
+ %% Another request answered as it should.
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(0),
+ %% Message Length conveniently small: the trailing optional
+ %% Origin-State-Id isn't included in the received request.
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(-12),
+ %% Server receives Origin-State-Id AVP as the first 12 bytes of
+ %% the next request: AVP <<Code:32, Flags:8, Len:24, Data:32>> is
+ %% interpreted as header <<Version:8, Len:24, Flags:8, Code:24,
+ %% ApplId: 32>>. In particular, the AVP Length 12 = 00001100 is
+ %% interpreted as Command Flags, so R=0 and the request is
+ %% interpreted as an unsolicited answer. Increase Message Length
+ %% to have the server receive all bytes sent thusfar.
+ {error, timeout}
+ = call(12),
+ %% Another request answered as it should.
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(0),
+ %% Shorten Message Length so much that that the server doesn't
+ %% receive the required Termination-Cause AVP.
+ #diameter_base_STA{'Result-Code' = ?MISSING_AVP}
+ = call(-24);
+
+%% Server transport discards message of insufficient length.
+send(discard) ->
+ %% First request times out when the server discards it but a
+ %% second succeeds since the transport remains up.
+ {error, timeout}
+ = call(4),
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(0);
+
+send(Config) ->
+ send(proplists:get_value(group, Config)).
+
+%% ===========================================================================
+
+call(Delta) ->
+ diameter:call(?CLIENT,
+ ?DICT,
+ #diameter_base_STR
+ {'Termination-Cause' = ?LOGOUT,
+ 'Auth-Application-Id' = ?DIAMETER_APP_ID_COMMON,
+ 'Origin-State-Id' = [7]},
+ [{extra, [Delta]}]).
+
+%% ===========================================================================
+%% diameter callbacks
+
+%% peer_up/3
+
+peer_up(_SvcName, _Peer, State) ->
+ State.
+
+%% peer_down/3
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%% pick_peer/5
+
+pick_peer([Peer], _, ?CLIENT, _State, _Delta) ->
+ {ok, Peer}.
+
+%% prepare_request/4
+
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Delta) ->
+ {send, resize(Delta, prepare(Pkt, Caps))}.
+
+prepare(#diameter_packet{msg = Req0} = Pkt, Caps) ->
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, DR}}
+ = Caps,
+ Req = Req0#diameter_base_STR{'Session-Id' = diameter:session_id(OH),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Destination-Realm' = DR},
+ diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}).
+
+resize(0, Pkt) ->
+ Pkt;
+resize(Delta, #diameter_packet{bin = Bin} = Pkt) ->
+ Pkt#diameter_packet{bin = resize(Delta, Bin)};
+
+resize(Delta, <<V, Len:24, T/binary>>) ->
+ <<V, (Len + Delta):24, T/binary>>.
+
+%% handle_answer/5
+
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Delta) ->
+ Pkt#diameter_packet.msg.
+
+%% handle_error/5
+
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Delta) ->
+ {error, Reason}.
+
+%% handle_request/3
+
+handle_request(Pkt, ?SERVER, {_Ref, Caps}) ->
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _},
+ origin_state_id = {_,[Id]}}
+ = Caps,
+ answer(origin(Id),
+ Pkt,
+ #diameter_base_STA{'Result-Code' = ?SUCCESS,
+ 'Session-Id' = diameter:session_id(OH),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR}).
+
+answer(Group, #diameter_packet{errors = Es}, Ans) ->
+ answer(Group, Es, Ans);
+
+%% No errors: just answer.
+answer(_, [], Ans) ->
+ {reply, Ans};
+
+%% Otherwise an invalid length should only reach the callback if
+%% length_errors = handle.
+answer(Group, [RC|_], Ans)
+ when RC == ?INVALID_MESSAGE_LENGTH, Group == handle;
+ RC /= ?INVALID_MESSAGE_LENGTH ->
+ {reply, Ans}.
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index f10d82bdf8..735a908d97 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-2012. 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
@@ -107,7 +107,7 @@
-define(LOOP_DETECTED, 3005).
-define(UNABLE_TO_DELIVER, 3002).
--define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
-define(AUTHORIZE_ONLY, ?'DIAMETER_BASE_RE-AUTH-REQUEST-TYPE_AUTHORIZE_ONLY').
%% ===========================================================================
diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl
index 8b7d8cb1b6..76ff764671 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-2012. 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
@@ -33,6 +33,7 @@
-export([reg/1,
incr/1,
read/1,
+ sum/1,
flush/1]).
-define(stat, diameter_stats).
@@ -53,6 +54,7 @@ tc() ->
[reg,
incr,
read,
+ sum,
flush].
init_per_suite(Config) ->
@@ -69,7 +71,7 @@ reg(_) ->
true = ?stat:reg(Ref),
false = ?stat:reg(Ref). %% duplicate
-incr(_) ->
+incr(_) ->
Ref = '_',
Ctr = x,
false = ?stat:incr(Ctr), %% not registered,
@@ -98,6 +100,23 @@ read(_) ->
[] = ?stat:read([make_ref()]),
?stat:flush([self(), Ref, make_ref()]).
+sum(_) ->
+ Ref = make_ref(),
+ C1 = {a,b},
+ C2 = {b,a},
+ true = ?stat:reg(Ref),
+ 1 = ?stat:incr(C1),
+ 1 = ?stat:incr(C2),
+ 2 = ?stat:incr(C2),
+ 7 = ?stat:incr(C1, Ref, 7),
+ [{Ref, [{C1,8}, {C2,2}]}]
+ = ?stat:sum([Ref, make_ref()]),
+ Self = self(),
+ [{Self, [{C1,1}, {C2,2}]}]
+ = ?stat:sum([self()]),
+ [{Ref, [{C1,7}]}, {Self, [{C1,1}, {C2,2}]}]
+ = lists:sort(?stat:flush([self(), Ref])).
+
flush(_) ->
Ref = make_ref(),
Ctr = '_',
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 6cc34b20c5..77194a0f56 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-2012. 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
@@ -122,7 +122,7 @@
{capabilities, Caps}]}).
-define(SUCCESS, 2001).
--define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
%% ===========================================================================
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 142b28a4c2..781ed234cc 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -40,6 +40,7 @@
send_nok/1,
send_eval/1,
send_bad_answer/1,
+ send_protocol_error/1,
send_arbitrary/1,
send_unknown/1,
send_unknown_mandatory/1,
@@ -48,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,
@@ -76,7 +80,9 @@
send_multiple_filters_2/1,
send_multiple_filters_3/1,
send_anything/1,
+ outstanding/1,
remove_transports/1,
+ empty/1,
stop_services/1,
stop/1]).
@@ -93,11 +99,21 @@
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
-include("diameter_gen_base_accounting.hrl").
+%% The listening transports use RFC 3588 dictionaries, the client
+%% transports use either 3588 or 6733. (So can't use the record
+%% definitions in the latter case.)
%% ===========================================================================
-define(util, diameter_util).
+-define(A, list_to_atom).
+-define(L, atom_to_list).
+
+%% Don't use is_record/2 since dictionary hrl's aren't included.
+%% (Since they define conflicting reqcords with the same names.)
+-define(is_record(Rec, Name), (Name == element(1, Rec))).
+
-define(ADDR, {127,0,0,1}).
-define(CLIENT, "CLIENT").
@@ -107,23 +123,42 @@
-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
-%% Run tests cases in different encoding variants. Send outgoing
-%% messages as lists or records.
+%% How to construct messages, as record or list.
-define(ENCODINGS, [list, record]).
-%% Identifers for client connections.
--define(CONNECTIONS, [c1,c2,c3]).
+%% How to send answers, in a diameter_packet or not.
+-define(CONTAINERS, [pkt, msg]).
+
+%% Which common dictionary to use in the clients.
+-define(RFCS, [rfc3588, rfc6733]).
+
+-record(group,
+ {client_encoding,
+ client_dict0,
+ server_encoding,
+ server_container}).
%% 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).
+
+%% A common match when receiving answers in a client.
+-define(answer_message(SessionId, ResultCode),
+ ['answer-message',
+ {'Session-Id', SessionId},
+ {'Origin-Host', _},
+ {'Origin-Realm', _},
+ {'Result-Code', ResultCode}
+ | _]).
+-define(answer_message(ResultCode),
+ ?answer_message(_, ResultCode)).
+
%% Config for diameter:start_service/2.
-define(SERVICE(Name),
[{'Origin-Host', Name ++ "." ++ ?REALM},
@@ -137,28 +172,33 @@
| [{application, [{dictionary, D},
{module, ?MODULE},
{answer_errors, callback}]}
- || D <- [?BASE, ?ACCT]]]).
+ || D <- [diameter_gen_base_rfc3588,
+ diameter_gen_base_accounting,
+ diameter_gen_base_rfc6733,
+ diameter_gen_acct_rfc6733]]]).
-define(SUCCESS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
+ ?'DIAMETER_BASE_RESULT-CODE_SUCCESS').
-define(COMMAND_UNSUPPORTED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_COMMAND_UNSUPPORTED').
+ ?'DIAMETER_BASE_RESULT-CODE_COMMAND_UNSUPPORTED').
-define(TOO_BUSY,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_TOO_BUSY').
+ ?'DIAMETER_BASE_RESULT-CODE_TOO_BUSY').
-define(APPLICATION_UNSUPPORTED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_APPLICATION_UNSUPPORTED').
+ ?'DIAMETER_BASE_RESULT-CODE_APPLICATION_UNSUPPORTED').
-define(INVALID_HDR_BITS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_HDR_BITS').
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_HDR_BITS').
-define(INVALID_AVP_BITS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_BITS').
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_AVP_BITS').
-define(AVP_UNSUPPORTED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_AVP_UNSUPPORTED').
+ ?'DIAMETER_BASE_RESULT-CODE_AVP_UNSUPPORTED').
-define(UNSUPPORTED_VERSION,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNSUPPORTED_VERSION').
+ ?'DIAMETER_BASE_RESULT-CODE_UNSUPPORTED_VERSION').
-define(REALM_NOT_SERVED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_REALM_NOT_SERVED').
+ ?'DIAMETER_BASE_RESULT-CODE_REALM_NOT_SERVED').
-define(UNABLE_TO_DELIVER,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNABLE_TO_DELIVER').
+ ?'DIAMETER_BASE_RESULT-CODE_UNABLE_TO_DELIVER').
+-define(INVALID_AVP_LENGTH,
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_AVP_LENGTH').
-define(EVENT_RECORD,
?'DIAMETER_BASE_ACCOUNTING-RECORD-TYPE_EVENT_RECORD').
@@ -168,14 +208,11 @@
?'DIAMETER_BASE_RE-AUTH-REQUEST-TYPE_AUTHORIZE_AUTHENTICATE').
-define(LOGOUT,
- ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
-define(BAD_ANSWER,
- ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_BAD_ANSWER').
-
--define(A, list_to_atom).
--define(L, atom_to_list).
-
--define(NAME(A,B), ?A(?L(A) ++ "," ++ ?L(B))).
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_BAD_ANSWER').
+-define(USER_MOVED,
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_USER_MOVED').
%% ===========================================================================
@@ -184,17 +221,27 @@ suite() ->
all() ->
[start, start_services, add_transports, result_codes]
- ++ [{group, ?util:name([E,C]), P} || E <- ?ENCODINGS,
- C <- ?CONNECTIONS,
- P <- [[], [parallel]]]
- ++ [remove_transports, stop_services, stop].
+ ++ [{group, ?util:name([R,D,A,C]), P} || R <- ?ENCODINGS,
+ D <- ?RFCS,
+ A <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ P <- [[], [parallel]]]
+ ++ [outstanding, remove_transports, empty, stop_services, stop].
groups() ->
Ts = tc(),
- [{?util:name([E,C]), [], Ts} || E <- ?ENCODINGS, C <- ?CONNECTIONS].
+ [{?util:name([R,D,A,C]), [], Ts} || R <- ?ENCODINGS,
+ D <- ?RFCS,
+ A <- ?ENCODINGS,
+ C <- ?CONTAINERS].
init_per_group(Name, Config) ->
- [{group, Name} | Config].
+ [R,D,A,C] = ?util:name(Name),
+ G = #group{client_encoding = R,
+ client_dict0 = dict0(D),
+ server_encoding = A,
+ server_container = C},
+ [{group, G} | Config].
end_per_group(_, _) ->
ok.
@@ -212,6 +259,7 @@ tc() ->
send_nok,
send_eval,
send_bad_answer,
+ send_protocol_error,
send_arbitrary,
send_unknown,
send_unknown_mandatory,
@@ -220,6 +268,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,
@@ -261,10 +312,33 @@ start_services(_Config) ->
| ?SERVICE(?CLIENT)]).
add_transports(Config) ->
- LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx/2}]),
- Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C}]) || C <- ?CONNECTIONS],
+ LRef = ?util:listen(?SERVER,
+ tcp,
+ [{capabilities_cb, fun capx/2},
+ {applications, apps(rfc3588)}]),
+ Cs = [?util:connect(?CLIENT,
+ tcp,
+ LRef,
+ [{id, Id},
+ {capabilities, [{'Origin-State-Id', origin(Id)}]},
+ {applications, apps(D)}])
+ || A <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ D <- ?RFCS,
+ Id <- [{A,C}]],
+ %% The server uses the client's Origin-State-Id to decide how to
+ %% answer.
?util:write_priv(Config, "transport", [LRef | Cs]).
+apps(D0) ->
+ D = dict0(D0),
+ [acct(D), D].
+
+%% Ensure there are no outstanding requests in request table.
+outstanding(_Config) ->
+ [] = [T || T <- ets:tab2list(diameter_request),
+ is_atom(element(1,T))].
+
remove_transports(Config) ->
[LRef | Cs] = ?util:read_priv(Config, "transport"),
[?util:disconnect(?CLIENT, C, ?SERVER, LRef) || C <- Cs].
@@ -273,6 +347,10 @@ stop_services(_Config) ->
ok = diameter:stop_service(?CLIENT),
ok = diameter:stop_service(?SERVER).
+%% Ensure even transports have been removed from request table.
+empty(_Config) ->
+ [] = ets:tab2list(diameter_request).
+
stop(_Config) ->
ok = diameter:stop().
@@ -284,7 +362,7 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) ->
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
- {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011}
+ {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
?UNABLE_TO_DELIVER,
@@ -294,22 +372,23 @@ result_codes(_Config) ->
?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_accounting_ACA{'Result-Code' = ?SUCCESS}
+
+ ['ACA', _SessionId, {'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}
+
+ ?answer_message(?INVALID_AVP_BITS)
= call(Config, Req).
%% Send an ACR and expect success.
@@ -317,7 +396,7 @@ send_eval(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 3}],
- #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
+ ['ACA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req).
%% Send an accounting ACR that the server tries to answer with an
@@ -328,43 +407,50 @@ send_bad_answer(Config) ->
{'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}],
+
+ ?answer_message(?TOO_BUSY)
+ = call(Config, Req).
+
%% Send an ASR with an arbitrary AVP and expect success and the same
%% AVP in the reply.
send_arbitrary(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{name = 'Class', value = "XXX"}]}],
- #diameter_base_ASA{'Result-Code' = ?SUCCESS,
- 'AVP' = Avps}
+ ['ASA', _SessionId, {'Result-Code', ?SUCCESS} | Avps]
= call(Config, Req),
- [#diameter_avp{name = 'Class',
- value = "XXX"}]
- = Avps.
+ {'AVP', [#diameter_avp{name = 'Class',
+ value = "XXX"}]}
+ = lists:last(Avps).
%% Send an unknown AVP (to some client) and check that it comes back.
send_unknown(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = false,
data = <<17>>}]}],
- #diameter_base_ASA{'Result-Code' = ?SUCCESS,
- 'AVP' = Avps}
+ ['ASA', _SessionId, {'Result-Code', ?SUCCESS} | Avps]
= call(Config, Req),
- [#diameter_avp{code = 999,
- is_mandatory = false,
- data = <<17>>}]
- = Avps.
+ {'AVP', [#diameter_avp{code = 999,
+ is_mandatory = false,
+ data = <<17>>}]}
+ = lists:last(Avps).
%% Ditto but set the M flag.
send_unknown_mandatory(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = true,
data = <<17>>}]}],
- #diameter_base_ASA{'Result-Code' = ?AVP_UNSUPPORTED,
- 'Failed-AVP' = Failed}
+ ['ASA', _SessionId, {'Result-Code', ?AVP_UNSUPPORTED} | Avps]
= call(Config, Req),
- [#'diameter_base_Failed-AVP'{'AVP' = Avps}] = Failed,
+ [#'diameter_base_Failed-AVP'{'AVP' = As}]
+ = proplists:get_value('Failed-AVP', Avps),
[#diameter_avp{code = 999,
is_mandatory = true,
data = <<17>>}]
- = Avps.
+ = As.
%% Send an STR that the server ignores.
send_noreply(Config) ->
@@ -374,32 +460,55 @@ send_noreply(Config) ->
%% Send an unsupported command and expect 3001.
send_unsupported(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
- #'diameter_base_answer-message'{'Result-Code' = ?COMMAND_UNSUPPORTED}
+ ?answer_message(?COMMAND_UNSUPPORTED)
= call(Config, Req).
%% 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}
+ ?answer_message(?APPLICATION_UNSUPPORTED)
= call(Config, Req).
%% Send a request with the E bit set and expect 3008.
send_error_bit(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
- #'diameter_base_answer-message'{'Result-Code' = ?INVALID_HDR_BITS}
+ ?answer_message(?INVALID_HDR_BITS)
= call(Config, Req).
%% Send a bad version and check that we get 5011.
send_unsupported_version(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- #diameter_base_STA{'Result-Code' = ?UNSUPPORTED_VERSION}
+ ['STA', _SessionId, {'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}],
+
+ ?answer_message(?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}],
+
+ ['STA', _SessionId, {'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}],
+
+ ?answer_message(?TOO_BUSY)
= call(Config, Req).
%% Send something long that will be fragmented by TCP.
send_long(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'User-Name', [lists:duplicate(1 bsl 20, $X)]}],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req).
%% Send something for which pick_peer finds no suitable peer.
@@ -424,14 +533,14 @@ send_any_1(Config) ->
send_any_2(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
- #'diameter_base_answer-message'{'Result-Code' = ?UNABLE_TO_DELIVER}
+ ?answer_message(?UNABLE_TO_DELIVER)
= call(Config, Req, [{filter, {any, [host, realm]}}]).
%% Send with a conjunctive filter.
send_all_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM),
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req, [{filter, {all, [{host, any},
{realm, Realm}]}}]).
send_all_2(Config) ->
@@ -449,8 +558,7 @@ send_timeout(Config) ->
%% received the Session-Id.
send_error(Config) ->
Req = ['RAR', {'Re-Auth-Request-Type', ?AUTHORIZE_AUTHENTICATE}],
- #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY,
- 'Session-Id' = SId}
+ ?answer_message(SId, ?TOO_BUSY)
= call(Config, Req),
undefined /= SId.
@@ -460,10 +568,9 @@ send_detach(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
Ref = make_ref(),
ok = call(Config, Req, [{extra, [{self(), Ref}]}, detach]),
- #diameter_packet{msg = Rec, errors = []}
- = receive {Ref, T} -> T after 2000 -> false end,
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
- = Rec.
+ Ans = receive {Ref, T} -> T after 2000 -> false end,
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
+ = Ans.
%% Send a request which can't be encoded and expect {error, encode}.
send_encode_error(Config) ->
@@ -473,11 +580,11 @@ send_encode_error(Config) ->
send_destination_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(?SERVER, ?REALM)]}],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req, [{filter, {all, [host, realm]}}]).
send_destination_2(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req, [{filter, {all, [host, realm]}}]).
%% Send with filtering on and expect failure when specifying an
@@ -498,12 +605,12 @@ send_destination_4(Config) ->
send_destination_5(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Realm', "unknown.org"}],
- #'diameter_base_answer-message'{'Result-Code' = ?REALM_NOT_SERVED}
+ ?answer_message(?REALM_NOT_SERVED)
= call(Config, Req).
send_destination_6(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
- #'diameter_base_answer-message'{'Result-Code' = ?UNABLE_TO_DELIVER}
+ ?answer_message(?UNABLE_TO_DELIVER)
= call(Config, Req).
%% Specify an invalid option and expect failure.
@@ -537,7 +644,7 @@ send_bad_filter(Config, F) ->
%% Specify multiple filter options and expect them be conjunctive.
send_multiple_filters_1(Config) ->
Fun = fun(#diameter_caps{}) -> true end,
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= send_multiple_filters(Config, [host, {eval, Fun}]).
send_multiple_filters_2(Config) ->
E = {erlang, is_tuple, []},
@@ -548,7 +655,7 @@ send_multiple_filters_3(Config) ->
E2 = {erlang, is_tuple, []},
E3 = {erlang, is_record, [diameter_caps]},
E4 = [{erlang, is_record, []}, diameter_caps],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]).
send_multiple_filters(Config, Fs) ->
@@ -559,7 +666,7 @@ send_multiple_filters(Config, Fs) ->
%% only the return value from the prepare_request callback being
%% significant.
send_anything(Config) ->
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, anything).
%% ===========================================================================
@@ -569,33 +676,69 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- [Encoding, Client] = ?util:name(proplists:get_value(group, Config)),
+ #group{client_encoding = ReqEncoding,
+ client_dict0 = Dict0}
+ = Group
+ = proplists:get_value(group, Config),
diameter:call(?CLIENT,
- dict(Req),
- req(Req, Encoding),
- [{extra, [Name, Client]} | Opts]).
-
-req(['ACR' = H | T], record) ->
- ?ACCT:'#new-'(?ACCT:msg2rec(H), T);
-req([H|T], record) ->
- ?BASE:'#new-'(?BASE:msg2rec(H), T);
-req(T, _) ->
- T.
-
-dict(['ACR' | _]) ->
- ?ACCT;
-dict(_) ->
- ?BASE.
+ dict(Req, Dict0),
+ msg(Req, ReqEncoding, Dict0),
+ [{extra, [Name, Group]} | Opts]).
+
+origin({A,C}) ->
+ 2*codec(A) + container(C);
+
+origin(N) ->
+ {codec(N band 2), container(N rem 2)}.
+
+%% Map booleans, but the readable atoms are part of (constructed)
+%% group names, so it's good that they're readable.
+
+codec(record) -> 0;
+codec(list) -> 1;
+codec(0) -> record;
+codec(_) -> list.
+
+container(pkt) -> 0;
+container(msg) -> 1;
+container(0) -> pkt;
+container(_) -> msg.
+
+msg([H|_] = Msg, record = E, diameter_gen_base_rfc3588)
+ when H == 'ACR';
+ H == 'ACA' ->
+ msg(Msg, E, diameter_gen_base_accounting);
+msg([H|_] = Msg, record = E, diameter_gen_base_rfc6733)
+ when H == 'ACR';
+ H == 'ACA' ->
+ msg(Msg, E, diameter_gen_acct_rfc6733);
+msg([H|T], record, Dict) ->
+ Dict:'#new-'(Dict:msg2rec(H), T);
+msg(Msg, _, _) ->
+ Msg.
+
+dict0(D) ->
+ ?A("diameter_gen_base_" ++ ?L(D)).
+
+dict(Msg, Dict0)
+ when 'ACR' == hd(Msg);
+ 'ACA' == hd(Msg);
+ ?is_record(Msg, diameter_base_accounting_ACR);
+ ?is_record(Msg, diameter_base_accounting_ACA) ->
+ acct(Dict0);
+dict(_, Dict0) ->
+ Dict0.
+
+acct(diameter_gen_base_rfc3588) ->
+ diameter_gen_base_accounting;
+acct(diameter_gen_base_rfc6733) ->
+ diameter_gen_acct_rfc6733.
%% Set only values that aren't already.
-set([H|T], Vs) ->
+set(_, [H|T], Vs) ->
[H | Vs ++ T];
-set(#diameter_base_accounting_ACR{} = Rec, Vs) ->
- set(Rec, Vs, ?ACCT);
-set(Rec, Vs) ->
- set(Rec, Vs, ?BASE).
-
-set(Rec, Vs, Dict) ->
+set(#group{client_dict0 = Dict0} = _Group, Rec, Vs) ->
+ Dict = dict(Rec, Dict0),
lists:foldl(fun({F,_} = FV, A) ->
set(Dict, Dict:'#get-'(F, A), FV, A)
end,
@@ -624,17 +767,18 @@ peer_down(_SvcName, _Peer, State) ->
%% pick_peer/6-7
-pick_peer(Peers, _, ?CLIENT, _State, Name, Id)
+pick_peer(Peers, _, ?CLIENT, _State, Name, Group)
when Name /= send_detach ->
- find(Id, Peers).
+ find(Group, Peers).
pick_peer(_Peers, _, ?CLIENT, _State, send_nopeer, _, ?EXTRA) ->
false;
-pick_peer(Peers, _, ?CLIENT, _State, send_detach, Id, {_,_}) ->
- find(Id, Peers).
+pick_peer(Peers, _, ?CLIENT, _State, send_detach, Group, {_,_}) ->
+ find(Group, Peers).
-find(Id, Peers) ->
+find(#group{server_encoding = A, server_container = C}, Peers) ->
+ Id = {A,C},
[P] = [P || P <- Peers, id(Id, P)],
{ok, P}.
@@ -648,115 +792,174 @@ id(Id, {Pid, _Caps}) ->
prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard, _) ->
{discard, unprepared};
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, _) ->
- {send, prepare(Pkt, Caps, Name)}.
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, Group) ->
+ {send, prepare(Pkt, Caps, Name, Group)}.
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _, _) ->
- {eval_packet, {send, prepare(Pkt, Caps)}, [fun log/2, detach]}.
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, Group, _) ->
+ {eval_packet, {send, prepare(Pkt, Caps, Group)}, [fun log/2, detach]}.
-log(#diameter_packet{} = P, T) ->
+log(#diameter_packet{bin = Bin} = P, T)
+ when is_binary(Bin) ->
io:format("~p: ~p~n", [T,P]).
-prepare(Pkt, Caps, send_unsupported) ->
- Req = prepare(Pkt, Caps),
+%% prepare/4
+
+prepare(Pkt, Caps, send_invalid_avp_bits, #group{client_dict0 = Dict0}
+ = Group) ->
+ Req = prepare(Pkt, Caps, Group),
+ %% 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(Dict0, 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, #group{client_dict0 = Dict0} = Group)
+ when N == send_invalid_avp_length;
+ N == send_invalid_reject ->
+ Req = prepare(Pkt, Caps, Group),
+ %% 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(Dict0, 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, #group{client_dict0 = Dict0} = Group) ->
+ Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
= E
- = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ = diameter_codec:encode(Dict0, 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),
+prepare(Pkt, Caps, send_unsupported_app, #group{client_dict0 = Dict0}
+ = Group) ->
+ Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
= E
- = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
- E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>};
+ = diameter_codec:encode(Dict0, Pkt#diameter_packet{msg = Req}),
+ E#diameter_packet{bin = <<H/binary, ?BAD_APP:32/integer, T/binary>>};
-prepare(Pkt, Caps, send_error_bit) ->
+prepare(Pkt, Caps, send_error_bit, Group) ->
#diameter_packet{header = Hdr} = Pkt,
Pkt#diameter_packet{header = Hdr#diameter_header{is_error = true},
- msg = prepare(Pkt, Caps)};
+ msg = prepare(Pkt, Caps, Group)};
-prepare(Pkt, Caps, send_unsupported_version) ->
+prepare(Pkt, Caps, send_unsupported_version, Group) ->
#diameter_packet{header = Hdr} = Pkt,
Pkt#diameter_packet{header = Hdr#diameter_header{version = 42},
- msg = prepare(Pkt, Caps)};
+ msg = prepare(Pkt, Caps, Group)};
-prepare(Pkt, Caps, send_anything) ->
+prepare(Pkt, Caps, send_anything, Group) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- prepare(Pkt#diameter_packet{msg = Req}, Caps);
+ prepare(Pkt#diameter_packet{msg = Req}, Caps, Group);
+
+prepare(Pkt, Caps, _Name, Group) ->
+ prepare(Pkt, Caps, Group).
-prepare(Pkt, Caps, _Name) ->
- prepare(Pkt, Caps).
+%% prepare/3
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_accounting_ACR);
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_accounting_ACR);
'ACR' == hd(Req) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Realm', DR}]);
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Realm', DR}]);
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_ASR);
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_ASR);
'ASR' == hd(Req) ->
#diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR},
- {'Auth-Application-Id', ?APP_ID}]);
-
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_STR);
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR},
+ {'Auth-Application-Id', ?APP_ID}]);
+
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_STR);
'STR' == hd(Req) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Realm', DR},
- {'Auth-Application-Id', ?APP_ID}]);
-
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_RAR);
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Realm', DR},
+ {'Auth-Application-Id', ?APP_ID}]);
+
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_RAR);
'RAR' == hd(Req) ->
#diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR},
- {'Auth-Application-Id', ?APP_ID}]).
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR},
+ {'Auth-Application-Id', ?APP_ID}]).
%% prepare_retransmit/5
-prepare_retransmit(_Pkt, false, _Peer, _Name, _Id) ->
+prepare_retransmit(_Pkt, false, _Peer, _Name, _Group) ->
discard.
%% handle_answer/6-7
-handle_answer(Pkt, Req, ?CLIENT, Peer, Name, _Id) ->
- answer(Pkt, Req, Peer, Name).
-
-handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, _Id, {Pid, Ref}) ->
- Pid ! {Ref, Pkt}.
-
-answer(#diameter_packet{msg = Rec, errors = []}, _Req, _Peer, _) ->
+handle_answer(Pkt, Req, ?CLIENT, Peer, Name, Group) ->
+ answer(Pkt, Req, Peer, Name, Group).
+
+handle_answer(Pkt, Req, ?CLIENT, Peer, send_detach = Name, Group, X) ->
+ {Pid, Ref} = X,
+ Pid ! {Ref, answer(Pkt, Req, Peer, Name, Group)}.
+
+answer(Pkt, Req, _Peer, Name, #group{client_dict0 = Dict0}) ->
+ #diameter_packet{header = H, msg = Ans, errors = Es} = Pkt,
+ ApplId = app(Req, Name, Dict0),
+ #diameter_header{application_id = ApplId} = H, %% assert
+ Dict = dict(Ans, Dict0),
+ [R | Vs] = Dict:'#get-'(answer(Ans, Es, Name)),
+ [Dict:rec2msg(R) | Vs].
+
+answer(Rec, [_|_], N)
+ when N == send_invalid_avp_bits;
+ N == send_invalid_avp_length;
+ N == send_invalid_reject ->
+ Rec;
+answer(Rec, [], _) ->
Rec.
+app(_, send_unsupported_app, _) ->
+ ?BAD_APP;
+app(Req, _, Dict0) ->
+ Dict = dict(Req, Dict0),
+ Dict:id().
+
%% handle_error/6
-handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Id) ->
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Group) ->
{error, Reason}.
%% handle_request/3
@@ -771,7 +974,18 @@ handle_request(#diameter_packet{header = H, msg = M}, ?SERVER, {_Ref, Caps}) ->
{V,B} = ?CLIENT_MASK,
V = EI bsr B, %% assert
V = HI bsr B, %%
- request(M, Caps).
+ #diameter_caps{origin_state_id = {_,[Id]}} = Caps,
+ answer(origin(Id), request(M, Caps)).
+
+answer(T, {Tag, Action, Post}) ->
+ {Tag, answer(T, Action), Post};
+answer({A,C}, {reply, Ans}) ->
+ answer(C, {reply, msg(Ans, A, diameter_gen_base_rfc3588)});
+answer(pkt, {reply, Ans})
+ when not is_record(Ans, diameter_packet) ->
+ {reply, #diameter_packet{msg = Ans}};
+answer(_, T) ->
+ T.
%% send_nok
request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0},
@@ -791,7 +1005,7 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}],
- {reply, #diameter_packet{header = #diameter_header{is_error = true},%% not
+ {reply, #diameter_packet{header = #diameter_header{is_error = true},%% NOT
msg = Ans}};
%% send_eval
@@ -821,15 +1035,29 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}]};
+%% send_protocol_error
+request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4},
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}) ->
+ Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR}],
+ {reply, Ans};
+
request(#diameter_base_ASR{'Session-Id' = SId,
'AVP' = Avps},
#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}};
+ {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},
@@ -838,13 +1066,13 @@ request(#diameter_base_STR{'Termination-Cause' = T},
discard;
%% send_destination_5
-request(#diameter_base_STR{'Destination-Realm'= R},
+request(#diameter_base_STR{'Destination-Realm' = R},
#diameter_caps{origin_realm = {OR, _}})
when R /= undefined, R /= OR ->
{protocol_error, ?REALM_NOT_SERVED};
%% send_destination_6
-request(#diameter_base_STR{'Destination-Host'= [H]},
+request(#diameter_base_STR{'Destination-Host' = [H]},
#diameter_caps{origin_host = {OH, _}})
when H /= OH ->
{protocol_error, ?UNABLE_TO_DELIVER};
@@ -852,10 +1080,10 @@ request(#diameter_base_STR{'Destination-Host'= [H]},
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}]};
%% send_error
request(#diameter_base_RAR{}, _Caps) ->
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 7ce09e93ca..704bf110c7 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-2012. 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
@@ -30,10 +30,14 @@
end_per_suite/1]).
%% testcases
--export([reopen/1, reopen/4, reopen/7]).
+-export([reopen/0, reopen/1, reopen/4, reopen/6,
+ suspect/1, suspect/4,
+ okay/1, okay/4]).
-export([id/1, %% jitter callback
- run1/1]).
+ run1/1,
+ abuse/1,
+ abuse/2]).
%% diameter_app callbacks
-export([peer_up/3,
@@ -64,7 +68,7 @@
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 42},
{'Product-Name', "OTP/diameter"},
- {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {'Auth-Application-Id', [0 = ?BASE:id()]},
{application, [{alias, Name},
{dictionary, ?BASE},
{module, ?MODULE}]}]).
@@ -72,48 +76,51 @@
%% Watchdog timer as a callback.
-define(WD(T), {?MODULE, id, [T]}).
-%% Watchdog timers used by the testcases. Note that the short timeout
-%% with random jitter is excluded since the reopen/1 isn't smart
-%% enough to deal with it: see ONE_WD below.
--define(WD_TIMERS, [?WD(6000)
- | [F_(T_) || T_ <- [10000, 20000, 30000],
- F_ <- [fun(T__) -> T__ end,
- fun(T__) -> ?WD(T__) end]]]).
+%% Watchdog timers used by the testcases.
+-define(WD_TIMERS, [10000, ?WD(10000)]).
-%% Watchdog timer of the misbehaving peer.
+%% Watchdog timer of the misbehaving node.
-define(PEER_WD, 10000).
-%% 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 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(EVENT(T, N, WdL, WdH),
- [?ERROR({received, _Elapsed_, _LowerBound_, N, WdL})
- || _UpperBound_ <- [(N)*(WdH) + (WdH) div 2],
- _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
+%% A timeout that ensures one watchdog. To ensure only one watchdog
%% requires (Wd + 2000) + 1000 < 2*(Wd - 2000) ==> 7000 < Wd for the
%% case with random jitter.
-define(ONE_WD(Wd), jitter(Wd,2000) + 1000).
+-define(INFO(T), #diameter_event{info = T}).
+
+%% Receive an event message from diameter.
+-define(EVENT(T), %% apply to not bind T_
+ apply(fun() ->
+ receive ?INFO(T = T_) -> log_event(T_) end
+ end,
+ [])).
+
+%% Receive a watchdog event.
+-define(WD_EVENT(Ref), log_wd(element(4, ?EVENT({watchdog, Ref, _, _, _})))).
+-define(WD_EVENT(Ref, Ms),
+ apply(fun() ->
+ receive ?INFO({watchdog, Ref, _, T_, _}) ->
+ log_wd(T_)
+ after Ms ->
+ false
+ end
+ end,
+ [])).
+
+%% Log to make failures identifiable.
+-define(LOG(T), ?LOG("~p", [T])).
+-define(LOG(F,A), ct:pal("~p: " ++ F, [self() | A])).
+-define(WARN(F,A), ct:pal(error, "~p: " ++ F, [self() | A])).
%% ===========================================================================
suite() ->
- [{timetrap, {minutes, 10}}].%% enough for 17 watchdogs @ 30 sec plus jitter
+ [{timetrap, {seconds, 90}}].
all() ->
- [reopen].
+ [reopen,
+ suspect,
+ okay].
init_per_suite(Config) ->
ok = diameter:start(),
@@ -127,91 +134,48 @@ end_per_suite(_Config) ->
%% ===========================================================================
%% Test the watchdog state machine for the required failover, failback
-%% and reopen behaviour. Do this by having the testcase replace
-%% diameter_service and start watchdogs, and having this module
-%% implement a transport process that plays the role of the peer
-%% Diameter node.
+%% and reopen behaviour by examining watchdog events.
-%reopen(_) ->
-% reopen(connect, ?WD(10000), 1, 'DWR');
+reopen() ->
+ [{timetrap, {minutes, 5}}]. %% 20 watchdogs @ 15 sec
reopen(_) ->
- [] = run([[reopen, T, Wd, N, M]
- || Wd <- ?WD_TIMERS, %% watchdog_timer value
- T <- [listen, connect], %% watchdog to test
+ [] = run([[reopen, T, W, N, M]
+ || T <- [listen, connect], %% watchdog to test
+ W <- ?WD_TIMERS, %% watchdog_timer value
N <- [0,1,2], %% DWR's to answer before ignoring
M <- ['DWR', 'DWA', 'RAA']]). %% how to induce failback
-reopen(Type, Wd, N, M) ->
- Server = start_service(),
- Client = start_service(),
+reopen(Test, Wd, N, M) ->
+ %% Publish a ref ensure the connecting transport is added only
+ %% once events from the listening transport are subscribed to.
+ Ref = make_ref(),
+ [] = run([[reopen, T, Test, Ref, Wd, N, M] || T <- [listen, connect]]).
- %% 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)}},
+%% reopen/6
- {{LH,LW},{CH,CW}} = case Type of
- listen -> {Node, Peer};
- connect -> {Peer, Node}
- end,
+reopen(Type, Test, Ref, Wd, N, M) ->
+ {SvcName, TRef} = start(Type, Ref, cfg(Type, Test, Wd)),
+ reopen(Type, Test, SvcName, TRef, Wd, N, M).
- 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.
+cfg(Type, Type, Wd) ->
+ {Wd, [], []};
+cfg(_Type, _Test, _Wd) ->
+ {?WD(?PEER_WD), [{okay, 0}], [{module, ?MODULE}]}.
%% 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) ->
+reopen(Type, Type, SvcName, Ref, Wd, N, M) ->
+ ?LOG("node ~p", [[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.
- ?EVENT({watchdog, Ref, _, {initial, okay}, _}, 2000),
- ?EVENT({up, Ref, _, _, #diameter_packet{}}, 0),
-
- %% Low/high watchdog timeouts.
- WdL = jitter(Wd, -2000),
- WdH = jitter(Wd, 2000),
+ {initial, okay} = ?WD_EVENT(Ref),
+ ?EVENT({up, Ref, _, _, #diameter_packet{}}),
%% OKAY Timer expires & Failover()
%% Pending SetWatchdog() SUSPECT
@@ -221,8 +185,13 @@ recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%% 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),
+ [0,0,0,0] = wd_counts(SvcName),
+
+ {okay, suspect} = ?WD_EVENT(Ref),
+ ?EVENT({down, Ref, _, _}),
+
+ %% N received DWA's
+ [_,_,_,N] = wd_counts(SvcName),
%% SUSPECT Receive DWA Pending = FALSE
%% Failback()
@@ -234,8 +203,13 @@ recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%% 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),
+ {suspect, okay} = ?WD_EVENT(Ref),
+ ?EVENT({up, Ref, _, _}),
+
+ %% N+1 sent DWR's, N/N+1 received DWA's
+ R1 = N+1,
+ A1 = choose(M == 'DWA', R1, N),
+ [R1,_,_,A1] = wd_counts(SvcName),
%% OKAY Timer expires & SendWatchdog()
%% !Pending SetWatchdog()
@@ -248,16 +222,19 @@ recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%% 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),
+ {okay, suspect} = ?WD_EVENT(Ref),
+ ?EVENT({down, Ref, _, _}),
%% SUSPECT Timer expires CloseConnection()
%% SetWatchdog() DOWN
%%
%% Non-response brings the connection down after another timeout.
- ?EVENT({watchdog, Ref, _, {suspect, down}, _}, 1, WdL, WdH),
+ {suspect, down} = ?WD_EVENT(Ref),
+
+ R2 = R1 + choose(M == 'DWA', 1, 0),
+ A2 = A1,
+ [R2,_,_,A2] = wd_counts(SvcName),
%% DOWN Timer expires AttemptOpen()
%% SetWatchdog() DOWN
@@ -269,7 +246,7 @@ recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%%
%% The connection is reestablished after another timeout.
- recv_reopen(Type, Ref, WdL, WdH),
+ recv_reopen(Type, Ref),
%% REOPEN Receive non-DWA Throwaway() REOPEN
%%
@@ -287,18 +264,27 @@ recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%% 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),
+ {reopen, okay} = ?WD_EVENT(Ref),
+ ?EVENT({up, Ref, _, _, #diameter_packet{}}),
+
+ %% Three DWR's have been answered.
+ R3 = R2 + 3,
+ A3 = A2 + 3,
+ [R3,_,_,A3] = wd_counts(SvcName),
%% Non-response brings it down again.
- ?EVENT({watchdog, Ref, _, {okay, suspect}, _}, 2, WdL, WdH),
- ?EVENT({down, Ref, _, _}, 0),
- ?EVENT({watchdog, Ref, _, {suspect, down}, _}, 1, WdL, WdH),
+ {okay, suspect} = ?WD_EVENT(Ref),
+ ?EVENT({down, Ref, _, _}),
+ {suspect, down} = ?WD_EVENT(Ref),
+
+ R4 = R3 + 1,
+ A4 = A3,
+ [R4,_,_,A4] = wd_counts(SvcName),
%% Reestablish after another watchdog.
- recv_reopen(Type, Ref, WdL, WdH),
+ recv_reopen(Type, Ref),
%% REOPEN Timer expires & NumDWA = -1
%% Pending & SetWatchdog()
@@ -311,63 +297,76 @@ recv(Type, Type, _SvcName, Ref, Wd, N, M) ->
%% Peer is now ignoring all watchdogs go down again after 2
%% timeouts.
- ?EVENT({watchdog, Ref, _, {reopen, down}, _}, 2, WdL, WdH);
+ {reopen, down} = ?WD_EVENT(Ref);
%% The misbehaving peer.
-recv(_, Type, SvcName, Ref, Wd, N, M) ->
+reopen(Type, _, SvcName, Ref, Wd, N, M) ->
+ ?LOG("peer ~p", [[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),
+ {initial, okay} = ?WD_EVENT(Ref),
+ ?EVENT({up, Ref, _, _, #diameter_packet{}}),
+
+ reg(Ref, SvcName, {SvcName, {Wd,N,M}}),
+
+ {okay, down} = ?WD_EVENT(Ref),
%% Second transport process.
- ?EVENT({watchdog, Ref, _, {_, reopen}, _}, infinity),
- reg(Type, Ref, SvcName, 3),
- ?EVENT({watchdog, Ref, _, {_, down}, _}, infinity),
+ ?EVENT({watchdog, Ref, _, {_, okay}, _}),
+ reg(Ref, SvcName, 3), %% answer 3 watchdogs then fall silent
+ ?EVENT({watchdog, Ref, _, {_, down}, _}),
%% Third transport process.
- ?EVENT({watchdog, Ref, _, {_, reopen}, _}, infinity),
- reg(Type, Ref, SvcName, 0),
- ?EVENT({watchdog, Ref, _, {_, down}, _}, infinity),
+ ?EVENT({watchdog, Ref, _, {_, okay}, _}),
+ reg(Ref, SvcName, 0), %% disable outgoing DWA
+ ?EVENT({watchdog, Ref, _, {_, down}, _}),
ok.
-%% recv_reopen/4
+log_wd({From, To} = T) ->
+ ?LOG("~p -> ~p", [From, To]),
+ T.
-recv_reopen(connect, Ref, WdL, WdH) ->
- ?EVENT({watchdog, Ref, _, {_, reopen}, _}, 1, WdL, WdH),
- ?EVENT({reconnect, Ref, _}, 0);
+log_event(E) ->
+ T = element(1,E),
+ T == watchdog orelse ?LOG("~p", [T]),
+ E.
-recv_reopen(listen, Ref, _, _) ->
- ?EVENT({watchdog, Ref, _, {_, reopen}, _}, 1, ?PEER_WD).
+%% recv_reopen/2
-%% reg/4
+recv_reopen(connect, Ref) ->
+ {down, reopen} = ?WD_EVENT(Ref),
+ ?EVENT({reconnect, Ref, _});
+
+recv_reopen(listen, Ref) ->
+ {_, reopen} = ?WD_EVENT(Ref).
+
+%% reg/3
%%
%% 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)),
+reg(TRef, SvcName, T) ->
+ TPid = tpid(TRef, 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/2
+
+tpid(Ref, [[{ref, Ref},
+ {type, connect},
+ {options, _},
+ {watchdog, _},
+ {peer, _},
+ {apps, _},
+ {caps, _},
+ {port, [{owner, TPid} | _]}
+ | _]]) ->
TPid;
-tpid(listen, Ref, [[{ref, Ref},
- {type, listen},
- {options, _},
- {accept, As}
- | _]]) ->
+tpid(Ref, [[{ref, Ref},
+ {type, listen},
+ {options, _},
+ {accept, As}
+ | _]]) ->
[[{watchdog, _},
{peer, _},
{apps, _},
@@ -375,12 +374,160 @@ tpid(listen, Ref, [[{ref, Ref},
{port, [{owner, TPid} | _]}
| _]]
= lists:filter(fun([{watchdog, {_,_,S}} | _]) ->
- S == okay orelse S == reopen
+ S == okay orelse S == reopen
end,
As),
TPid.
%% ===========================================================================
+%% # suspect/1
+%% ===========================================================================
+
+%% Configure transports to require a set number of watchdog timeouts
+%% before moving from OKAY to SUSPECT.
+
+suspect(_) ->
+ [] = run([[abuse, [suspect, N]] || N <- [0,1,3]]).
+
+suspect(Type, Fake, Ref, N)
+ when is_reference(Ref) ->
+ {SvcName, TRef}
+ = start(Type, Ref, {?WD(10000), [{suspect, N}], mod(Fake)}),
+ {initial, okay} = ?WD_EVENT(TRef),
+ suspect(TRef, Fake, SvcName, N);
+
+suspect(TRef, true, SvcName, _) ->
+ reg(TRef, SvcName, 0), %% disable outgoing DWA
+ {okay, _} = ?WD_EVENT(TRef);
+
+suspect(TRef, false, SvcName, 0) -> %% SUSPECT disabled
+ %% Wait 2+ watchdogs and see that only one watchdog has been sent.
+ false = ?WD_EVENT(TRef, 28000),
+ [1,0,0,0] = wd_counts(SvcName);
+
+suspect(TRef, false, SvcName, N) ->
+ %% Check that no watchdog transition takes place within N+
+ %% watchdogs ...
+ false = ?WD_EVENT(TRef, N*10000+8000),
+ [1,0,0,0] = wd_counts(SvcName),
+ %% ... but that the connection then becomes suspect ...
+ {okay, suspect} = ?WD_EVENT(TRef, 10000),
+ [1,0,0,0] = wd_counts(SvcName),
+ %% ... and goes down.
+ {suspect, down} = ?WD_EVENT(TRef, 18000),
+ [1,0,0,0] = wd_counts(SvcName).
+
+%% abuse/1
+
+abuse(F) ->
+ [] = run([[abuse, F, T] || T <- [listen, connect]]).
+
+abuse(F, [_,_,_|_] = Args) ->
+ ?LOG("~p", [Args]),
+ apply(?MODULE, F, Args);
+
+abuse([F|A], Test) ->
+ Ref = make_ref(),
+ [] = run([[abuse, F, [T, T == Test, Ref] ++ A]
+ || T <- [listen, connect]]);
+
+abuse(F, Test) ->
+ abuse([F], Test).
+
+mod(true) ->
+ [{module, ?MODULE}];
+mod(false) ->
+ [].
+
+%% ===========================================================================
+%% # okay/1
+%% ===========================================================================
+
+%% Configure the number of watchdog exchanges before moving from
+%% REOPEN to OKAY.
+
+okay(_) ->
+ [] = run([[abuse, [okay, N]] || N <- [0,2,3]]).
+
+okay(Type, Fake, Ref, N)
+ when is_reference(Ref) ->
+ {SvcName, TRef}
+ = start(Type, Ref, {?WD(10000),
+ [{okay, choose(Fake, 0, N)}],
+ mod(Fake)}),
+ {initial, okay} = ?WD_EVENT(TRef),
+ okay(TRef,
+ Fake,
+ SvcName,
+ choose(Type == listen, initial, down),
+ N).
+
+okay(TRef, true, SvcName, Down, _) ->
+ reg(TRef, SvcName, 0), %% disable outgoing DWA
+ {okay, down} = ?WD_EVENT(TRef),
+ {Down, okay} = ?WD_EVENT(TRef),
+ reg(TRef, SvcName, -1), %% enable outgoing DWA
+ {okay, down} = ?WD_EVENT(TRef);
+
+okay(TRef, false, SvcName, Down, N) ->
+ {okay, suspect} = ?WD_EVENT(TRef),
+ [1,0,0,0] = wd_counts(SvcName),
+ {suspect, down} = ?WD_EVENT(TRef),
+ ok(TRef, SvcName, Down, N).
+
+ok(TRef, SvcName, Down, 0) ->
+ %% Connection comes up without watchdog exchange.
+ {Down, okay} = ?WD_EVENT(TRef),
+ [1,0,0,0] = wd_counts(SvcName),
+ %% Wait 2+ watchdog timeouts to see that the connection stays up
+ %% and two watchdogs are exchanged.
+ false = ?WD_EVENT(TRef, 28000),
+ [3,0,0,2] = wd_counts(SvcName);
+
+ok(TRef, SvcName, Down, N) ->
+ %% Connection required watchdog exchange before reaching OKAY.
+ {Down, reopen} = ?WD_EVENT(TRef),
+ {reopen, okay} = ?WD_EVENT(TRef),
+ %% One DWR was sent in moving to expect, plus N more to reopen the
+ %% connection.
+ N1 = N+1,
+ [N1,0,0,N] = wd_counts(SvcName).
+
+%% ===========================================================================
+
+%% wd_counts/1
+
+wd_counts(SvcName) ->
+ [Info] = diameter:service_info(SvcName, transport),
+ {_, Counters} = lists:keyfind(statistics, 1, Info),
+ [proplists:get_value({{0,280,R}, D}, Counters, 0) || D <- [send,recv],
+ R <- [1,0]].
+
+%% start/3
+
+start(Type, Ref, T) ->
+ Name = hostname(),
+ true = diameter:subscribe(Name),
+ ok = diameter:start_service(Name, [{monitor, self()} | ?SERVICE(Name)]),
+ {ok, TRef} = diameter:add_transport(Name, {Type, opts(Type, Ref, T)}),
+ true = diameter_reg:add_new({Type, Ref, Name}),
+ {Name, TRef}.
+
+opts(Type, Ref, {Timer, Config, Mod}) ->
+ [{transport_module, diameter_tcp},
+ {transport_config, Mod ++ [{ip, ?ADDR}, {port, 0}] ++ cfg(Type, Ref)},
+ {watchdog_timer, Timer},
+ {watchdog_config, Config}].
+
+cfg(listen, _) ->
+ [];
+cfg(connect, Ref) ->
+ [{{_, _, SvcName}, _Pid}] = diameter_reg:wait({listen, Ref, '_'}),
+ [[{ref, LRef} | _]] = diameter:service_info(SvcName, transport),
+ [LP] = ?util:lport(tcp, LRef, 20),
+ [{raddr, ?ADDR}, {rport, LP}].
+
+%% ===========================================================================
listen(PortNr, Opts) ->
gen_tcp:listen(PortNr, Opts).
@@ -402,6 +549,7 @@ send(Sock, Bin) ->
%% First outgoing message from a new transport process is CER/CEA.
%% Remaining outgoing messages are either DWR or DWA.
send(undefined, Sock, Bin) ->
+ <<_:32, _:8, 257:24, _/binary>> = Bin,
putr(config, init),
gen_tcp:send(Sock, Bin);
@@ -432,7 +580,7 @@ send({SvcName, {_,_,_} = T}, Sock, Bin) ->
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),
@@ -511,15 +659,10 @@ run1([F|A]) ->
catch
E:R ->
S = erlang:get_stacktrace(),
- io:format("~p~n", [{A, E, R, S}]),
+ ?WARN("~p", [{A, E, R, S}]),
S
end.
-%% now_diff/2
-
-now_diff(T1, T2) ->
- timer:now_diff(T2, T1).
-
%% jitter/2
jitter(?WD(T), _) ->
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index 5898e125ae..c4a713fb10 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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,25 +22,28 @@ COVER_SPEC_FILE = diameter.cover
MODULES = \
diameter_ct \
- diameter_util \
diameter_enum \
- diameter_compiler_SUITE \
+ diameter_util \
+ diameter_3xxx_SUITE \
+ diameter_app_SUITE \
+ diameter_capx_SUITE \
diameter_codec_SUITE \
diameter_codec_test \
- diameter_app_SUITE \
+ diameter_compiler_SUITE \
diameter_dict_SUITE \
- diameter_reg_SUITE \
- diameter_sync_SUITE \
- diameter_stats_SUITE \
- diameter_watchdog_SUITE \
+ diameter_dpr_SUITE \
+ diameter_event_SUITE \
+ diameter_failover_SUITE \
diameter_gen_sctp_SUITE \
- diameter_transport_SUITE \
- diameter_capx_SUITE \
- diameter_traffic_SUITE \
+ diameter_length_SUITE \
+ diameter_reg_SUITE \
diameter_relay_SUITE \
+ diameter_stats_SUITE \
+ diameter_sync_SUITE \
diameter_tls_SUITE \
- diameter_failover_SUITE \
- diameter_dpr_SUITE
+ diameter_traffic_SUITE \
+ diameter_transport_SUITE \
+ diameter_watchdog_SUITE
HRL_FILES = \
diameter_ct.hrl
diff --git a/lib/diameter/test/testspec b/lib/diameter/test/testspec
new file mode 100644
index 0000000000..2fd8307281
--- /dev/null
+++ b/lib/diameter/test/testspec
@@ -0,0 +1,3 @@
+
+{suites, ".", all}.
+{cover, "./coverspec"}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 7b2208137b..757f29a32e 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -18,8 +18,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.3.1
-PRE_VSN =
-APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)"
-
-TICKETS =
+DIAMETER_VSN = 1.4.1.1
+APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index 472b3bfc34..12f93ab400 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>2012</year>
+ <year>2007</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,69 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.7.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>EDoc sometimes failed to associate a comment with the
+ preceding type declaration. This bug has been fixed.
+ (Thanks to Serge Aleynikov for reporting the bug.) </p>
+ <p>
+ Own Id: OTP-10866</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to Unicode support. </p>
+ <p>
+ Own Id: OTP-10820</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.11</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Since EDoc 0.7.7 (R14B02) separate values of union
+ types can be annotated. However, the parser has hitherto
+ chosen not to add the necessary parentheses due to
+ backwards compatibility. </p> <p> From this release on
+ code traversing the output of <c>edoc_parser</c> needs to
+ take care of parentheses around separate values of union
+ types. Examples of such code are layout modules and
+ doclet modules. </p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10195</p>
+ </item>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.10</title>
<section><title>Fixed Bugs and Malfunctions</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..a87a8471e3 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) ->
@@ -659,7 +660,7 @@ read_source(Name, Opts0) ->
check_forms(Forms, Name),
Forms;
{error, R} ->
- edoc_report:error({"error reading file '~s'.",
+ edoc_report:error({"error reading file '~ts'.",
[edoc_lib:filename(Name)]}),
exit({error, R})
end.
@@ -676,7 +677,84 @@ read_source_2(Name, Opts) ->
Includes = proplists:append_values(includes, Opts)
++ [filename:dirname(Name)],
Macros = proplists:append_values(macros, Opts),
- epp:parse_file(Name, Includes, Macros).
+ %% epp:parse_file(Name, Includes, Macros).
+ parse_file(Name, Includes, Macros).
+
+%% The code below has been copied from epp.erl.
+%%
+%% Copy the line of the last token to the last token that will be
+%% part of the parse tree.
+%%
+%% The last line is used in edoc_extract:find_type_docs() to determine
+%% if a type declaration is followed by a comment.
+%% <example>
+%% -type t() :: [
+%% {tag, integer()}
+%% ].
+%% %% Protocol options.
+%% </example>
+%% The line of the dot token will be copied to the integer token.
+
+parse_file(Name, Includes, Macros) ->
+ case epp:open(Name, Includes, Macros) of
+ {ok, Epp} ->
+ try {ok, parse_file(Epp)}
+ after _ = epp:close(Epp)
+ end;
+ Error ->
+ Error
+ end.
+
+parse_file(Epp) ->
+ case scan_and_parse(Epp) of
+ {ok, Form} ->
+ case Form of
+ {attribute,La,record,{Record, Fields}} ->
+ case epp:normalize_typed_record_fields(Fields) of
+ {typed, NewFields} ->
+ [{attribute, La, record, {Record, NewFields}},
+ {attribute, La, type,
+ {{record, Record}, Fields, []}}
+ | parse_file(Epp)];
+ not_typed ->
+ [Form | parse_file(Epp)]
+ end;
+ _ ->
+ [Form | parse_file(Epp)]
+ end;
+ {error, E} ->
+ [{error, E} | parse_file(Epp)];
+ {eof, Location} ->
+ [{eof, Location}]
+ end.
+
+scan_and_parse(Epp) ->
+ case epp:scan_erl_form(Epp) of
+ {ok, Toks0} ->
+ Toks = fix_last_line(Toks0),
+ case erl_parse:parse_form(Toks) of
+ {ok, Form} ->
+ {ok, Form};
+ Else ->
+ Else
+ end;
+ Else ->
+ Else
+ end.
+
+fix_last_line(Toks0) ->
+ Toks1 = lists:reverse(Toks0),
+ {line, LastLine} = erl_scan:token_info(hd(Toks1), line),
+ fll(Toks1, LastLine, []).
+
+fll([{Category, Attributes0, Symbol} | L], LastLine, Ts) ->
+ F = fun(_OldLine) -> LastLine end,
+ Attributes = erl_scan:set_attribute(line, Attributes0, F),
+ lists:reverse(L, [{Category, Attributes, Symbol} | Ts]);
+fll([T | L], LastLine, Ts) ->
+ fll(L, LastLine, [T | Ts]);
+fll(L, _LastLine, Ts) ->
+ lists:reverse(L, Ts).
check_forms(Fs, Name) ->
Fun = fun (F) ->
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 624f9177a2..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 -> []
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index 385d20e9ae..ce1e94a26a 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -192,21 +192,22 @@ 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}
end;
R ->
- report("skipping source file '~s': ~W.", [File, R, 15]),
+ report("skipping source file '~ts': ~W.", [File, R, 15]),
{Set, true}
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
@@ -215,14 +216,14 @@ check_name(M, M0, P0, File) ->
ok;
_ ->
if N =/= N0 ->
- warning("file '~s' actually contains module '~s'.",
+ warning("file '~ts' actually contains module '~s'.",
[File, M]);
true ->
ok
end
end,
if P =/= P0 ->
- warning("file '~s' belongs to package '~s', not '~s'.",
+ warning("file '~ts' belongs to package '~s', not '~s'.",
[File, P, P0]);
true ->
ok
@@ -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 90fb8a679c..276f48453e 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)];
@@ -454,20 +466,20 @@ uri_get("file://localhost/" ++ Path) ->
uri_get_file(Path);
uri_get("file://" ++ Path) ->
Msg = io_lib:format("cannot handle 'file:' scheme with "
- "nonlocal network-path: 'file://~s'.",
+ "nonlocal network-path: 'file://~ts'.",
[Path]),
{error, Msg};
uri_get("file:/" ++ Path) ->
uri_get_file(Path);
uri_get("file:" ++ Path) ->
- Msg = io_lib:format("ignoring malformed URI: 'file:~s'.", [Path]),
+ Msg = io_lib:format("ignoring malformed URI: 'file:~ts'.", [Path]),
{error, Msg};
uri_get("http:" ++ Path) ->
uri_get_http("http:" ++ Path);
uri_get("ftp:" ++ Path) ->
uri_get_ftp("ftp:" ++ Path);
uri_get("//" ++ Path) ->
- Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]),
+ Msg = io_lib:format("cannot access network-path: '//~ts'.", [Path]),
{error, Msg};
uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
uri_get_file(Path); % special case for Windows
@@ -478,7 +490,7 @@ uri_get(URI) ->
true ->
uri_get_file(URI);
false ->
- Msg = io_lib:format("cannot handle URI: '~s'.", [URI]),
+ Msg = io_lib:format("cannot handle URI: '~ts'.", [URI]),
{error, Msg}
end.
@@ -543,12 +555,12 @@ uri_get_http_1(Result, URI) ->
end.
http_errmsg(Reason, URI) ->
- io_lib:format("http error: ~s: '~s'", [Reason, URI]).
+ io_lib:format("http error: ~ts: '~ts'", [Reason, URI]).
%% TODO: implement ftp access method
uri_get_ftp(URI) ->
- Msg = io_lib:format("cannot access ftp scheme yet: '~s'.", [URI]),
+ Msg = io_lib:format("cannot access ftp scheme yet: '~ts'.", [URI]),
{error, Msg}.
%% @private
@@ -603,7 +615,7 @@ copy_file(From, To) ->
{ok, _} -> ok;
{error, R} ->
R1 = file:format_error(R),
- report("error copying '~s' to '~s': ~s.", [From, To, R1]),
+ report("error copying '~ts' to '~ts': ~ts.", [From, To, R1]),
exit(error)
end.
@@ -619,7 +631,7 @@ list_dir(Dir, Error) ->
fun (S, As) -> warning(S, As), [] end
end,
R1 = file:format_error(R),
- F("could not read directory '~s': ~s.", [filename(Dir), R1])
+ F("could not read directory '~ts': ~ts.", [filename(Dir), R1])
end.
%% @private
@@ -655,7 +667,7 @@ simplify_path(P) ->
%% ok -> ok;
%% {error, R} ->
%% R1 = file:format_error(R),
-%% report("cannot create directory '~s': ~s.", [Dir, R1]),
+%% report("cannot create directory '~ts': ~ts.", [Dir, R1]),
%% exit(error)
%% end.
@@ -677,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
@@ -685,16 +696,18 @@ 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);
{error, R} ->
R1 = file:format_error(R),
- report("could not write file '~s': ~s.", [File, R1]),
+ report("could not write file '~ts': ~ts.", [File, R1]),
exit(error)
end.
@@ -705,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}
%%
@@ -714,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.
@@ -740,7 +761,7 @@ read_info_file(Dir) ->
parse_info_file(Text, File);
{error, R} ->
R1 = file:format_error(R),
- warning("could not read '~s': ~s.", [File, R1]),
+ warning("could not read '~ts': ~ts.", [File, R1]),
{?NO_APP, [], []}
end;
false ->
@@ -755,7 +776,7 @@ uri_get_info_file(Base) ->
{ok, Text} ->
parse_info_file(Text, URI);
{error, Msg} ->
- warning("could not read '~s': ~s.", [URI, Msg]),
+ warning("could not read '~ts': ~ts.", [URI, Msg]),
{?NO_APP, [], []}
end.
@@ -764,10 +785,10 @@ parse_info_file(Text, Name) ->
{ok, Vs} ->
info_file_data(Vs);
{error, eof} ->
- warning("unexpected end of file in '~s'.", [Name]),
+ warning("unexpected end of file in '~ts'.", [Name]),
{?NO_APP, [], []};
{error, {_Line,Module,R}} ->
- warning("~s: ~s.", [Module:format_error(R), Name]),
+ warning("~ts: ~ts.", [Module:format_error(R), Name]),
{?NO_APP, [], []}
end.
@@ -818,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 ->
@@ -846,9 +867,12 @@ find_sources_2(Dir, Pkg, Rec, Ext, Skip) ->
find_sources_3(Es, Dir, Pkg, Rec, Ext, Skip) ->
[find_sources_2(filename:join(Dir, E),
- to_atom(packages:concat(Pkg, E)), Rec, Ext, Skip)
+ to_atom(join(Pkg, E)), Rec, Ext, Skip)
|| E <- Es, is_package_dir(E, Dir)].
+join('', E) -> E;
+join(Pkg, E) -> filename:join(Pkg, E).
+
is_source_file(Name, Ext) ->
(filename:extension(Name) == Ext)
andalso is_name(filename:rootname(Name, Ext)).
@@ -858,16 +882,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
@@ -1009,7 +1033,7 @@ run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) ->
{ok, Value} ->
Value;
R ->
- report("error in ~s '~w': ~W.", [Name, Module, R, 20]),
+ report("error in ~ts '~w': ~W.", [Name, Module, R, 20]),
exit(error)
end.
diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl
index 70fb38bf0a..8efbfd00c7 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=\"~ts\"~ts>~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) ->
@@ -102,7 +102,7 @@ type_macro(S, Line, Env) ->
Def = edoc_parser:parse_typedef(S1, Line),
{#t_typedef{type = T}, _} = Def,
Txt = edoc_layout:type(edoc_data:type(T, Env)),
- lists:flatten(io_lib:fwrite("<code>~s</code>", [Txt])).
+ lists:flatten(io_lib:fwrite("<code>~ts</code>", [Txt])).
%% Expand inline macros in tag content.
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index 4d6428f75b..a20f152f34 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
@@ -466,4 +462,4 @@ throw_error({parse_throws, E}, L) ->
throw_error(parse_param, L) ->
throw({error, L, "missing parameter name"});
throw_error({Where, E}, L) when is_list(Where) ->
- throw({error,L,{"unknown error parsing ~s: ~P.",[Where,E,15]}}).
+ throw({error,L,{"unknown error parsing ~ts: ~P.",[Where,E,15]}}).
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_report.erl b/lib/edoc/src/edoc_report.erl
index 9bec08ab97..dc6320df6d 100644
--- a/lib/edoc/src/edoc_report.erl
+++ b/lib/edoc/src/edoc_report.erl
@@ -83,13 +83,13 @@ report(L, Where, S, Vs) ->
io:nl().
where({File, module}) ->
- io_lib:fwrite("~s, in module header: ", [File]);
+ io_lib:fwrite("~ts, in module header: ", [File]);
where({File, footer}) ->
- io_lib:fwrite("~s, in module footer: ", [File]);
+ io_lib:fwrite("~ts, in module footer: ", [File]);
where({File, header}) ->
- io_lib:fwrite("~s, in header file: ", [File]);
+ io_lib:fwrite("~ts, in header file: ", [File]);
where({File, {F, A}}) ->
- io_lib:fwrite("~s, function ~s/~w: ", [File, F, A]);
+ io_lib:fwrite("~ts, function ~s/~w: ", [File, F, A]);
where([]) ->
io_lib:fwrite("~s: ", [?APPLICATION]);
where(File) when is_list(File) ->
diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl
index 48b6137ac1..b5a1ef713d 100644
--- a/lib/edoc/src/edoc_run.erl
+++ b/lib/edoc/src/edoc_run.erl
@@ -162,7 +162,7 @@ file(Args) ->
-spec invalid_args(string(), list()) -> no_return().
invalid_args(Where, Args) ->
- report("invalid arguments to ~s: ~w.", [Where, Args]),
+ report("invalid arguments to ~ts: ~w.", [Where, Args]),
shutdown_error().
run(F) ->
@@ -213,13 +213,13 @@ parse_arg(A) ->
{ok, Expr} ->
case catch erl_parse:normalise(Expr) of
{'EXIT', _} ->
- report("bad argument: '~s':", [A]),
+ report("bad argument: '~ts':", [A]),
exit(error);
Term ->
Term
end;
{error, _, D} ->
- report("error parsing argument '~s'", [A]),
+ report("error parsing argument '~ts'", [A]),
error(D),
exit(error)
end.
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 2d986988c2..eb41f1922a 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -391,10 +391,10 @@ parse_header(Data, Line, Env, Where) when is_list(Where) ->
-spec throw_error(line(), err()) -> no_return().
throw_error(L, {read_file, File, R}) ->
- throw_error(L, {"error reading file '~s': ~w",
+ throw_error(L, {"error reading file '~ts': ~w",
[edoc_lib:filename(File), R]});
throw_error(L, {file_not_found, F}) ->
- throw_error(L, {"file not found: ~s", [F]});
+ throw_error(L, {"file not found: ~ts", [F]});
throw_error(L, file_not_string) ->
throw_error(L, "expected file name as a string");
throw_error(L, D) ->
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
index 5c71658af5..5d0d78bf3c 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)).
@@ -294,7 +295,7 @@ expand_uri([], _, L, _Ss, Us, _As) ->
expand_uri_error(Us, L) ->
{Ps, _} = edoc_lib:split_at(lists:reverse(Us), $:),
- throw_error(L, {"reference '[~s:...' ended unexpectedly", [Ps]}).
+ throw_error(L, {"reference '[~ts:...' ended unexpectedly", [Ps]}).
push_uri(Us, Ss, As) ->
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 2f403212c8..af2aa2203f 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.10
+EDOC_VSN = 0.7.12
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 04dad2eee7..30767abd7e 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2012</year>
+ <year>2012</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml
index d9b11875de..2cdba83bcd 100644
--- a/lib/eldap/doc/src/notes.xml
+++ b/lib/eldap/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2012</year>
+ <year>2012</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,28 @@
</header>
<p>This document describes the changes made to the Eldap application.</p>
- <section>
+ <section><title>Eldap 1.0.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed various dialyzer warnings</p>
+ <p>
+ Own Id: OTP-10403 Aux Id: kunagi-258 [169] </p>
+ </item>
+ <item>
+ <p>
+ Configure the SSL options fully in eldap.</p>
+ <p>
+ Own Id: OTP-10728</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section>
<title>Eldap 1.0</title>
<p>New application. </p>
</section>
diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile
index 39a41d08e2..ebb7967e11 100644
--- a/lib/eldap/src/Makefile
+++ b/lib/eldap/src/Makefile
@@ -1,7 +1,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
@@ -59,7 +59,7 @@ APP_TARGET = $(EBIN)/$(APP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -I../include -I../ebin
+ERL_COMPILE_FLAGS += -I../include -I../ebin -Werror
# ----------------------------------------------------
# Targets
@@ -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..d11f904996 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
@@ -694,26 +699,12 @@ do_recv(S, #eldap{use_tls=true, timeout=Timeout}, Len) ->
recv_response(S, Data) ->
case do_recv(S, Data, 0) of
{ok, Packet} ->
- check_tag(Packet),
case asn1rt:decode('ELDAPv3', 'LDAPMessage', Packet) of
{ok,Resp} -> {ok,Resp};
Error -> throw(Error)
end;
{error,Reason} ->
- throw({gen_tcp_error, Reason});
- Error ->
- throw(Error)
- 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})
+ throw({gen_tcp_error, Reason})
end.
%%% Check for expected kind of reply
@@ -1108,7 +1099,3 @@ get_head(Str,Tail) ->
%%% Should always succeed !
get_head([H|Tail],Tail,Rhead) -> lists:reverse([H|Rhead]);
get_head([H|Rest],Tail,Rhead) -> get_head(Rest,Tail,[H|Rhead]).
-
-l2b(B) when is_binary(B) -> B;
-l2b(L) when is_list(L) -> list_to_binary(L).
-
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/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 9775909d76..abd3a663bd 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,47 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.3.3</title>
+ <section><title>Erl_Docgen 0.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct a PDF indentation bug for tagged lists in
+ tagged lists and added some missing tags to the DTD. </p>
+ <p>
+ Own Id: OTP-10725</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.3.3</title>
<section><title>Improvements and New Features</title>
<list>
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/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/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/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index 7de5af2a49..c15b16eb5b 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-2012. All Rights Reserved.
+ # Copyright Ericsson AB 2009-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
@@ -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>
diff --git a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl
index 4e61f1f476..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>
@@ -162,7 +162,7 @@
<xsl:attribute name="border-after-width">1pt</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>
@@ -227,20 +227,20 @@
<xsl:attribute name="border-after-style">solid</xsl:attribute>
<xsl:attribute name="border-after-width">2pt</xsl:attribute>
<xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></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">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 6c8b438b25..ef96f5dbf2 100644
--- a/lib/erl_docgen/src/Makefile
+++ b/lib/erl_docgen/src/Makefile
@@ -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 cbaa93a15d..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, [
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 2599dc0ff7..a2262198dc 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1,2 +1 @@
-ERL_DOCGEN_VSN = 0.3.3
-
+ERL_DOCGEN_VSN = 0.3.4
diff --git a/lib/erl_interface/aclocal.m4 b/lib/erl_interface/aclocal.m4
index 9578cd35c4..25f40944e7 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-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2013. 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
@@ -1849,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 97f1cff345..d511f2e240 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -273,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'`
@@ -287,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/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 539e16d837..6c340378d4 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -82,6 +82,28 @@
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"/>erlang_char_encoding</tag>
+ <item>
+ <p/>
+ <code type="none">
+typedef enum {
+ ERLANG_ASCII = 1,
+ ERLANG_LATIN1 = 2,
+ ERLANG_UTF8 = 4
+}erlang_char_encoding;
+</code>
+ <p>The character encodings 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. Note that these
+ constants are bit-flags and can be combined with bitwise-or.</p>
+ </item>
+ </taglist>
+ </section>
<funcs>
<func>
<name><ret>void</ret><nametext>ei_set_compat_rel(release_number)</nametext></name>
@@ -225,12 +247,34 @@
<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, erlang_char_encoding from_enc, 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, erlang_char_encoding from_enc, erlang_char_encoding to_enc)</nametext></name>
+ <name><ret>int</ret><nametext>ei_x_encode_atom_as(ei_x_buff* x, const char *p, erlang_char_encoding from_enc, 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, erlang_char_encoding from_enc, 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. If <c>to_enc</c> is set to the bitwise-or'd combination
+ <c>(ERLANG_LATIN1|ERLANG_UTF8)</c>, utf8 encoding is only used if the atom string
+ can not be represented in latin1 encoding.</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 +534,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, erlang_char_encoding want, erlang_char_encoding* was, 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..a7921ea9d2 100644
--- a/lib/erl_interface/doc/src/erl_eterm.xml
+++ b/lib/erl_interface/doc/src/erl_eterm.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 f0a9b336ff..4c0267c264 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,67 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.7.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Superfluous trailing comma in enum erlang_char_encoding
+ causing compile error for g++ with --pedantic option.</p>
+ <p>
+ Own Id: OTP-10913 Aux Id: seq12264 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7.11</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Revert the structs <c>erlang_pid</c>, <c>erlang_port</c>
+ and <c>erlang_ref</c> as they were before R16A (without
+ <c>node_org_enc</c>) in order to be backward compatible
+ with user code that accesses the fields of these structs.</p>
+ <p>
+ Own Id: OTP-10885 Aux Id: seq12256 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Limited support for unicode atoms in the external format
+ and in the internal representation of the vm. This is a
+ preparative feature in order to support communication
+ with future releases of Erlang/OTP that may create
+ unicode atoms.</p>
+ <p>
+ Own Id: OTP-10753</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.7.9</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index ae815b414a..9b83385a46 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. 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
@@ -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,19 @@ 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
+typedef enum {
+ ERLANG_ASCII = 1,
+ ERLANG_LATIN1 = 2,
+ ERLANG_UTF8 = 4
+}erlang_char_encoding;
+
/* a pid */
typedef struct {
- char node[MAXATOMLEN+1];
+ char node[MAXATOMLEN_UTF8];
unsigned int num;
unsigned int serial;
unsigned int creation;
@@ -196,14 +206,14 @@ typedef struct {
/* a port */
typedef struct {
- char node[MAXATOMLEN+1];
+ char node[MAXATOMLEN_UTF8];
unsigned int id;
unsigned int creation;
} erlang_port;
/* a ref */
typedef struct {
- char node[MAXATOMLEN+1];
+ char node[MAXATOMLEN_UTF8];
int len;
unsigned int n[3];
unsigned int creation;
@@ -223,15 +233,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];
+ erlang_char_encoding module_org_enc;
char md5[16];
long index;
long old_index;
@@ -256,7 +267,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 +436,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,
+ erlang_char_encoding from, 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,
+ erlang_char_encoding from, 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,
+ erlang_char_encoding from, 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,
+ erlang_char_encoding from, 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 +496,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, erlang_char_encoding want, erlang_char_encoding* was, 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..7f44cb83bd 100644
--- a/lib/erl_interface/include/erl_interface.h
+++ b/lib/erl_interface/include/erl_interface.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. 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
@@ -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..a069aa07ab 100644
--- a/lib/erl_interface/src/Makefile
+++ b/lib/erl_interface/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. 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
@@ -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 cb41391fe9..ebacc1cee0 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.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/auxdir/config.guess b/lib/erl_interface/src/auxdir/config.guess
index 38a833903b..f475ceb413 100755
--- a/lib/erl_interface/src/auxdir/config.guess
+++ b/lib/erl_interface/src/auxdir/config.guess
@@ -1,14 +1,12 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-05-17'
+timestamp='2013-02-12'
# 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
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@@ -17,26 +15,22 @@ timestamp='2007-05-17'
# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
#
-# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# Please send patches with a ChangeLog entry to [email protected].
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -56,8 +50,7 @@ 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.
+Copyright 1992-2013 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."
@@ -144,7 +137,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
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*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -170,7 +163,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 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
+ | grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -180,7 +173,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
;;
esac
# The OS release
@@ -201,6 +194,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -223,7 +220,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -269,7 +266,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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 ;;
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
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
@@ -295,12 +295,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-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:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -324,14 +324,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
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/[^.]*//'`
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
@@ -375,23 +394,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -461,8 +480,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ # 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 ] || \
@@ -475,7 +494,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -532,7 +551,7 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[45])
+ *:AIX:*:[4567])
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
@@ -575,52 +594,52 @@ EOF
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" ;;
+ 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
+ esac ;;
+ esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ sed 's/^ //' << EOF >$dummy.c
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
+ #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);
+ 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);
- }
+ 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
@@ -640,7 +659,7 @@ EOF
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
+ grep -q __LP64__
then
HP_ARCH="hppa2.0w"
else
@@ -711,22 +730,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -750,14 +769,14 @@ EOF
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 ;;
+ 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}"
+ 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}
@@ -769,40 +788,51 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
amd64)
echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
+ exit ;;
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
+ i*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ # 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)
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
- EM64T | authenticamd)
+ authenticamd | genuineintel | EM64T)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
+ IA64)
+ echo ia64-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 ;;
+ 8664:Windows_NT:*)
+ echo x86_64-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
@@ -832,20 +862,68 @@ EOF
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
- arm*:Linux:*:*)
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
echo ${UNAME_MACHINE}-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 -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -856,74 +934,36 @@ EOF
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:*:*)
+ mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
- #undef mips64
- #undef mips64el
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
+ CPU=${UNAME_MACHINE}el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
+ CPU=${UNAME_MACHINE}
#else
CPU=
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
+ or1k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
+ padre:Linux:*:*)
+ echo sparc-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}
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
@@ -933,14 +973,17 @@ EOF
*) echo hppa-unknown-linux-gnu ;;
esac
exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -948,81 +991,18 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- tile:Linux:*:*)
- echo tile-unknown-linux-gnu
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-gnu
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- xtensa:Linux:*:*)
- echo xtensa-unknown-linux-gnu
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-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
@@ -1030,11 +1010,11 @@ EOF
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,
+ # 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.
+ # Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1051,7 +1031,7 @@ EOF
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
@@ -1066,7 +1046,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1094,10 +1074,13 @@ EOF
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 ;;
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1132,8 +1115,18 @@ EOF
/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; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ 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; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
@@ -1146,7 +1139,7 @@ EOF
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
@@ -1166,10 +1159,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- echo i586-unisys-sysv4
- 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
@@ -1195,11 +1188,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv${UNAME_RELEASE}
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1209,6 +1202,12 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1236,6 +1235,16 @@ EOF
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
unknown) UNAME_PROCESSOR=powerpc ;;
esac
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1251,7 +1260,10 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
@@ -1296,13 +1308,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ 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`
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1317,11 +1329,14 @@ EOF
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ 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_
@@ -1339,11 +1354,11 @@ main ()
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
- "4"
+ "4"
#else
- ""
+ ""
#endif
- ); exit (0);
+ ); exit (0);
#endif
#endif
@@ -1477,9 +1492,9 @@ 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
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
If the version you run ($0) is already up to date, please
send the following data and any information you think might be
diff --git a/lib/erl_interface/src/auxdir/config.sub b/lib/erl_interface/src/auxdir/config.sub
index f43233b104..bb6edbdb47 100755
--- a/lib/erl_interface/src/auxdir/config.sub
+++ b/lib/erl_interface/src/auxdir/config.sub
@@ -1,44 +1,40 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-04-29'
+timestamp='2013-02-12'
-# 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
+# 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 3 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.
+# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Please send patches with a ChangeLog entry to [email protected].
#
# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
# 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.
@@ -72,8 +68,7 @@ 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.
+Copyright 1992-2013 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."
@@ -120,12 +115,18 @@ esac
# 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* | \
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -148,10 +149,13 @@ case $os in
-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)
+ -apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
+ -bluegene*)
+ os=-cnk
+ ;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
@@ -166,10 +170,10 @@ case $os in
os=-chorusos
basic_machine=$1
;;
- -chorusrdb)
- os=-chorusrdb
+ -chorusrdb)
+ os=-chorusrdb
basic_machine=$1
- ;;
+ ;;
-hiux*)
os=-hiuxwe2
;;
@@ -214,6 +218,12 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
-lynx*)
os=-lynxos
;;
@@ -238,24 +248,34 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 | aarch64_be \
| 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 \
+ | arc \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | be32 | be64 \
| bfin \
| c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
+ | epiphany \
| fido | fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
- | mips64vr | mips64vrel \
+ | mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
@@ -266,31 +286,45 @@ case $basic_machine in
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
+ | moxie \
| mt \
| msp430 \
- | nios | nios2 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | or32 \
+ | open8 \
+ | or1k | or32 \
| pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
+ | rl78 | rx \
| score \
- | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | 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 \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
- | z8k)
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
basic_machine=$basic_machine-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -300,6 +334,21 @@ case $basic_machine in
basic_machine=mt-unknown
;;
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-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.
@@ -314,29 +363,37 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* | aarch64_be-* \
| 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-* \
+ | be32-* | be64-* \
| bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
| 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-* \
+ | hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
+ | mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
@@ -347,31 +404,41 @@ case $basic_machine in
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
- | nios-* | nios2-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | 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-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
| tron-* \
- | v850-* | v850e-* | vax-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
| we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
- | xstormy16-* | xtensa-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
| ymp-* \
- | z8k-*)
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -389,7 +456,7 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
- abacus)
+ abacus)
basic_machine=abacus-unknown
;;
adobe68k)
@@ -435,6 +502,10 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-bsd
;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -443,10 +514,35 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -475,8 +571,8 @@ case $basic_machine in
basic_machine=craynv-cray
os=-unicosmp
;;
- cr16c)
- basic_machine=cr16c-unknown
+ cr16 | cr16-*)
+ basic_machine=cr16-unknown
os=-elf
;;
crds | unos)
@@ -514,6 +610,10 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
@@ -629,7 +729,6 @@ case $basic_machine in
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
@@ -668,6 +767,14 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
m88k-omron*)
basic_machine=m88k-omron
;;
@@ -679,6 +786,13 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
mingw32)
basic_machine=i386-pc
os=-mingw32
@@ -715,10 +829,18 @@ case $basic_machine in
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -783,6 +905,12 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@@ -813,6 +941,14 @@ case $basic_machine in
basic_machine=i860-intel
os=-osf
;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
pbd)
basic_machine=sparc-tti
;;
@@ -857,9 +993,10 @@ case $basic_machine in
;;
power) basic_machine=power-ibm
;;
- ppc) basic_machine=powerpc-unknown
+ ppc | ppcbe) basic_machine=powerpc-unknown
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
@@ -884,7 +1021,11 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
- rdos)
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
basic_machine=i386-pc
os=-rdos
;;
@@ -953,6 +1094,9 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sun2)
basic_machine=m68000-sun
;;
@@ -1009,17 +1153,9 @@ case $basic_machine in
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
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
;;
tx39)
basic_machine=mipstx39-unknown
@@ -1027,10 +1163,6 @@ case $basic_machine in
tx39el)
basic_machine=mipstx39el-unknown
;;
- tile*)
- basic_machine=tile-tilera
- os=-linux-gnu
- ;;
toad1)
basic_machine=pdp10-xkl
os=-tops20
@@ -1092,6 +1224,9 @@ case $basic_machine in
xps | xps100)
basic_machine=xps100-honeywell
;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1100,6 +1235,10 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
none)
basic_machine=none-none
os=-none
@@ -1138,7 +1277,7 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
- sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1185,9 +1324,12 @@ esac
if [ x"$os" != x"" ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
@@ -1208,21 +1350,23 @@ case $os in
# 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* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
+ | -aos* | -aros* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
+ | -bitrig* | -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* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
@@ -1230,7 +1374,7 @@ case $os in
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1269,7 +1413,7 @@ case $os in
-opened*)
os=-openedition
;;
- -os400*)
+ -os400*)
os=-os400
;;
-wince*)
@@ -1318,7 +1462,7 @@ case $os in
-sinix*)
os=-sysv4
;;
- -tpf*)
+ -tpf*)
os=-tpf
;;
-triton*)
@@ -1354,12 +1498,14 @@ case $os in
-aros*)
os=-aros
;;
- -kaos*)
- os=-kaos
- ;;
-zvmoe)
os=-zvmoe
;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
+ ;;
-none)
;;
*)
@@ -1382,10 +1528,10 @@ else
# system, and we'll never get to this point.
case $basic_machine in
- score-*)
+ score-*)
os=-elf
;;
- spu-*)
+ spu-*)
os=-elf
;;
*-acorn)
@@ -1397,8 +1543,20 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
- c4x-* | tic4x-*)
- os=-coff
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
@@ -1418,14 +1576,11 @@ case $basic_machine in
;;
m68000-sun)
os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
;;
m68*-cisco)
os=-aout
;;
- mep-*)
+ mep-*)
os=-elf
;;
mips*-cisco)
@@ -1434,6 +1589,9 @@ case $basic_machine in
mips*-*)
os=-elf
;;
+ or1k-*)
+ os=-elf
+ ;;
or32-*)
os=-coff
;;
@@ -1452,7 +1610,7 @@ case $basic_machine in
*-ibm)
os=-aix
;;
- *-knuth)
+ *-knuth)
os=-mmixware
;;
*-wec)
@@ -1557,7 +1715,7 @@ case $basic_machine in
-sunos*)
vendor=sun
;;
- -aix*)
+ -cnk*|-aix*)
vendor=ibm
;;
-beos*)
@@ -1628,3 +1786,4 @@ exit
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
+
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 34362b4b9f..3ab86bb340 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -1070,7 +1070,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 +1332,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..42ab9b58d7 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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..2545c5f3de 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-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
@@ -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..1821de3844 100644
--- a/lib/erl_interface/src/connect/eirecv.c
+++ b/lib/erl_interface/src/connect/eirecv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. 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
@@ -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..44fd4df12c 100644
--- a/lib/erl_interface/src/decode/decode_atom.c
+++ b/lib/erl_interface/src/decode/decode_atom.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-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
@@ -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,
+ erlang_char_encoding want_enc,
+ erlang_char_encoding* was_encp,
+ erlang_char_encoding* res_encp)
+{
+ const char *s = buf + *index;
+ const char *s0 = s;
+ int len;
+ 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,
+ 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,
+ 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,
+ 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..8c44a1ccfc 100644
--- a/lib/erl_interface/src/decode/decode_boolean.c
+++ b/lib/erl_interface/src/decode/decode_boolean.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
@@ -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..0857874c89 100644
--- a/lib/erl_interface/src/decode/decode_fun.c
+++ b/lib/erl_interface/src/decode/decode_fun.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,25 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
const char *s = buf + *index;
const char *s0 = s;
int i, ix, ix0, n;
+ erlang_pid* p_pid;
+ char* p_module;
+ erlang_char_encoding* p_module_org_enc;
+ long* p_index;
+ long* p_uniq;
+ long* p_old_index;
+
+ if (p != NULL) {
+ p_pid = &p->pid;
+ p_module = &p->module[0];
+ p_module_org_enc = &p->module_org_enc;
+ p_index = &p->index;
+ p_uniq = &p->uniq;
+ p_old_index = &p->old_index;
+ }
+ else {
+ p_pid = NULL; p_module = NULL; p_module_org_enc = NULL;
+ p_index = NULL; p_uniq = NULL; p_old_index = NULL;
+ }
switch (get8(s)) {
case ERL_FUN_EXT:
@@ -39,16 +58,17 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
n = get32be(s);
/* then the pid */
ix = 0;
- if (ei_decode_pid(s, &ix, (p == NULL ? (erlang_pid*)NULL : &p->pid)) < 0)
+ if (ei_decode_pid(s, &ix, 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_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)
+ if (ei_decode_long(s, &ix, p_index) < 0)
return -1;
/* then the uniq */
- if (ei_decode_long(s, &ix, (p == NULL ? (long*)NULL : &p->uniq)) < 0)
+ if (ei_decode_long(s, &ix, p_uniq) < 0)
return -1;
/* finally the free vars */
ix0 = ix;
@@ -84,16 +104,17 @@ 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_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)
+ if (ei_decode_long(s, &ix, p_old_index) < 0)
return -1;
/* then the old_uniq */
- if (ei_decode_long(s, &ix, (p == NULL ? (long*)NULL : &p->uniq)) < 0)
+ if (ei_decode_long(s, &ix, p_uniq) < 0)
return -1;
/* the the pid */
- if (ei_decode_pid(s, &ix, (p == NULL ? (erlang_pid*)NULL : &p->pid)) < 0)
+ if (ei_decode_pid(s, &ix, p_pid) < 0)
return -1;
/* finally the free vars */
s += ix;
diff --git a/lib/erl_interface/src/decode/decode_pid.c b/lib/erl_interface/src/decode/decode_pid.c
index 9ed1c36db6..cd5ae2ab20 100644
--- a/lib/erl_interface/src/decode/decode_pid.c
+++ b/lib/erl_interface/src/decode/decode_pid.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-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
@@ -21,34 +21,24 @@
#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;
-
- /* now the numbers: num (4), serial (4), creation (1) */
if (p) {
+ if (get_atom(&s, p->node, NULL) < 0) return -1;
p->num = get32be(s) & 0x7fff; /* 15 bits */
p->serial = get32be(s) & 0x1fff; /* 13 bits */
p->creation = get8(s) & 0x03; /* 2 bits */
}
- else s+= 9;
+ else {
+ if (get_atom(&s, NULL, NULL) < 0) return -1;
+ s+= 9;
+ }
*index += s-s0;
diff --git a/lib/erl_interface/src/decode/decode_port.c b/lib/erl_interface/src/decode/decode_port.c
index 28abed801a..8fbdc5f3d3 100644
--- a/lib/erl_interface/src/decode/decode_port.c
+++ b/lib/erl_interface/src/decode/decode_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-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
@@ -25,29 +25,18 @@ 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;
-
- /* now the numbers: num (4), creation (1) */
if (p) {
+ if (get_atom(&s, p->node, NULL) < 0) return -1;
p->id = get32be(s) & 0x0fffffff /* 28 bits */;
p->creation = get8(s) & 0x03;
}
- else s += 5;
+ else {
+ if (get_atom(&s, NULL, NULL) < 0) return -1;
+ s += 5;
+ }
*index += s-s0;
diff --git a/lib/erl_interface/src/decode/decode_ref.c b/lib/erl_interface/src/decode/decode_ref.c
index 7b15808bc5..78db118172 100644
--- a/lib/erl_interface/src/decode/decode_ref.c
+++ b/lib/erl_interface/src/decode/decode_ref.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-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
@@ -21,35 +21,25 @@
#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;
-
- /* now the numbers: num (4), creation (1) */
if (p) {
+ if (get_atom(&s, p->node, NULL) < 0) return -1;
p->n[0] = get32be(s);
p->len = 1;
p->creation = get8(s) & 0x03;
}
- else s += 5;
+ else {
+ if (get_atom(&s, NULL, NULL) < 0) return -1;
+ s += 5;
+ }
*index += s-s0;
@@ -59,24 +49,16 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
case ERL_NEW_REFERENCE_EXT:
/* first the integer count */
count = get16be(s);
- 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;
-
- /* creation */
- if (p) {
+ p->len = count;
+ if (get_atom(&s, p->node, NULL) < 0) return -1;
p->creation = get8(s) & 0x03;
}
- else s += 1;
+ else {
+ if (get_atom(&s, NULL, NULL) < 0) return -1;
+ s += 1;
+ }
/* finally the id integers */
if (p) {
@@ -95,3 +77,4 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
return -1;
}
}
+
diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c
index f6c5d861ab..553266471c 100644
--- a/lib/erl_interface/src/decode/decode_skip.c
+++ b/lib/erl_interface/src/decode/decode_skip.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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,7 +30,8 @@ int ei_skip_term(const char* buf, int* index)
switch (ty) {
case ERL_ATOM_EXT:
/* FIXME: what if some weird locale is in use? */
- if (ei_decode_atom(buf, index, NULL) < 0) return -1;
+ if (ei_decode_atom_as(buf, index, NULL, MAXATOMLEN_UTF8, (ERLANG_LATIN1|ERLANG_UTF8),
+ NULL, NULL) < 0) return -1;
break;
case ERL_PID_EXT:
if (ei_decode_pid(buf, index, NULL) < 0) return -1;
diff --git a/lib/erl_interface/src/decode/decode_trace.c b/lib/erl_interface/src/decode/decode_trace.c
index ebaa78e29e..d192c7f8b1 100644
--- a/lib/erl_interface/src/decode/decode_trace.c
+++ b/lib/erl_interface/src/decode/decode_trace.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
@@ -22,18 +22,30 @@
int ei_decode_trace(const char *buf, int *index, erlang_trace *p)
{
int arity = 0;
- int tindex = *index;
-
- /* use a temporary index if any function should fail */
+ int tindex = *index; /* use a temporary index if any function should fail */
+ long *p_flags, *p_label, *p_serial, *p_prev;
+ erlang_pid *p_from;
+
+ if (p != NULL) {
+ p_flags = &p->flags;
+ p_label = &p->label;
+ p_serial = &p->serial;
+ p_prev = &p->prev;
+ p_from = &p->from;
+ }
+ else {
+ p_flags = p_label = p_serial = p_prev = NULL;
+ p_from = NULL;
+ }
/* { Flags, Label, Serial, FromPid, Prev } */
if (ei_decode_tuple_header(buf, &tindex, &arity)
|| (arity != 5)
- || ei_decode_long(buf, &tindex, &p->flags)
- || ei_decode_long(buf, &tindex, &p->label)
- || ei_decode_long(buf, &tindex, &p->serial)
- || ei_decode_pid( buf, &tindex, &p->from)
- || ei_decode_long(buf, &tindex, &p->prev)) return -1;
+ || ei_decode_long(buf, &tindex, p_flags)
+ || ei_decode_long(buf, &tindex, p_label)
+ || ei_decode_long(buf, &tindex, p_serial)
+ || ei_decode_pid( buf, &tindex, p_from)
+ || ei_decode_long(buf, &tindex, p_prev)) return -1;
/* index is updated by the functions we called */
diff --git a/lib/erl_interface/src/encode/encode_atom.c b/lib/erl_interface/src/encode/encode_atom.c
index 6f41f045e0..46d34c3bf0 100644
--- a/lib/erl_interface/src/encode/encode_atom.c
+++ b/lib/erl_interface/src/encode/encode_atom.c
@@ -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
@@ -22,29 +22,120 @@
#include "eiext.h"
#include "putget.h"
+
+static int verify_ascii_atom(const char* src, int slen);
+static int verify_utf8_atom(const char* src, int slen);
+static int is_latin1_as_utf8(const char *p, int len);
+
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,
+ erlang_char_encoding from_enc,
+ 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,
+ erlang_char_encoding from_enc,
+ 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);
+ if (to_enc == (ERLANG_LATIN1 | ERLANG_UTF8)) {
+ if (from_enc == ERLANG_UTF8) {
+ to_enc = is_latin1_as_utf8(p, len) ? ERLANG_LATIN1 : ERLANG_UTF8;
+ }
+ else {
+ to_enc = from_enc;
+ }
+ }
+ 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 (verify_ascii_atom(p, len) < 0) return -1;
+ memcpy(s+2, p, len);
+ 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;
+ } else if (from_enc == ERLANG_ASCII)
+ if (verify_ascii_atom(p, 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 (verify_ascii_atom(p, len) < 0) return -1;
+ if (buf) memcpy(s+offs,p,len);
+ break;
+ case ERLANG_UTF8:
+ if (len >= 256) offs++;
+ if (verify_utf8_atom(p, len) < 0) return -1;
+ if (buf) memcpy(s+offs,p,len);
+ 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 +144,65 @@ 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,
+ 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;
+}
+
+
+static int verify_ascii_atom(const char* src, int slen)
+{
+ while (slen > 0) {
+ if ((src[0] & 0x80) != 0) return -1;
+ src++;
+ slen--;
+ }
+ return 0;
+}
+
+static int verify_utf8_atom(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;
+ src++;
+ slen--;
+ }
+ else if ((src[0] & 0xF0) == 0xE0) {
+ if (slen < 3 || (src[1] & 0xC0) != 0x80 || (src[2] & 0xC0) != 0x80) return -1;
+ src += 2;
+ 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;
+ src += 3;
+ slen -= 3;
+ }
+ else return -1;
+ }
+ src++;
+ slen--;
+ }
+ return 0;
+}
+
+/* Only latin1 code points in utf8 string?
+ */
+static int is_latin1_as_utf8(const char *p, int len)
+{
+ int i;
+ for (i=0; i<len; i++) {
+ if ((unsigned char)p[i] > 0xC3) return 0;
+ }
+ return 1;
+}
diff --git a/lib/erl_interface/src/encode/encode_fun.c b/lib/erl_interface/src/encode/encode_fun.c
index 54ee2083d6..455d8a5a94 100644
--- a/lib/erl_interface/src/encode/encode_fun.c
+++ b/lib/erl_interface/src/encode/encode_fun.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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..86d0f393e5 100644
--- a/lib/erl_interface/src/encode/encode_pid.c
+++ b/lib/erl_interface/src/encode/encode_pid.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
@@ -24,29 +24,24 @@
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, ERLANG_LATIN1|ERLANG_UTF8) < 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..a206de56c7 100644
--- a/lib/erl_interface/src/encode/encode_port.c
+++ b/lib/erl_interface/src/encode/encode_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
@@ -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,
+ ERLANG_LATIN1|ERLANG_UTF8) < 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..9855231848 100644
--- a/lib/erl_interface/src/encode/encode_ref.c
+++ b/lib/erl_interface/src/encode/encode_ref.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
@@ -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,
+ ERLANG_LATIN1|ERLANG_UTF8) < 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..ae0265a388 100644
--- a/lib/erl_interface/src/legacy/erl_connect.c
+++ b/lib/erl_interface/src/legacy/erl_connect.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-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
@@ -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,13 @@ 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);
+ }
+ else {
+ strcpy(topid.node, to->uval.pidval.node.utf8);
+ }
topid.num = ERL_PID_NUMBER(to);
topid.serial = ERL_PID_SERIAL(to);
topid.creation = ERL_PID_CREATION(to);
@@ -263,7 +267,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 +303,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..7ca4f430de 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.c
+++ b/lib/erl_interface/src/legacy/erl_eterm.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2010. 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
@@ -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) {
+ 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..ca4ec7925a 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.h
+++ b/lib/erl_interface/src/legacy/erl_eterm.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. 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
@@ -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..fda05cbdb8 100644
--- a/lib/erl_interface/src/legacy/erl_format.c
+++ b/lib/erl_interface/src/legacy/erl_format.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-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
@@ -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..c320a00bfe 100644
--- a/lib/erl_interface/src/legacy/erl_malloc.c
+++ b/lib/erl_interface/src/legacy/erl_malloc.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. 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
@@ -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..e207b5f0f1 100644
--- a/lib/erl_interface/src/legacy/erl_marshal.c
+++ b/lib/erl_interface/src/legacy/erl_marshal.c
@@ -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
@@ -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;
+ 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)
+{
+ erlang_char_encoding enc1 = tag2enc(tag1);
+ 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..589c9d17de 100644
--- a/lib/erl_interface/src/legacy/global_whereis.c
+++ b/lib/erl_interface/src/legacy/global_whereis.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
@@ -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..ce5ae5b19d 100644
--- a/lib/erl_interface/src/misc/ei_decode_term.c
+++ b/lib/erl_interface/src/misc/ei_decode_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,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, NULL) < 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, NULL) < 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, NULL) < 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, NULL) < 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..07499213cd 100644
--- a/lib/erl_interface/src/misc/ei_format.c
+++ b/lib/erl_interface/src/misc/ei_format.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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..95369db15a 100644
--- a/lib/erl_interface/src/misc/ei_printterm.c
+++ b/lib/erl_interface/src/misc/ei_printterm.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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..14d0b56b8f 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -99,7 +99,8 @@ 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 i = x->index;
- ei_encode_string_len(NULL, &i, s, len);
+ if (ei_encode_string_len(NULL, &i, s, len) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_string_len(x->buff, &x->index, s, len);
@@ -108,7 +109,8 @@ int ei_x_encode_string_len(ei_x_buff* x, const char* s, int len)
int ei_x_encode_binary(ei_x_buff* x, const void* p, int len)
{
int i = x->index;
- ei_encode_binary(NULL, &i, p, len);
+ if (ei_encode_binary(NULL, &i, p, len) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_binary(x->buff, &x->index, p, len);
@@ -117,7 +119,8 @@ int ei_x_encode_binary(ei_x_buff* x, const void* p, int len)
int ei_x_encode_long(ei_x_buff* x, long n)
{
int i = x->index;
- ei_encode_long(NULL, &i, n);
+ if (ei_encode_long(NULL, &i, n) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_long(x->buff, &x->index, n);
@@ -126,7 +129,8 @@ int ei_x_encode_long(ei_x_buff* x, long n)
int ei_x_encode_ulong(ei_x_buff* x, unsigned long n)
{
int i = x->index;
- ei_encode_ulong(NULL, &i, n);
+ if (ei_encode_ulong(NULL, &i, n) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_ulong(x->buff, &x->index, n);
@@ -135,7 +139,8 @@ int ei_x_encode_ulong(ei_x_buff* x, unsigned long n)
int ei_x_encode_char(ei_x_buff* x, char p)
{
int i = x->index;
- ei_encode_char(NULL, &i, p);
+ if (ei_encode_char(NULL, &i, p) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_char(x->buff, &x->index, p);
@@ -144,7 +149,8 @@ int ei_x_encode_char(ei_x_buff* x, char p)
int ei_x_encode_boolean(ei_x_buff* x, int p)
{
int i = x->index;
- ei_encode_boolean(NULL, &i, p);
+ if (ei_encode_boolean(NULL, &i, p) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_boolean(x->buff, &x->index, p);
@@ -153,7 +159,8 @@ int ei_x_encode_boolean(ei_x_buff* x, int p)
int ei_x_encode_double(ei_x_buff* x, double dbl)
{
int i = x->index;
- ei_encode_double(NULL, &i, dbl);
+ if (ei_encode_double(NULL, &i, dbl) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_double(x->buff, &x->index, dbl);
@@ -162,7 +169,8 @@ int ei_x_encode_double(ei_x_buff* x, double dbl)
int ei_x_encode_list_header(ei_x_buff* x, long n)
{
int i = x->index;
- ei_encode_list_header(NULL, &i, n);
+ if (ei_encode_list_header(NULL, &i, n) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_list_header(x->buff, &x->index, n);
@@ -171,7 +179,8 @@ int ei_x_encode_list_header(ei_x_buff* x, long n)
int ei_x_encode_empty_list(ei_x_buff* x)
{
int i = x->index;
- ei_encode_empty_list(NULL, &i);
+ if (ei_encode_empty_list(NULL, &i) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_empty_list(x->buff, &x->index);
@@ -180,7 +189,8 @@ int ei_x_encode_empty_list(ei_x_buff* x)
int ei_x_encode_version(ei_x_buff* x)
{
int i = x->index;
- ei_encode_version(NULL, &i);
+ if (ei_encode_version(NULL, &i) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_version(x->buff, &x->index);
@@ -189,7 +199,8 @@ int ei_x_encode_version(ei_x_buff* x)
int ei_x_encode_tuple_header(ei_x_buff* x, long n)
{
int i = x->index;
- ei_encode_tuple_header(NULL, &i, n);
+ if (ei_encode_tuple_header(NULL, &i, n) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_tuple_header(x->buff, &x->index, n);
@@ -197,22 +208,39 @@ 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,
+ erlang_char_encoding from_enc,
+ 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,
+ erlang_char_encoding from_enc,
+ erlang_char_encoding to_enc)
+{
int i = x->index;
- ei_encode_atom_len(NULL, &i, s, len);
+ if (ei_encode_atom_len_as(NULL, &i, s, len, from_enc, to_enc) == -1)
+ return -1;
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;
- ei_encode_pid(NULL, &i, pid);
+ if (ei_encode_pid(NULL, &i, pid) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_pid(x->buff, &x->index, pid);
@@ -221,7 +249,8 @@ int ei_x_encode_pid(ei_x_buff* x, const erlang_pid* pid)
int ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun)
{
int i = x->index;
- ei_encode_fun(NULL, &i, fun);
+ if (ei_encode_fun(NULL, &i, fun) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_fun(x->buff, &x->index, fun);
@@ -230,7 +259,8 @@ int ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun)
int ei_x_encode_ref(ei_x_buff* x, const erlang_ref* ref)
{
int i = x->index;
- ei_encode_ref(NULL, &i, ref);
+ if (ei_encode_ref(NULL, &i, ref) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_ref(x->buff, &x->index, ref);
@@ -239,7 +269,8 @@ int ei_x_encode_ref(ei_x_buff* x, const erlang_ref* ref)
int ei_x_encode_port(ei_x_buff* x, const erlang_port* port)
{
int i = x->index;
- ei_encode_port(NULL, &i, port);
+ if (ei_encode_port(NULL, &i, port) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_port(x->buff, &x->index, port);
@@ -248,7 +279,8 @@ int ei_x_encode_port(ei_x_buff* x, const erlang_port* port)
int ei_x_encode_trace(ei_x_buff* x, const erlang_trace* trace)
{
int i = x->index;
- ei_encode_trace(NULL, &i, trace);
+ if (ei_encode_trace(NULL, &i, trace) == -1)
+ return -1;
if (!x_fix_buff(x, i))
return -1;
return ei_encode_trace(x->buff, &x->index, trace);
diff --git a/lib/erl_interface/src/misc/get_type.c b/lib/erl_interface/src/misc/get_type.c
index 2a680d0f94..2bd992bd56 100644
--- a/lib/erl_interface/src/misc/get_type.c
+++ b/lib/erl_interface/src/misc/get_type.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. 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
@@ -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..c751e03093 100644
--- a/lib/erl_interface/src/misc/putget.h
+++ b/lib/erl_interface/src/misc/putget.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. 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
@@ -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, erlang_char_encoding* res_encp);
+int latin1_to_utf8(char* dst, const char* src, int slen, int destlen, erlang_char_encoding* res_encp);
+int ei_internal_get_atom(const char** bufp, char* p, erlang_char_encoding*);
+int ei_internal_put_atom(char** bufp, const char* p, int slen, 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..935a9a3287 100644
--- a/lib/erl_interface/src/misc/show_msg.c
+++ b/lib/erl_interface/src/misc/show_msg.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. 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
@@ -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..56d4eb7db4 100644
--- a/lib/erl_interface/src/prog/ei_fake_prog.c
+++ b/lib/erl_interface/src/prog/ei_fake_prog.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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
@@ -96,6 +96,7 @@ int main(void)
EI_ULONGLONG *ulonglongp = (EI_ULONGLONG*)NULL;
EI_ULONGLONG ulonglongx = 0;
#endif
+ 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/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl
index 7e370c566a..5245173260 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-2012. 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
@@ -40,7 +40,7 @@ run([]) ->
run1(Name) ->
CFile = Name ++ ".c",
{ok, Bin} = file:read_file(CFile),
- RE = "\nTESTCASE\\(([_a-zA-Z]*)\\)",
+ RE = "\nTESTCASE\\(([_a-zA-Z0-9]*)\\)",
{match, Cases0} = re:run(Bin, RE, [{capture,all_but_first,list},global]),
Cases = lists:concat(Cases0),
generate(Name, Cases).
diff --git a/lib/erl_interface/test/all_SUITE_data/runner.c b/lib/erl_interface/test/all_SUITE_data/runner.c
index 24df0f5f40..a474c17722 100644
--- a/lib/erl_interface/test/all_SUITE_data/runner.c
+++ b/lib/erl_interface/test/all_SUITE_data/runner.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. 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
@@ -18,6 +18,7 @@
*/
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl
index bb44b78854..d7df63cc88 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-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,14 +27,16 @@
-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_group/2,end_per_group/2, init_per_testcase/2,
+ end_per_testcase/2,
test_ei_decode_long/1,
test_ei_decode_ulong/1,
test_ei_decode_longlong/1,
test_ei_decode_ulonglong/1,
test_ei_decode_char/1,
test_ei_decode_nonoptimal/1,
- test_ei_decode_misc/1
+ test_ei_decode_misc/1,
+ test_ei_decode_utf8_atom/1
]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -42,7 +45,7 @@ all() ->
[test_ei_decode_long, test_ei_decode_ulong,
test_ei_decode_longlong, test_ei_decode_ulonglong,
test_ei_decode_char, test_ei_decode_nonoptimal,
- test_ei_decode_misc].
+ test_ei_decode_misc, test_ei_decode_utf8_atom].
groups() ->
[].
@@ -59,6 +62,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(_TC, Config) ->
+ Config.
+
+end_per_testcase(_RC, Config) ->
+ Config.
%% ---------------------------------------------------------------------------
@@ -204,15 +212,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,[]),
@@ -220,6 +228,29 @@ test_ei_decode_misc(Config) when is_list(Config) ->
?line runner:recv_eot(P),
ok.
+%% ######################################################################## %%
+
+test_ei_decode_utf8_atom(Config) ->
+ ?line P = runner:start(?test_ei_decode_utf8_atom),
+
+ send_utf8_atom_as_binary(P,"å"),
+ send_utf8_atom_as_binary(P,"ä"),
+ send_term_as_binary(P,'ö'),
+ send_term_as_binary(P,'õ'),
+
+ ?line send_utf8_atom_as_binary(P,[1758]),
+ ?line send_utf8_atom_as_binary(P,[1758,1758]),
+ ?line send_utf8_atom_as_binary(P,[1758,1758,1758]),
+ ?line send_utf8_atom_as_binary(P,[1758,1758,1758,1758]),
+
+ send_utf8_atom_as_binary(P,"a"),
+ send_utf8_atom_as_binary(P,"b"),
+ send_term_as_binary(P,'c'),
+ send_term_as_binary(P,'d'),
+
+ ?line runner:recv_eot(P),
+ ok.
+
%% ######################################################################## %%
@@ -229,6 +260,8 @@ send_term_as_binary(Port, Term) when is_port(Port) ->
send_raw(Port, Bin) when is_port(Port) ->
Port ! {self(), {command, Bin}}.
+send_utf8_atom_as_binary(Port, String) ->
+ Port ! {self(), {command, term_to_binary(uc_atup(String))}}.
send_integers(P) ->
?line send_term_as_binary(P,0), % SMALL_INTEGER_EXT smallest
@@ -303,3 +336,43 @@ send_integers2(P) ->
?line send_term_as_binary(P, 16#ffffffffffffffff), % largest u64
?line send_term_as_binary(P, []), % illegal type
ok.
+
+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_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index b349138ae9..a676e59470 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -1,7 +1,7 @@
/*
* %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
@@ -17,6 +17,8 @@
* %CopyrightEnd%
*/
+#include <string.h>
+
#ifdef VXWORKS
#include "reclaim.h"
#endif
@@ -38,6 +40,24 @@
err, size1, SIZE, (EI_LONGLONG)p);
#endif
+#define ERLANG_ANY (ERLANG_ASCII|ERLANG_LATIN1|ERLANG_UTF8)
+
+struct my_atom {
+ erlang_char_encoding from;
+ erlang_char_encoding was_check;
+ erlang_char_encoding result_check;
+};
+
+/* Allow arrays constants to be part of macro arguments */
+#define P99(...) __VA_ARGS__
+
+int ei_decode_my_atom_as(const char *buf, int *index, char *to,
+ struct my_atom *atom);
+int ei_decode_my_atom(const char *buf, int *index, char *to,
+ struct my_atom *atom);
+int ei_decode_my_string(const char *buf, int *index, char *to,
+ struct my_atom *atom);
+
#define EI_DECODE_2(FUNC,SIZE,TYPE,VAL) \
{ \
TYPE p; \
@@ -129,17 +149,26 @@
} \
} \
-#define EI_DECODE_STRING(FUNC,SIZE,VAL) \
+#define dump(arr, num) { \
+ int i; \
+ message("Dumping " #arr ": "); \
+ for (i = 0; i < num; i++) message("%u, ",(unsigned char)arr[i]); \
+ message("\n"); \
+ }
+
+#define EI_DECODE_STRING_4(FUNC,SIZE,VAL,ATOM) \
{ \
char p[1024]; \
char *buf; \
+ unsigned char val[] = VAL; \
int size1 = 0; \
int size2 = 0; \
int err; \
- message("ei_" #FUNC " should be " #VAL); \
+ struct my_atom atom = ATOM; \
+ message("ei_" #FUNC " should be " #VAL "\n"); \
buf = read_packet(NULL); \
\
- err = ei_ ## FUNC(buf+1, &size1, NULL); \
+ err = ei_ ## FUNC(buf+1, &size1, NULL, &atom); \
message("err = %d, size = %d, expected size = %d\n",err,size1,SIZE); \
if (err != 0) { \
if (err != -1) { \
@@ -150,7 +179,7 @@
return; \
} \
\
- err = ei_ ## FUNC(buf+1, &size2, p); \
+ err = ei_ ## FUNC(buf+1, &size2, p, &atom); \
message("err = %d, size = %d, expected size = %d\n",err,size2,SIZE); \
if (err != 0) { \
if (err != -1) { \
@@ -161,7 +190,7 @@
return; \
} \
\
- if (strcmp(p,VAL) != 0) { \
+ if (strcmp(p,val) != 0) { \
fail("value is not correct"); \
return; \
} \
@@ -177,6 +206,51 @@
} \
} \
+#define EI_DECODE_STRING(FUNC,SIZE,VAL) \
+ EI_DECODE_STRING_4(FUNC,SIZE,VAL, \
+ P99({ERLANG_ANY,ERLANG_ANY,ERLANG_ANY}))
+
+#define EI_DECODE_STRING_FAIL(FUNC,ATOM) \
+ { \
+ char p[1024]; \
+ char *buf; \
+ int size1 = 0; \
+ int size2 = 0; \
+ int err; \
+ struct my_atom atom = ATOM;\
+ message("ei_" #FUNC " should fail\n"); \
+ p[0] = 0; \
+ message("p[0] is %d\n",p[0]); \
+ buf = read_packet(NULL); \
+\
+ err = ei_ ## FUNC(buf+1, &size1, NULL, &atom); \
+ if (err != -1) { \
+ fail("should return -1 if NULL pointer"); \
+ return; \
+ } \
+\
+ err = ei_ ## FUNC(buf+1, &size2, p, &atom); \
+ if (err != -1) { \
+ fail("should return -1"); \
+ return; \
+ } \
+ if (p[0] != 0) { \
+ message("p[0] argument was modified to %u\n",(unsigned char)p[0]); \
+ } \
+\
+ if (size1 != 0) { \
+ fail("size of encoded data should be 0 if NULL"); \
+ return; \
+ } \
+\
+ if (size2 != 0) { \
+ fail("size of encoded data should be 0"); \
+ return; \
+ } \
+ } \
+
+//#define EI_DECODE_UTF8_STRING(FUNC,SIZE,VAL)
+
#define EI_DECODE_BIN(FUNC,SIZE,VAL,LEN) \
{ \
char p[1024]; \
@@ -536,13 +610,13 @@ TESTCASE(test_ei_decode_misc)
EI_DECODE_2(decode_boolean, 8, int, 0);
EI_DECODE_2(decode_boolean, 7, int, 1);
- EI_DECODE_STRING(decode_atom, 6, "foo");
- EI_DECODE_STRING(decode_atom, 3, "");
- EI_DECODE_STRING(decode_atom, 9, "������");
+ EI_DECODE_STRING(decode_my_atom, 6, "foo");
+ EI_DECODE_STRING(decode_my_atom, 3, "");
+ EI_DECODE_STRING(decode_my_atom, 9, "������");
- EI_DECODE_STRING(decode_string, 6, "foo");
- EI_DECODE_STRING(decode_string, 1, "");
- EI_DECODE_STRING(decode_string, 9, "������");
+ EI_DECODE_STRING(decode_my_string, 6, "foo");
+ EI_DECODE_STRING(decode_my_string, 1, "");
+ EI_DECODE_STRING(decode_my_string, 9, "������");
EI_DECODE_BIN(decode_binary, 8, "foo", 3);
EI_DECODE_BIN(decode_binary, 5, "", 0);
@@ -559,3 +633,63 @@ TESTCASE(test_ei_decode_misc)
/* ******************************************************************** */
+TESTCASE(test_ei_decode_utf8_atom)
+{
+
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, P99({229,0}), /* LATIN1 "�" */
+ P99({ERLANG_ANY,ERLANG_LATIN1,ERLANG_LATIN1}));
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, P99({195,164,0}), /* UTF8 "�" */
+ P99({ERLANG_UTF8,ERLANG_LATIN1,ERLANG_UTF8}));
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, P99({246,0}), /* LATIN1 "�" */
+ P99({ERLANG_LATIN1,ERLANG_LATIN1,ERLANG_LATIN1}));
+ EI_DECODE_STRING_FAIL(decode_my_atom_as,
+ P99({ERLANG_ASCII,ERLANG_ANY,ERLANG_ANY}));
+
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, P99({219,158,0}),
+ P99({ERLANG_ANY,ERLANG_UTF8,ERLANG_UTF8}));
+ EI_DECODE_STRING_4(decode_my_atom_as, 6, P99({219,158,219,158,0}),
+ P99({ERLANG_UTF8,ERLANG_UTF8,ERLANG_UTF8}));
+ EI_DECODE_STRING_FAIL(decode_my_atom_as,
+ P99({ERLANG_LATIN1,ERLANG_ANY,ERLANG_ANY}));
+ EI_DECODE_STRING_FAIL(decode_my_atom_as,
+ P99({ERLANG_ASCII,ERLANG_ANY,ERLANG_ANY}));
+
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, "a",
+ P99({ERLANG_ANY,ERLANG_LATIN1,ERLANG_ASCII}));
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, "b",
+ P99({ERLANG_UTF8,ERLANG_LATIN1,ERLANG_ASCII}));
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, "c",
+ P99({ERLANG_LATIN1,ERLANG_LATIN1,ERLANG_ASCII}));
+ EI_DECODE_STRING_4(decode_my_atom_as, 4, "d",
+ P99({ERLANG_ASCII,ERLANG_LATIN1,ERLANG_ASCII}));
+
+ report(1);
+}
+
+/* ******************************************************************** */
+
+int ei_decode_my_atom_as(const char *buf, int *index, char *to,
+ struct my_atom *atom) {
+ erlang_char_encoding was,result;
+ int res = ei_decode_atom_as(buf,index,to,1024,atom->from,&was,&result);
+ if (res != 0)
+ return res;
+ if (!(was & atom->was_check)) {
+ message("Original encoding was %d not %d\n",was,atom->was_check);
+ return -1;
+ } else if (!(result & atom->result_check)) {
+ message("Result encoding was %d not %d\n",result,atom->result_check);
+ return -1;
+ }
+ return res;
+}
+
+int ei_decode_my_atom(const char *buf, int *index, char *to,
+ struct my_atom *atom) {
+ return ei_decode_atom(buf, index, to);
+}
+
+int ei_decode_my_string(const char *buf, int *index, char *to,
+ struct my_atom *atom) {
+ return ei_decode_string(buf, index, to);
+}
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
index 85cb62239b..2271278291 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-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
@@ -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..317e5edecd 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
@@ -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
@@ -29,171 +29,258 @@
*/
-#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];
+ 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 ? a->name : NULL), sizeof(a->name),
+ ERLANG_UTF8, (a ? &a->enc : NULL), 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, NULL);
+ 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;
+ }
+
+ err = t->ei_decode_fp(buf+1, &size2, obj);
+ if (err != 0) {
+ if (err != -1) {
+ fail("decode returned non zero but not -1");
+ } else {
+ fail("decode returned non zero");
+ }
+ return;
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode sizes differs");
+ return;
+ }
+
+ size2 = 0;
+ err = ei_skip_term(buf+1, &size2);
+ if (err != 0) {
+ fail("ei_skip_term returned non zero");
+ return;
+ }
+ if (size1 != size2) {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("skip size differs");
+ return;
+ }
+
+ MESSAGE("ei_encode_%s buf is NULL, arg is type %s", t->name, t->type);
+ size2 = 0;
+ 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 +288,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 +312,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..8d622a9e65 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-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
@@ -33,7 +34,9 @@
test_ei_encode_ulonglong/1,
test_ei_encode_char/1,
test_ei_encode_misc/1,
- test_ei_encode_fails/1
+ test_ei_encode_fails/1,
+ test_ei_encode_utf8_atom/1,
+ test_ei_encode_utf8_atom_len/1
]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -42,7 +45,8 @@ all() ->
[test_ei_encode_long, test_ei_encode_ulong,
test_ei_encode_longlong, test_ei_encode_ulonglong,
test_ei_encode_char, test_ei_encode_misc,
- test_ei_encode_fails].
+ test_ei_encode_fails, test_ei_encode_utf8_atom,
+ test_ei_encode_utf8_atom_len].
groups() ->
[].
@@ -213,19 +217,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 []
@@ -256,6 +260,38 @@ test_ei_encode_fails(Config) when is_list(Config) ->
%% ######################################################################## %%
+test_ei_encode_utf8_atom(Config) ->
+ ?line P = runner:start(?test_ei_encode_utf8_atom),
+
+ ?line {<<119,2,195,133>>,'Å'} = get_buf_and_term(P),
+ ?line {<<100,0,1,197>>,'Å'} = get_buf_and_term(P),
+ ?line {<<100,0,1,197>>,'Å'} = get_buf_and_term(P),
+ ?line {<<119,2,195,133>>,'Å'} = get_buf_and_term(P),
+
+ ?line {<<119,1,$A>>,'A'} = get_buf_and_term(P),
+ ?line {<<100,0,1,$A>>,'A'} = get_buf_and_term(P),
+
+ ?line runner:recv_eot(P),
+ ok.
+
+%% ######################################################################## %%
+test_ei_encode_utf8_atom_len(Config) ->
+ ?line P = runner:start(?test_ei_encode_utf8_atom_len),
+
+ ?line {<<119,2,195,133>>,'Å'} = get_buf_and_term(P),
+ ?line {<<100,0,2,197,196>>,'ÅÄ'} = get_buf_and_term(P),
+ ?line {<<100,0,1,197>>,'Å'} = get_buf_and_term(P),
+ ?line {<<119,4,195,133,195,132>>,'ÅÄ'} = get_buf_and_term(P),
+
+ ?line {<<119,1,$A>>,'A'} = get_buf_and_term(P),
+ ?line {<<100,0,2,$A,$B>>,'AB'} = get_buf_and_term(P),
+ ?line {<<100,0,255,_:(255*8)>>,_} = get_buf_and_term(P),
+
+ ?line runner:recv_eot(P),
+ ok.
+
+%% ######################################################################## %%
+
% We read two packets for each test, the ei_encode and ei_x_encode version....
get_buf_and_term(P) ->
diff --git a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
index c373658152..50fcaace8d 100644
--- a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
+++ b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
@@ -1,7 +1,7 @@
/*
* %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
@@ -208,6 +208,196 @@
ei_x_free(&arg); \
}
+#define EI_ENCODE_3(FUNC,ARG1,ARG2,ARG3) \
+ { \
+ char buf[1024]; \
+ int size1 = 0; \
+ int size2 = 0; \
+ int err; \
+ message("ei_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " encoded as "); \
+ err = ei_ ## FUNC(NULL, &size1, ARG1, ARG2, ARG3); \
+ if (err != 0) { \
+ if (err != -1) { \
+ fail("size calculation returned non zero but not -1"); \
+ return; \
+ } else { \
+ fail("size calculation returned non zero"); \
+ return; \
+ } \
+ } \
+ err = ei_ ## FUNC(buf, &size2, ARG1, ARG2, ARG3); \
+ if (err != 0) { \
+ if (err != -1) { \
+ fail("returned non zero but not -1"); \
+ } else { \
+ fail("returned non zero"); \
+ } \
+ return; \
+ } \
+ if (size1 != size2) { \
+ fail("size differs when arg is NULL or buf"); \
+ return; \
+ } \
+ if (size1 < 1) { \
+ fail("size is < 1"); \
+ return; \
+ } \
+ send_buffer(buf, size1); \
+ } \
+ { \
+ ei_x_buff arg; \
+ int err; \
+ message("ei_x_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " encoded as "); \
+ ei_x_new(&arg); \
+ err = ei_x_ ## FUNC(&arg, ARG1, ARG2, ARG3); \
+ 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_ENCODE_3_FAIL(FUNC,ARG1,ARG2,ARG3) \
+ { \
+ char buf[1024]; \
+ int size1 = 0; \
+ int size2 = 0; \
+ int err; \
+ message("ei_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " encoded as "); \
+ err = ei_ ## FUNC(NULL, &size1, ARG1, ARG2, ARG3); \
+ if (err != -1) { \
+ fail("size calculation returned non -1"); \
+ return; \
+ } \
+ err = ei_ ## FUNC(buf, &size2, ARG1, ARG2, ARG3); \
+ if (err != -1) { \
+ fail("returned non -1"); \
+ } \
+ if (size1 != size2) { \
+ fail("size differs when arg is NULL or buf"); \
+ return; \
+ } \
+ } \
+ { \
+ ei_x_buff arg; \
+ int err; \
+ message("ei_x_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " encoded as "); \
+ ei_x_new(&arg); \
+ err = ei_x_ ## FUNC(&arg, ARG1, ARG2, ARG3); \
+ if (err != -1) { \
+ fail("returned non -1"); \
+ ei_x_free(&arg); \
+ return; \
+ } \
+ ei_x_free(&arg); \
+ }
+
+#define EI_ENCODE_4(FUNC,ARG1,ARG2,ARG3,ARG4) \
+ { \
+ char buf[1024]; \
+ int size1 = 0; \
+ int size2 = 0; \
+ int err; \
+ message("ei_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " " #ARG4 " encoded as "); \
+ err = ei_ ## FUNC(NULL, &size1, ARG1, ARG2, ARG3, ARG4); \
+ if (err != 0) { \
+ if (err != -1) { \
+ fail("size calculation returned non zero but not -1"); \
+ return; \
+ } else { \
+ fail("size calculation returned non zero"); \
+ return; \
+ } \
+ } \
+ err = ei_ ## FUNC(buf, &size2, ARG1, ARG2, ARG3, ARG4); \
+ if (err != 0) { \
+ if (err != -1) { \
+ fail("returned non zero but not -1"); \
+ } else { \
+ fail("returned non zero"); \
+ } \
+ return; \
+ } \
+ if (size1 != size2) { \
+ fail("size differs when arg is NULL or buf"); \
+ return; \
+ } \
+ if (size1 < 1) { \
+ fail("size is < 1"); \
+ return; \
+ } \
+ send_buffer(buf, size1); \
+ } \
+ { \
+ ei_x_buff arg; \
+ int err; \
+ message("ei_x_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " " #ARG4 " encoded as "); \
+ ei_x_new(&arg); \
+ err = ei_x_ ## FUNC(&arg, ARG1, ARG2, ARG3, ARG4); \
+ 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_ENCODE_4_FAIL(FUNC,ARG1,ARG2,ARG3,ARG4) \
+ { \
+ char buf[1024]; \
+ int size1 = 0; \
+ int size2 = 0; \
+ int err; \
+ message("ei_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " " #ARG4 " encoded as "); \
+ err = ei_ ## FUNC(NULL, &size1, ARG1, ARG2, ARG3, ARG4); \
+ if (err != -1) { \
+ fail("size calculation returned non -1"); \
+ return; \
+ } \
+ err = ei_ ## FUNC(buf, &size2, ARG1, ARG2, ARG3, ARG4); \
+ if (err != -1) { \
+ fail("returned non -1"); \
+ } \
+ if (size1 != size2) { \
+ fail("size differs when arg is NULL or buf"); \
+ return; \
+ } \
+ } \
+ { \
+ ei_x_buff arg; \
+ int err; \
+ message("ei_x_" #FUNC " " #ARG1 " " #ARG2 " " #ARG3 " " #ARG4 " encoded as "); \
+ ei_x_new(&arg); \
+ err = ei_x_ ## FUNC(&arg, ARG1, ARG2, ARG3, ARG4); \
+ if (err != -1) { \
+ fail("returned non -1"); \
+ ei_x_free(&arg); \
+ return; \
+ } \
+ ei_x_free(&arg); \
+ }
+
/* ******************************************************************** */
TESTCASE(test_ei_encode_long)
@@ -464,3 +654,57 @@ TESTCASE(test_ei_encode_fails)
report(1);
}
+
+/* ******************************************************************** */
+
+TESTCASE(test_ei_encode_utf8_atom)
+{
+
+ EI_ENCODE_3(encode_atom_as, "�", ERLANG_LATIN1, ERLANG_UTF8);
+ EI_ENCODE_3(encode_atom_as, "�", ERLANG_LATIN1, ERLANG_LATIN1);
+ EI_ENCODE_3(encode_atom_as, "\303\205",
+ ERLANG_UTF8, ERLANG_LATIN1);
+ EI_ENCODE_3(encode_atom_as, "\303\205",
+ ERLANG_UTF8, ERLANG_UTF8);
+
+ EI_ENCODE_3(encode_atom_as, "A", ERLANG_ASCII, ERLANG_UTF8);
+ EI_ENCODE_3(encode_atom_as, "A", ERLANG_ASCII, ERLANG_LATIN1);
+
+ EI_ENCODE_3_FAIL(encode_atom_as, "�", ERLANG_UTF8, ERLANG_UTF8);
+ EI_ENCODE_3_FAIL(encode_atom_as, "�", ERLANG_UTF8, ERLANG_LATIN1);
+ EI_ENCODE_3_FAIL(encode_atom_as, "�", ERLANG_ASCII, ERLANG_UTF8);
+ EI_ENCODE_3_FAIL(encode_atom_as, "�", ERLANG_ASCII, ERLANG_LATIN1);
+
+ EI_ENCODE_3_FAIL(encode_atom_as, "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", ERLANG_ASCII, ERLANG_LATIN1);
+ EI_ENCODE_3_FAIL(encode_atom_as, "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", ERLANG_LATIN1, ERLANG_LATIN1);
+
+ EI_ENCODE_3_FAIL(encode_atom_as, "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", ERLANG_UTF8, ERLANG_LATIN1);
+
+ report(1);
+}
+
+TESTCASE(test_ei_encode_utf8_atom_len)
+{
+
+ EI_ENCODE_4(encode_atom_len_as, "���", 1, ERLANG_LATIN1, ERLANG_UTF8);
+ EI_ENCODE_4(encode_atom_len_as, "���", 2, ERLANG_LATIN1, ERLANG_LATIN1);
+ EI_ENCODE_4(encode_atom_len_as, "\303\205\303\204\303\226", 2,
+ ERLANG_UTF8, ERLANG_LATIN1);
+ EI_ENCODE_4(encode_atom_len_as, "\303\205\303\204\303\226", 4,
+ ERLANG_UTF8, ERLANG_UTF8);
+
+ EI_ENCODE_4(encode_atom_len_as, "ABC", 1, ERLANG_ASCII, ERLANG_UTF8);
+ EI_ENCODE_4(encode_atom_len_as, "ABC", 2, ERLANG_ASCII, ERLANG_LATIN1);
+
+ EI_ENCODE_4_FAIL(encode_atom_len_as, "���", 1, ERLANG_UTF8, ERLANG_UTF8);
+ EI_ENCODE_4_FAIL(encode_atom_len_as, "���", 2, ERLANG_UTF8, ERLANG_LATIN1);
+ EI_ENCODE_4_FAIL(encode_atom_len_as, "���", 3, ERLANG_ASCII, ERLANG_UTF8);
+ EI_ENCODE_4_FAIL(encode_atom_len_as, "���", 4, ERLANG_ASCII, ERLANG_LATIN1);
+
+ EI_ENCODE_4(encode_atom_len_as, "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", 255, ERLANG_ASCII, ERLANG_LATIN1);
+
+ EI_ENCODE_4_FAIL(encode_atom_len_as, "\303\205\303\204\303\226", 1,
+ ERLANG_UTF8, ERLANG_LATIN1);
+
+ report(1);
+}
diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
index 4f6c15ba9c..2f53effd78 100644
--- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
+++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,6 +22,7 @@
#endif
#include "ei_runner.h"
+#include <string.h>
/*
* Purpose: Tests the ei_format() function.
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_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
index cc9b8048ca..8ce89e237a 100644
--- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
+++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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,6 +19,9 @@
#include "ei_runner.h"
+#include <string.h>
+#include <stdlib.h>
+
/*
* Purpose: Tests the ei_print() function.
* Author: Jakob
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c b/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c
index 56e2d43d2f..fd91373c3e 100644
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c
+++ b/lib/erl_interface/test/erl_eterm_SUITE_data/print_term.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. 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
@@ -23,6 +23,7 @@
*/
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 1718f38069..9287e105df 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1 @@
-EI_VSN = 3.7.9
+EI_VSN = 3.7.12
diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml
index 0b1f3eddce..8d9675b801 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>2012</year>
+ <year>2002</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,6 +36,23 @@
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.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>ET 1.4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile
index c68a3f4efd..386169fe95 100644
--- a/lib/et/src/Makefile
+++ b/lib/et/src/Makefile
@@ -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/vsn.mk b/lib/et/vsn.mk
index 167c9d9594..99532ee3f3 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1 +1 @@
-ET_VSN = 1.4.4.2
+ET_VSN = 1.4.4.3
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index b797be0ccb..fd51b05f79 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>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,23 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.2.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index fba840c3bd..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
@@ -124,8 +125,8 @@
-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.
%% The plain assert macro should be defined to do nothing if this file
@@ -142,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.
@@ -170,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.
@@ -189,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)())).
@@ -210,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.
@@ -228,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))).
@@ -248,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)},
@@ -259,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.
@@ -299,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
@@ -324,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))).
@@ -350,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.
@@ -368,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.
@@ -394,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.erl b/lib/eunit/src/eunit.erl
index 51846d73b3..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}
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_lib.erl b/lib/eunit/src/eunit_lib.erl
index ea9e944d7e..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
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 46b8c8b503..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
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 174d197117..798196f8cf 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.3
+EUNIT_VSN = 2.2.4
diff --git a/lib/gs/contribs/bonk/Makefile b/lib/gs/contribs/bonk/Makefile
index a630deaf24..d160ca8b73 100644
--- a/lib/gs/contribs/bonk/Makefile
+++ b/lib/gs/contribs/bonk/Makefile
@@ -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 3af91e1dae..3036e9565e 100644
--- a/lib/gs/contribs/cols/Makefile
+++ b/lib/gs/contribs/cols/Makefile
@@ -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 5f0b047955..308ba0cbb0 100644
--- a/lib/gs/contribs/mandel/Makefile
+++ b/lib/gs/contribs/mandel/Makefile
@@ -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 5f37d164f5..f9d131c315 100644
--- a/lib/gs/contribs/othello/Makefile
+++ b/lib/gs/contribs/othello/Makefile
@@ -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/notes.xml b/lib/gs/doc/src/notes.xml
index 69b1e90ba5..f778c010e9 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,24 @@
</header>
<p>This document describes the changes made to the GS application.</p>
- <section><title>GS 1.5.15.1</title>
+ <section><title>GS 1.5.15.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>GS 1.5.15.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/gs/src/Makefile b/lib/gs/src/Makefile
index 4b7a4523b9..0a63d5466e 100644
--- a/lib/gs/src/Makefile
+++ b/lib/gs/src/Makefile
@@ -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/vsn.mk b/lib/gs/vsn.mk
index 48249cb3d0..5c18153c34 100644
--- a/lib/gs/vsn.mk
+++ b/lib/gs/vsn.mk
@@ -1,2 +1,2 @@
-GS_VSN = 1.5.15.1
+GS_VSN = 1.5.15.2
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 c97de59701..0a2c6e822f 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
@@ -45,11 +45,9 @@
t_atom_vals/1,
t_binary/0,
t_bitstr/0,
- t_bitstrlist/0,
t_boolean/0,
t_byte/0,
t_char/0,
- t_charlist/0,
t_cons/0,
t_cons/2,
t_cons_hd/1,
@@ -72,8 +70,6 @@
t_non_neg_integer/0,
t_pos_integer/0,
t_integers/1,
- t_iodata/0,
- t_iolist/0,
t_is_any/1,
t_is_atom/1,
t_is_binary/1,
@@ -85,7 +81,6 @@
t_is_fun/1,
t_is_integer/1,
t_is_integer/1,
- t_is_list/1,
t_is_nil/1,
t_is_none/1,
t_is_none_or_unit/1,
@@ -118,14 +113,11 @@
t_subtract/2,
t_sup/1,
t_sup/2,
- t_tid/0,
- t_timeout/0,
t_tuple/0,
t_tuple/1,
t_tuple_args/1,
t_tuple_size/1,
- t_tuple_subtypes/1,
- t_unicode_string/0
+ t_tuple_subtypes/1
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -144,111 +136,14 @@ type(M, F, A) ->
-spec type(atom(), atom(), arity(), [erl_types:erl_type()]) -> erl_types:erl_type().
-%%-- binary -------------------------------------------------------------------
-type(binary, at, 2, Xs) ->
- strict(arg_types(binary, at, 2), Xs, fun(_) -> t_integer() end);
-type(binary, bin_to_list, Arity, Xs) when 1 =< Arity, Arity =< 3 ->
- strict(arg_types(binary, bin_to_list, Arity), Xs,
- fun(_) -> t_list(t_integer()) end);
-type(binary, compile_pattern, 1, Xs) ->
- strict(arg_types(binary, compile_pattern, 1), Xs,
- fun(_) -> t_binary_compiled_pattern() end);
-type(binary, copy, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
- strict(arg_types(binary, copy, Arity), Xs,
- fun(_) -> t_binary() end);
-type(binary, decode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
- strict(arg_types(binary, decode_unsigned, Arity), Xs,
- fun(_) -> t_non_neg_integer() end);
-type(binary, encode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
- strict(arg_types(binary, encode_unsigned, Arity), Xs,
- fun(_) -> t_binary() end);
-type(binary, first, 1, Xs) ->
- strict(arg_types(binary, first, 1), Xs, fun(_) -> t_non_neg_integer() end);
-type(binary, last, 1, Xs) ->
- strict(arg_types(binary, last, 1), Xs, fun(_) -> t_non_neg_integer() end);
-type(binary, list_to_bin, 1, Xs) ->
- type(erlang, list_to_binary, 1, Xs);
-type(binary, longest_common_prefix, 1, Xs) ->
- strict(arg_types(binary, longest_common_prefix, 1), Xs,
- fun(_) -> t_integer() end);
-type(binary, longest_common_suffix, 1, Xs) ->
- strict(arg_types(binary, longest_common_suffix, 1), Xs,
- fun(_) -> t_integer() end);
-type(binary, match, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
- strict(arg_types(binary, match, Arity), Xs,
- fun(_) ->
- t_sup(t_atom('nomatch'), t_binary_canonical_part())
- end);
-type(binary, matches, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
- strict(arg_types(binary, matches, Arity), Xs,
- fun(_) -> t_list(t_binary_canonical_part()) end);
-type(binary, part, 2, Xs) ->
- type(erlang, binary_part, 2, Xs);
-type(binary, part, 3, Xs) ->
- type(erlang, binary_part, 3, Xs);
-type(binary, referenced_byte_size, 1, Xs) ->
- strict(arg_types(binary, referenced_byte_size, 1), Xs,
- fun(_) -> t_non_neg_integer() end);
-%%-- code ---------------------------------------------------------------------
-type(code, get_chunk, 2, Xs) ->
- strict(arg_types(code, get_chunk, 2), Xs,
- fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(code, is_module_native, 1, Xs) ->
- strict(arg_types(code, is_module_native, 1), Xs,
- fun (_) -> t_sup(t_boolean(), t_atom('undefined')) end);
-type(code, module_md5, 1, Xs) ->
- strict(arg_types(code, module_md5, 1), Xs,
- fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(code, make_stub_module, 3, Xs) ->
- strict(arg_types(code, make_stub_module, 3), Xs, fun ([Mod,_,_]) -> Mod end);
-type(code, rehash, 0, _) ->
- t_atom('ok');
-%%-- erl_ddll -----------------------------------------------------------------
-type(erl_ddll, demonitor, 1, Xs) ->
- type(erlang, demonitor, 1, Xs);
-type(erl_ddll, format_error_int, 1, Xs) ->
- strict(arg_types(erl_ddll, format_error_int, 1), Xs,
- fun (_) -> t_string() end);
-type(erl_ddll, info, 2, Xs) ->
- strict(arg_types(erl_ddll, info, 2), Xs, fun (_) -> t_atom() end);
-type(erl_ddll, loaded_drivers, 0, _) ->
- t_tuple([t_atom('ok'), t_list(t_string())]);
-type(erl_ddll, monitor, 2, Xs) -> % return type is the same, though args are not
- type(erlang, monitor, 2, Xs);
-type(erl_ddll, try_load, 3, Xs) ->
- strict(arg_types(erl_ddll, try_load, 3), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_atom('already_loaded')]),
- t_tuple([t_atom('ok'), t_atom('loaded')]),
- t_tuple([t_atom('ok'),
- t_atom('pending_driver'), t_reference()]),
- t_tuple([t_atom('error'), t_atom('inconsistent')]),
- t_tuple([t_atom('error'), t_atom('permanent')])])
- end);
-type(erl_ddll, try_unload, 2, Xs) ->
- strict(arg_types(erl_ddll, try_unload, 2), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_atom('pending_process')]),
- t_tuple([t_atom('ok'), t_atom('unloaded')]),
- t_tuple([t_atom('ok'), t_atom('pending_driver')]),
- t_tuple([t_atom('ok'),
- t_atom('pending_driver'), t_reference()]),
- t_tuple([t_atom('error'), t_atom('permanent')]),
- t_tuple([t_atom('error'), t_atom('not_loaded')]),
- t_tuple([t_atom('error'),
- t_atom('not_loaded_by_this_process')])])
- end);
%%-- erlang -------------------------------------------------------------------
type(erlang, halt, 0, _) -> t_none();
type(erlang, halt, 1, _) -> t_none();
type(erlang, halt, 2, _) -> t_none();
type(erlang, exit, 1, _) -> t_none();
-%% Note that exit/2 sends an exit signal to another process.
-type(erlang, exit, 2, _) -> t_atom('true');
type(erlang, error, 1, _) -> t_none();
type(erlang, error, 2, _) -> t_none();
type(erlang, throw, 1, _) -> t_none();
-type(erlang, hibernate, 3, _) -> t_none();
type(erlang, '==', 2, Xs = [X1, X2]) ->
case t_is_atom(X1) andalso t_is_atom(X2) of
true -> type(erlang, '=:=', 2, Xs);
@@ -600,20 +495,12 @@ type(erlang, 'bnot', 1, Xs) ->
{ok, T} -> T
end
end);
-%% This returns (-X)-1, so it often gives a negative result.
-%% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end);
+%% Guard bif, needs to be here.
type(erlang, abs, 1, Xs) ->
strict(arg_types(erlang, abs, 1), Xs, fun ([X]) -> X end);
-type(erlang, adler32, 1, Xs) ->
- strict(arg_types(erlang, adler32, 1), Xs, fun (_) -> t_adler32() end);
-type(erlang, adler32, 2, Xs) ->
- strict(arg_types(erlang, adler32, 2), Xs, fun (_) -> t_adler32() end);
-type(erlang, adler32_combine, 3, Xs) ->
- strict(arg_types(erlang, adler32_combine, 3), Xs,
- fun (_) -> t_adler32() end);
+%% This returns (-X)-1, so it often gives a negative result.
+%% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end);
type(erlang, append, 2, Xs) -> type(erlang, '++', 2, Xs); % alias
-type(erlang, append_element, 2, Xs) ->
- strict(arg_types(erlang, append_element, 2), Xs, fun (_) -> t_tuple() end);
type(erlang, apply, 2, Xs) ->
Fun = fun ([X, _Y]) ->
case t_is_fun(X) of
@@ -626,111 +513,24 @@ type(erlang, apply, 2, Xs) ->
strict(arg_types(erlang, apply, 2), Xs, Fun);
type(erlang, apply, 3, Xs) ->
strict(arg_types(erlang, apply, 3), Xs, fun (_) -> t_any() end);
-type(erlang, atom_to_binary, 2, Xs) ->
- strict(arg_types(erlang, atom_to_binary, 2), Xs, fun (_) -> t_binary() end);
-type(erlang, atom_to_list, 1, Xs) ->
- strict(arg_types(erlang, atom_to_list, 1), Xs, fun (_) -> t_string() end);
+%% Guard bif, needs to be here.
type(erlang, binary_part, 2, Xs) ->
strict(arg_types(erlang, binary_part, 2), Xs, fun (_) -> t_binary() end);
+%% Guard bif, needs to be here.
type(erlang, binary_part, 3, Xs) ->
strict(arg_types(erlang, binary_part, 3), Xs, fun (_) -> t_binary() end);
-type(erlang, binary_to_atom, 2, Xs) ->
- strict(arg_types(erlang, binary_to_atom, 2), Xs, fun (_) -> t_atom() end);
-type(erlang, binary_to_existing_atom, 2, Xs) ->
- type(erlang, binary_to_atom, 2, Xs);
-type(erlang, binary_to_list, 1, Xs) ->
- strict(arg_types(erlang, binary_to_list, 1), Xs,
- fun (_) -> t_list(t_byte()) end);
-type(erlang, binary_to_list, 3, Xs) ->
- strict(arg_types(erlang, binary_to_list, 3), Xs,
- fun (_) -> t_list(t_byte()) end);
-type(erlang, binary_to_term, 1, Xs) ->
- strict(arg_types(erlang, binary_to_term, 1), Xs, fun (_) -> t_any() end);
-type(erlang, binary_to_term, 2, Xs) ->
- strict(arg_types(erlang, binary_to_term, 2), Xs, fun (_) -> t_any() end);
-type(erlang, bitsize, 1, Xs) -> % XXX: TAKE OUT
- type(erlang, bit_size, 1, Xs);
+%% Guard bif, needs to be here.
type(erlang, bit_size, 1, Xs) ->
strict(arg_types(erlang, bit_size, 1), Xs,
fun (_) -> t_non_neg_integer() end);
-type(erlang, bitstr_to_list, 1, Xs) -> % XXX: TAKE OUT
- type(erlang, bitstring_to_list, 1, Xs);
-type(erlang, bitstring_to_list, 1, Xs) ->
- strict(arg_types(erlang, bitstring_to_list, 1), Xs,
- fun (_) -> t_list(t_sup(t_byte(), t_bitstr())) end);
-type(erlang, bump_reductions, 1, Xs) ->
- strict(arg_types(erlang, bump_reductions, 1), Xs,
- fun (_) -> t_atom('true') end);
+%% Guard bif, needs to be here.
type(erlang, byte_size, 1, Xs) ->
strict(arg_types(erlang, byte_size, 1), Xs,
fun (_) -> t_non_neg_integer() end);
-type(erlang, call_on_load_function, 1, Xs) ->
- %% Internal BIF used by on_load.
- strict(arg_types(erlang, call_on_load_function, 1), Xs,
- fun (_) -> t_any() end);
-type(erlang, cancel_timer, 1, Xs) ->
- strict(arg_types(erlang, cancel_timer, 1), Xs,
- fun (_) -> t_sup(t_integer(), t_atom('false')) end);
-type(erlang, check_old_code, 1, Xs) ->
- strict(arg_types(erlang, check_old_code, 1), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, check_process_code, 2, Xs) ->
- strict(arg_types(erlang, check_process_code, 2), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, crc32, 1, Xs) ->
- strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_crc32() end);
-type(erlang, crc32, 2, Xs) ->
- strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_crc32() end);
-type(erlang, crc32_combine, 3, Xs) ->
- strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_crc32() end);
-type(erlang, date, 0, _) ->
- t_date();
-type(erlang, decode_packet, 3, Xs) ->
- strict(arg_types(erlang, decode_packet, 3), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_packet(), t_binary()]),
- t_tuple([t_atom('more'), t_sup([t_non_neg_integer(),
- t_atom('undefined')])]),
- t_tuple([t_atom('error'), t_any()])])
- end);
-type(erlang, delete_module, 1, Xs) ->
- strict(arg_types(erlang, delete_module, 1), Xs,
- fun (_) -> t_sup(t_atom('true'), t_atom('undefined')) end);
-type(erlang, demonitor, 1, Xs) ->
- strict(arg_types(erlang, demonitor, 1), Xs, fun (_) -> t_atom('true') end);
-%% TODO: overapproximation -- boolean only if 'info' is part of arg2 otherwise 'true'
-type(erlang, demonitor, 2, Xs) ->
- strict(arg_types(erlang, demonitor, 2), Xs, fun (_) -> t_boolean() end);
type(erlang, disconnect_node, 1, Xs) ->
strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_sup([t_boolean(), t_atom('ignored')]) end);
-type(erlang, display, 1, _) -> t_atom('true');
-type(erlang, display_string, 1, Xs) ->
- strict(arg_types(erlang, display_string, 1), Xs, fun(_) -> t_atom('true') end);
-type(erlang, display_nl, 0, _) ->
- t_atom('true');
-type(erlang, dist_exit, 3, Xs) ->
- strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end);
-type(erlang, dt_append_vm_tag_data, 1, Xs) ->
- strict(arg_types(erlang, dt_append_vm_tag_data, 1),
- Xs,
- fun(_) -> t_iodata() end);
-type(erlang, dt_get_tag, 0, _) ->
- t_sup(t_binary(), t_atom('undefined'));
-type(erlang, dt_get_tag_data, 0, _) ->
- t_sup(t_binary(), t_atom('undefined'));
-type(erlang, dt_prepend_vm_tag_data, 1, Xs) ->
- strict(arg_types(erlang, dt_prepend_vm_tag_data, 1),
- Xs,
- fun(_) -> t_iodata() end);
-type(erlang, dt_put_tag, 1, Xs) ->
- strict(arg_types(erlang, dt_put_tag, 1), Xs,
- fun(_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(erlang, dt_restore_tag, 1, Xs) ->
- strict(arg_types(erlang, dt_restore_tag, 1), Xs, fun(_) -> t_atom('true') end);
-type(erlang, dt_spread_tag, 1, Xs) ->
- strict(arg_types(erlang, dt_spread_tag, 1), Xs,
- fun(_) -> t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]),
- t_atom('true')) end);
+%% Guard bif, needs to be here.
+%% Also much more expressive than anything you could write in a spec...
type(erlang, element, 2, Xs) ->
strict(arg_types(erlang, element, 2), Xs,
fun ([X1, X2]) ->
@@ -754,95 +554,22 @@ type(erlang, element, 2, Xs) ->
t_sup([type(erlang, element, 2, [X1, Y]) || Y <- Ts])
end
end);
-type(erlang, erase, 0, _) -> t_any();
-type(erlang, erase, 1, _) -> t_any();
-type(erlang, external_size, 1, _) -> t_integer();
-type(erlang, external_size, 2, _) -> t_integer();
-type(erlang, finish_after_on_load, 2, Xs) ->
- %% Internal BIF used by on_load.
- strict(arg_types(erlang, finish_after_on_load, 2), Xs,
- fun (_) -> t_atom('true') end);
+%% Guard bif, needs to be here.
type(erlang, float, 1, Xs) ->
strict(arg_types(erlang, float, 1), Xs, fun (_) -> t_float() end);
-type(erlang, float_to_list, 1, Xs) ->
- strict(arg_types(erlang, float_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, function_exported, 3, Xs) ->
- strict(arg_types(erlang, function_exported, 3), Xs,
- fun (_) -> t_boolean() end);
type(erlang, fun_info, 1, Xs) ->
strict(arg_types(erlang, fun_info, 1), Xs,
fun (_) -> t_list(t_tuple([t_atom(), t_any()])) end);
-type(erlang, fun_info, 2, Xs) ->
- strict(arg_types(erlang, fun_info, 2), Xs,
- fun (_) -> t_tuple([t_atom(), t_any()]) end);
-type(erlang, fun_to_list, 1, Xs) ->
- strict(arg_types(erlang, fun_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, garbage_collect, 0, _) -> t_atom('true');
-type(erlang, garbage_collect, 1, Xs) ->
- strict(arg_types(erlang, garbage_collect, 1), Xs, fun (_) -> t_boolean() end);
-type(erlang, garbage_collect_message_area, 0, _) ->
- t_boolean();
-type(erlang, get, 0, _) -> t_list(t_tuple(2));
-type(erlang, get, 1, _) -> t_any(); % | t_atom('undefined')
type(erlang, get_cookie, 0, _) -> t_atom(); % | t_atom('nocookie')
-type(erlang, get_keys, 1, _) -> t_list();
-type(erlang, get_module_info, 1, Xs) ->
- strict(arg_types(erlang, get_module_info, 1), Xs,
- fun (_) ->
- t_list(t_tuple([t_atom(), t_list(t_tuple([t_atom(), t_any()]))]))
- end);
-type(erlang, get_module_info, 2, Xs) ->
- T_module_info_2_returns =
- t_sup([t_atom(),
- t_list(t_tuple([t_atom(), t_any()])),
- t_list(t_tuple([t_atom(), t_arity(), t_integer()]))]),
- strict(arg_types(erlang, get_module_info, 2), Xs,
- fun ([Module, Item]) ->
- case t_is_atom(Item) of
- true ->
- case t_atom_vals(Item) of
- ['module'] -> t_inf(t_atom(), Module);
- ['imports'] -> t_nil();
- ['exports'] -> t_list(t_tuple([t_atom(), t_arity()]));
- ['functions'] -> t_list(t_tuple([t_atom(), t_arity()]));
- ['attributes'] -> t_list(t_tuple([t_atom(), t_any()]));
- ['compile'] -> t_list(t_tuple([t_atom(), t_any()]));
- ['native_addresses'] -> % [{FunName, Arity, Address}]
- t_list(t_tuple([t_atom(), t_arity(), t_integer()]));
- List when is_list(List) ->
- T_module_info_2_returns;
- unknown ->
- T_module_info_2_returns
- end;
- false ->
- T_module_info_2_returns
- end
- end);
-type(erlang, get_stacktrace, 0, _) ->
- t_list(t_tuple([t_atom(), t_atom(), t_sup([t_arity(), t_list()]),
- t_list()]));
-type(erlang, group_leader, 0, _) -> t_pid();
-type(erlang, group_leader, 2, Xs) ->
- strict(arg_types(erlang, group_leader, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, hash, 2, Xs) ->
- strict(arg_types(erlang, hash, 2), Xs, fun (_) -> t_integer() end);
+%% Guard bif, needs to be here.
type(erlang, hd, 1, Xs) ->
strict(arg_types(erlang, hd, 1), Xs, fun ([X]) -> t_cons_hd(X) end);
-type(erlang, integer_to_list, 1, Xs) ->
- strict(arg_types(erlang, integer_to_list, 1), Xs,
- fun (_) -> t_string() end);
type(erlang, integer_to_list, 2, Xs) ->
strict(arg_types(erlang, integer_to_list, 2), Xs,
fun (_) -> t_string() end);
type(erlang, info, 1, Xs) -> type(erlang, system_info, 1, Xs); % alias
-type(erlang, iolist_size, 1, Xs) ->
- strict(arg_types(erlang, iolist_size, 1), Xs,
- fun (_) -> t_non_neg_integer() end);
-type(erlang, iolist_to_binary, 1, Xs) ->
- strict(arg_types(erlang, iolist_to_binary, 1), Xs,
- fun (_) -> t_binary() end);
-type(erlang, is_alive, 0, _) -> t_boolean();
+%% All type tests are guard BIF's and may be implemented in ways that
+%% cannot be expressed in a type spec, why they are kept in erl_bif_types.
type(erlang, is_atom, 1, Xs) ->
Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_atom(Y) end, t_atom()) end,
strict(arg_types(erlang, is_atom, 1), Xs, Fun);
@@ -851,8 +578,6 @@ type(erlang, is_binary, 1, Xs) ->
check_guard(X, fun (Y) -> t_is_binary(Y) end, t_binary())
end,
strict(arg_types(erlang, is_binary, 1), Xs, Fun);
-type(erlang, is_bitstr, 1, Xs) -> % XXX: TAKE OUT
- type(erlang, is_bitstring, 1, Xs);
type(erlang, is_bitstring, 1, Xs) ->
Fun = fun (X) ->
check_guard(X, fun (Y) -> t_is_bitstr(Y) end, t_bitstr())
@@ -863,8 +588,6 @@ type(erlang, is_boolean, 1, Xs) ->
check_guard(X, fun (Y) -> t_is_boolean(Y) end, t_boolean())
end,
strict(arg_types(erlang, is_boolean, 1), Xs, Fun);
-type(erlang, is_builtin, 3, Xs) ->
- strict(arg_types(erlang, is_builtin, 3), Xs, fun (_) -> t_boolean() end);
type(erlang, is_float, 1, Xs) ->
Fun = fun (X) ->
check_guard(X, fun (Y) -> t_is_float(Y) end, t_float())
@@ -909,9 +632,6 @@ type(erlang, is_pid, 1, Xs) ->
type(erlang, is_port, 1, Xs) ->
Fun = fun (X) -> check_guard(X, fun (Y) -> t_is_port(Y) end, t_port()) end,
strict(arg_types(erlang, is_port, 1), Xs, Fun);
-type(erlang, is_process_alive, 1, Xs) ->
- strict(arg_types(erlang, is_process_alive, 1), Xs,
- fun (_) -> t_boolean() end);
type(erlang, is_record, 2, Xs) ->
Fun = fun ([X, Y]) ->
case t_is_tuple(X) of
@@ -1013,68 +733,9 @@ type(erlang, is_tuple, 1, Xs) ->
check_guard(X, fun (Y) -> t_is_tuple(Y) end, t_tuple())
end,
strict(arg_types(erlang, is_tuple, 1), Xs, Fun);
+%% Guard bif, needs to be here.
type(erlang, length, 1, Xs) ->
strict(arg_types(erlang, length, 1), Xs, fun (_) -> t_non_neg_fixnum() end);
-type(erlang, link, 1, Xs) ->
- strict(arg_types(erlang, link, 1), Xs, fun (_) -> t_atom('true') end);
-type(erlang, list_to_atom, 1, Xs) ->
- strict(arg_types(erlang, list_to_atom, 1), Xs, fun (_) -> t_atom() end);
-type(erlang, list_to_binary, 1, Xs) ->
- strict(arg_types(erlang, list_to_binary, 1), Xs,
- fun (_) -> t_binary() end);
-type(erlang, list_to_bitstr, 1, Xs) ->
- type(erlang, list_to_bitstring, 1, Xs);
-type(erlang, list_to_bitstring, 1, Xs) ->
- strict(arg_types(erlang, list_to_bitstring, 1), Xs,
- fun (_) -> t_bitstr() end);
-type(erlang, list_to_existing_atom, 1, Xs) ->
- strict(arg_types(erlang, list_to_existing_atom, 1), Xs,
- fun (_) -> t_atom() end);
-type(erlang, list_to_float, 1, Xs) ->
- strict(arg_types(erlang, list_to_float, 1), Xs, fun (_) -> t_float() end);
-type(erlang, list_to_integer, 1, Xs) ->
- strict(arg_types(erlang, list_to_integer, 1), Xs,
- fun (_) -> t_integer() end);
-type(erlang, list_to_integer, 2, Xs) ->
- strict(arg_types(erlang, list_to_integer, 2), Xs,
- fun (_) -> t_integer() end);
-type(erlang, list_to_pid, 1, Xs) ->
- strict(arg_types(erlang, list_to_pid, 1), Xs, fun (_) -> t_pid() end);
-type(erlang, list_to_tuple, 1, Xs) ->
- strict(arg_types(erlang, list_to_tuple, 1), Xs, fun (_) -> t_tuple() end);
-type(erlang, load_module, 2, Xs) ->
- strict(arg_types(erlang, load_module, 2), Xs,
- fun ([Mod,_Bin]) -> t_code_load_return(Mod) end);
-type(erlang, load_nif, 2, Xs) ->
- strict(arg_types(erlang, load_nif, 2), Xs,
- fun (_) ->
- Reason = t_atoms(['load_failed', 'bad_lib', 'load',
- 'reload', 'upgrade', 'old_code']),
- RsnPair = t_tuple([Reason, t_string()]),
- t_sup(t_atom('ok'), t_tuple([t_atom('error'), RsnPair]))
- end);
-type(erlang, loaded, 0, _) ->
- t_list(t_atom());
-type(erlang, localtime, 0, Xs) ->
- type(erlang, universaltime, 0, Xs); % same
-type(erlang, localtime_to_universaltime, 1, Xs) ->
- type(erlang, universaltime_to_localtime, 1, Xs); % same
-type(erlang, localtime_to_universaltime, 2, Xs) ->
- strict(arg_types(erlang, localtime_to_universaltime, 2), Xs, % typecheck
- fun ([X,_]) -> type(erlang, localtime_to_universaltime, 1, [X]) end);
-type(erlang, make_fun, 3, Xs) ->
- strict(arg_types(erlang, make_fun, 3), Xs,
- fun ([_, _, Arity]) ->
- case t_number_vals(Arity) of
- [N] ->
- case is_integer(N) andalso 0 =< N andalso N =< 255 of
- true -> t_fun(N, t_any());
- false -> t_none()
- end;
- _Other -> t_fun()
- end
- end);
-type(erlang, make_ref, 0, _) -> t_reference();
type(erlang, make_tuple, 2, Xs) ->
strict(arg_types(erlang, make_tuple, 2), Xs,
fun ([Int, _]) ->
@@ -1091,290 +752,21 @@ type(erlang, make_tuple, 3, Xs) ->
_Other -> t_tuple()
end
end);
-type(erlang, match_spec_test, 3, Xs) ->
- strict(arg_types(erlang, match_spec_test, 3), Xs,
- fun (_) -> t_sup(t_tuple([t_atom('ok'),
- t_any(), % it can be any term
- t_list(t_atom('return_trace')),
- t_match_spec_test_errors()]),
- t_tuple([t_atom('error'),
- t_match_spec_test_errors()])) end);
-type(erlang, md5, 1, Xs) ->
- strict(arg_types(erlang, md5, 1), Xs, fun (_) -> t_binary() end);
-type(erlang, md5_final, 1, Xs) ->
- strict(arg_types(erlang, md5_final, 1), Xs, fun (_) -> t_binary() end);
-type(erlang, md5_init, 0, _) -> t_binary();
-type(erlang, md5_update, 2, Xs) ->
- strict(arg_types(erlang, md5_update, 2), Xs, fun (_) -> t_binary() end);
type(erlang, memory, 0, _) -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
-type(erlang, memory, 1, Xs) ->
- strict(arg_types(erlang, memory, 1), Xs,
- fun ([Type]) ->
- case t_is_atom(Type) of
- true -> t_non_neg_fixnum();
- false ->
- case t_is_list(Type) of
- true -> t_list(t_tuple([t_atom(), t_non_neg_fixnum()]));
- false ->
- t_sup(t_non_neg_fixnum(),
- t_list(t_tuple([t_atom(), t_non_neg_fixnum()])))
- end
- end
- end);
-type(erlang, module_loaded, 1, Xs) ->
- strict(arg_types(erlang, module_loaded, 1), Xs, fun (_) -> t_boolean() end);
-type(erlang, monitor, 2, Xs) ->
- strict(arg_types(erlang, monitor, 2), Xs, fun (_) -> t_reference() end);
-type(erlang, monitor_node, 2, Xs) ->
- strict(arg_types(erlang, monitor_node, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, monitor_node, 3, Xs) ->
- strict(arg_types(erlang, monitor_node, 3), Xs,
- fun (_) -> t_atom('true') end);
type(erlang, nif_error, 1, _) ->
t_any(); % this BIF and the next one are stubs for NIFs and never return
type(erlang, nif_error, 2, Xs) ->
strict(arg_types(erlang, nif_error, 2), Xs, fun (_) -> t_any() end);
+%% Guard bif, needs to be here.
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, nodes, 1, Xs) ->
- strict(arg_types(erlang, nodes, 1), Xs, fun (_) -> t_list(t_node()) end);
-type(erlang, now, 0, _) ->
- t_timestamp();
-type(erlang, open_port, 2, Xs) ->
- strict(arg_types(erlang, open_port, 2), Xs, fun (_) -> t_port() end);
-type(erlang, phash, 2, Xs) ->
- strict(arg_types(erlang, phash, 2), Xs, fun (_) -> t_pos_integer() end);
-type(erlang, phash2, 1, Xs) ->
- strict(arg_types(erlang, phash2, 1), Xs, fun (_) -> t_non_neg_integer() end);
-type(erlang, phash2, 2, Xs) ->
- strict(arg_types(erlang, phash2, 2), Xs, fun (_) -> t_non_neg_integer() end);
-type(erlang, pid_to_list, 1, Xs) ->
- strict(arg_types(erlang, pid_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, port_call, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
- strict(arg_types(erlang, port_call, Arity), Xs, fun (_) -> t_any() end);
-type(erlang, port_close, 1, Xs) ->
- strict(arg_types(erlang, port_close, 1), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, port_command, 2, Xs) ->
- strict(arg_types(erlang, port_command, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, port_command, 3, Xs) ->
- strict(arg_types(erlang, port_command, 3), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, port_connect, 2, Xs) ->
- strict(arg_types(erlang, port_connect, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, port_control, 3, Xs) ->
- strict(arg_types(erlang, port_control, 3), Xs,
- fun (_) -> t_sup(t_string(), t_binary()) end);
-type(erlang, port_get_data, 1, Xs) ->
- strict(arg_types(erlang, port_get_data, 1), 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);
-type(erlang, port_to_list, 1, Xs) ->
- strict(arg_types(erlang, port_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, ports, 0, _) -> t_list(t_port());
-type(erlang, port_set_data, 2, Xs) ->
- strict(arg_types(erlang, port_set_data, 2), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, pre_loaded, 0, _) -> t_list(t_atom());
-type(erlang, process_display, 2, _) -> t_atom('true');
-type(erlang, process_flag, 2, Xs) ->
- T_process_flag_returns = t_sup([t_boolean(), t_atom(), t_non_neg_integer()]),
- strict(arg_types(erlang, process_flag, 2), Xs,
- fun ([Flag, _Option]) ->
- case t_is_atom(Flag) of
- true ->
- case t_atom_vals(Flag) of
- ['error_handler'] -> t_atom();
- ['min_heap_size'] -> t_non_neg_integer();
- ['min_bin_vheap_size'] -> t_non_neg_integer();
- ['scheduler'] -> t_non_neg_integer();
- ['monitor_nodes'] -> t_boolean();
- ['priority'] -> t_process_priority_level();
- ['save_calls'] -> t_non_neg_integer();
- ['trap_exit'] -> t_boolean();
- ['sensitive'] -> t_boolean();
- List when is_list(List) ->
- T_process_flag_returns;
- unknown ->
- T_process_flag_returns
- end;
- false -> % XXX: over-approximation if Flag is tuple
- T_process_flag_returns
- end
- end);
-type(erlang, process_flag, 3, Xs) ->
- strict(arg_types(erlang, process_flag, 3), Xs,
- fun (_) -> t_non_neg_integer() end);
-type(erlang, process_info, 1, Xs) ->
- strict(arg_types(erlang, process_info, 1), Xs,
- fun (_) ->
- t_sup(t_list(t_tuple([t_pinfo(), t_any()])),
- t_atom('undefined'))
- end);
-type(erlang, process_info, 2, Xs) ->
- %% we define all normal return values: the return when the process exists
- %% t_nil() is the return for 'registered_name'; perhaps for more
- T_process_info_2_normal_returns =
- t_sup([t_tuple([t_pinfo_item(), t_any()]), t_nil()]),
- strict(arg_types(erlang, process_info, 2), Xs,
- fun ([_Pid, InfoItem]) ->
- Ret = case t_is_atom(InfoItem) of
- true ->
- case t_atom_vals(InfoItem) of
- ['backtrace'] -> t_tuple([InfoItem, t_binary()]);
- ['current_function'] -> t_tuple([InfoItem, t_mfa()]);
- ['dictionary'] -> t_tuple([InfoItem, t_list()]);
- ['error_handler'] -> t_tuple([InfoItem, t_atom()]);
- ['garbage_collection'] ->
- t_tuple([InfoItem, t_list()]);
- ['group_leader'] -> t_tuple([InfoItem, t_pid()]);
- ['heap_size'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['initial_call'] -> t_tuple([InfoItem, t_mfa()]);
- ['last_calls'] ->
- t_tuple([InfoItem,
- t_sup(t_atom('false'), t_list())]);
- ['links'] -> t_tuple([InfoItem, t_list(t_sup(t_pid(),t_port()))]);
- ['memory'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['message_queue_len'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['messages'] -> t_tuple([InfoItem, t_list()]);
- ['monitored_by'] ->
- t_tuple([InfoItem, t_list(t_pid())]);
- ['monitors'] ->
- t_tuple([InfoItem,
- t_list(t_sup(t_tuple([t_atom('process'),
- t_pid()]),
- t_tuple([t_atom('process'),
- t_tuple([t_atom(),
- t_atom()])])))]);
- ['priority'] ->
- t_tuple([InfoItem, t_process_priority_level()]);
- ['reductions'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['registered_name'] ->
- t_sup(t_tuple([InfoItem, t_atom()]), t_nil());
- ['sequential_trace_token'] ->
- t_tuple([InfoItem, t_any()]); %% Underspecified
- ['stack_size'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['status'] ->
- t_tuple([InfoItem, t_process_status()]);
- ['suspending'] ->
- t_tuple([InfoItem,
- t_list(t_tuple([t_pid(),
- t_non_neg_integer(),
- t_non_neg_integer()]))]);
- ['total_heap_size'] ->
- t_tuple([InfoItem, t_non_neg_integer()]);
- ['trap_exit'] ->
- t_tuple([InfoItem, t_boolean()]);
- List when is_list(List) ->
- T_process_info_2_normal_returns;
- unknown ->
- T_process_info_2_normal_returns
- end;
- false ->
- case t_is_list(InfoItem) of
- true ->
- t_list(t_tuple([t_pinfo_item(), t_any()]));
- false ->
- t_sup(T_process_info_2_normal_returns,
- t_list(t_tuple([t_pinfo_item(), t_any()])))
- end
- end,
- t_sup([Ret, t_atom('undefined')])
- end);
-type(erlang, processes, 0, _) -> t_list(t_pid());
-type(erlang, purge_module, 1, Xs) ->
- strict(arg_types(erlang, purge_module, 1), Xs,
- fun (_) -> t_atom('true') end);
-type(erlang, put, 2, Xs) ->
- strict(arg_types(erlang, put, 2), Xs, fun (_) -> t_any() end);
-type(erlang, raise, 3, _) -> t_none();
-type(erlang, read_timer, 1, Xs) ->
- strict(arg_types(erlang, read_timer, 1), Xs,
- fun (_) -> t_sup(t_non_neg_integer(), t_atom('false')) end);
-type(erlang, ref_to_list, 1, Xs) ->
- strict(arg_types(erlang, ref_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, register, 2, Xs) ->
- strict(arg_types(erlang, register, 2), Xs, fun (_) -> t_atom('true') end);
-type(erlang, registered, 0, _) -> t_list(t_atom());
-type(erlang, resume_process, 1, Xs) ->
- strict(arg_types(erlang, resume_process, 1), Xs,
- fun (_) -> t_any() end); %% TODO: overapproximation -- fix this
+%% Guard bif, needs to be here.
type(erlang, round, 1, Xs) ->
strict(arg_types(erlang, round, 1), Xs, fun (_) -> t_integer() end);
-type(erlang, posixtime_to_universaltime, 1, Xs) ->
- strict(arg_types(erlang, posixtime_to_universaltime, 1), Xs,
- fun(_) -> t_tuple([t_date(), t_time()]) end);
+%% Guard bif, needs to be here.
type(erlang, self, 0, _) -> t_pid();
-type(erlang, send, 2, Xs) -> type(erlang, '!', 2, Xs); % alias
-type(erlang, send, 3, Xs) ->
- strict(arg_types(erlang, send, 3), Xs,
- fun (_) -> t_sup(t_atom('ok'), t_sendoptions()) end);
-type(erlang, send_after, 3, Xs) ->
- strict(arg_types(erlang, send_after, 3), Xs, fun (_) -> t_reference() end);
-type(erlang, seq_trace, 2, Xs) ->
- strict(arg_types(erlang, seq_trace, 2), Xs,
- fun (_) -> t_sup(t_seq_trace_info_returns(), t_tuple(5)) end);
-type(erlang, seq_trace_info, 1, Xs) ->
- strict(arg_types(erlang, seq_trace_info, 1), Xs,
- fun ([Item]) ->
- case t_atom_vals(Item) of
- ['label'] ->
- t_sup(t_tuple([Item, t_non_neg_integer()]), t_nil());
- ['serial'] ->
- t_sup(t_tuple([Item, t_tuple([t_non_neg_integer(),
- t_non_neg_integer()])]),
- t_nil());
- ['send'] -> t_tuple([Item, t_boolean()]);
- ['receive'] -> t_tuple([Item, t_boolean()]);
- ['print'] -> t_tuple([Item, t_boolean()]);
- ['timestamp'] -> t_tuple([Item, t_boolean()]);
- List when is_list(List) ->
- t_seq_trace_info_returns();
- unknown ->
- t_seq_trace_info_returns()
- end
- end);
-type(erlang, seq_trace_print, 1, Xs) ->
- strict(arg_types(erlang, seq_trace_print, 1), Xs, fun (_) -> t_boolean() end);
-type(erlang, seq_trace_print, 2, Xs) ->
- strict(arg_types(erlang, seq_trace_print, 2), Xs, fun (_) -> t_boolean() end);
type(erlang, set_cookie, 2, Xs) ->
strict(arg_types(erlang, set_cookie, 2), Xs, fun (_) -> t_atom('true') end);
type(erlang, setelement, 3, Xs) ->
@@ -1408,148 +800,22 @@ type(erlang, setelement, 3, Xs) ->
t_sup([type(erlang, setelement, 3, [X1, Y, X3]) || Y <- Ts])
end
end);
-type(erlang, setnode, 2, Xs) ->
- strict(arg_types(erlang, setnode, 2), Xs, fun (_) -> t_atom('true') end);
-type(erlang, setnode, 3, Xs) ->
- strict(arg_types(erlang, setnode, 3), Xs, fun (_) -> t_atom('true') end);
+%% Guard bif, needs to be here.
type(erlang, size, 1, Xs) ->
strict(arg_types(erlang, size, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, spawn, 1, Xs) ->
strict(arg_types(erlang, spawn, 1), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 2, Xs) ->
strict(arg_types(erlang, spawn, 2), Xs, fun (_) -> t_pid() end);
-type(erlang, spawn, 3, Xs) ->
- strict(arg_types(erlang, spawn, 3), Xs, fun (_) -> t_pid() end);
type(erlang, spawn, 4, Xs) ->
strict(arg_types(erlang, spawn, 4), Xs, fun (_) -> t_pid() end);
type(erlang, spawn_link, 1, Xs) -> type(erlang, spawn, 1, Xs); % same
type(erlang, spawn_link, 2, Xs) -> type(erlang, spawn, 2, Xs); % same
-type(erlang, spawn_link, 3, Xs) -> type(erlang, spawn, 3, Xs); % same
type(erlang, spawn_link, 4, Xs) -> type(erlang, spawn, 4, Xs); % same
-type(erlang, spawn_opt, 1, Xs) ->
- strict(arg_types(erlang, spawn_opt, 1), Xs,
- fun ([Tuple]) ->
- Fun = fun (TS) ->
- [_, _, _, List] = t_tuple_args(TS),
- t_spawn_opt_return(List)
- end,
- t_sup([Fun(TS) || TS <- t_tuple_subtypes(Tuple)])
- end);
-type(erlang, spawn_opt, 2, Xs) ->
- strict(arg_types(erlang, spawn_opt, 2), Xs,
- fun ([_, List]) -> t_spawn_opt_return(List) end);
-type(erlang, spawn_opt, 3, Xs) ->
- strict(arg_types(erlang, spawn_opt, 3), Xs,
- fun ([_, _, List]) -> t_spawn_opt_return(List) end);
-type(erlang, spawn_opt, 4, Xs) ->
- strict(arg_types(erlang, spawn_opt, 4), Xs,
- fun ([_, _, _, List]) -> t_spawn_opt_return(List) end);
-type(erlang, split_binary, 2, Xs) ->
- strict(arg_types(erlang, split_binary, 2), Xs,
- fun (_) -> t_tuple([t_binary(), t_binary()]) end);
-type(erlang, start_timer, 3, Xs) ->
- strict(arg_types(erlang, start_timer, 3), Xs, fun (_) -> t_reference() end);
-type(erlang, statistics, 1, Xs) ->
- strict(arg_types(erlang, statistics, 1), Xs,
- fun ([Type]) ->
- T_statistics_1 = t_sup([t_non_neg_integer(),
- t_tuple([t_non_neg_integer(),
- t_non_neg_integer()]),
- %% When called with the argument 'io'.
- t_tuple([t_tuple([t_atom('input'),
- t_non_neg_integer()]),
- t_tuple([t_atom('output'),
- t_non_neg_integer()])]),
- t_tuple([t_non_neg_integer(),
- t_non_neg_integer(),
- t_non_neg_integer()])]),
- case t_atom_vals(Type) of
- ['context_switches'] ->
- t_tuple([t_non_neg_integer(), t_integer(0)]);
- ['exact_reductions'] ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]);
- ['garbage_collection'] ->
- t_tuple([t_non_neg_integer(),
- t_non_neg_integer(),
- t_integer(0)]);
- ['io'] ->
- t_tuple([t_tuple([t_atom('input'), t_non_neg_integer()]),
- t_tuple([t_atom('output'), t_non_neg_integer()])]);
- ['reductions'] ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]);
- ['run_queue'] ->
- t_non_neg_integer();
- ['runtime'] ->
- t_tuple([t_non_neg_integer(), t_integer(0)]);
- ['wall_clock'] ->
- t_tuple([t_non_neg_integer(), t_integer(0)]);
- ['scheduler_wall_time'] ->
- t_list(t_tuple([t_integer(), t_number(), t_number()]));
- List when is_list(List) ->
- T_statistics_1;
- unknown ->
- T_statistics_1
- end
- end);
type(erlang, subtract, 2, Xs) -> type(erlang, '--', 2, Xs); % alias
type(erlang, suspend_process, 1, Xs) ->
strict(arg_types(erlang, suspend_process, 1), Xs,
fun (_) -> t_atom('true') end);
-type(erlang, suspend_process, 2, Xs) ->
- strict(arg_types(erlang, suspend_process, 2), Xs,
- fun (_) -> t_boolean() end);
-type(erlang, system_flag, 2, Xs) ->
- strict(arg_types(erlang, system_flag, 2), Xs,
- fun ([Flag,_Value]) ->
- %% this provides an overapproximation of all return values
- T_system_flag_2 = t_sup([t_boolean(),
- t_integer(),
- t_sequential_tracer(),
- t_system_cpu_topology(),
- t_system_multi_scheduling()]),
- case t_is_atom(Flag) of
- true ->
- case t_atom_vals(Flag) of
- ['backtrace_depth'] ->
- t_non_neg_fixnum();
- ['cpu_topology'] ->
- t_system_cpu_topology();
- ['debug_flags'] ->
- t_atom('true');
- ['display_items'] ->
- t_non_neg_fixnum();
- ['fullsweep_after'] ->
- t_non_neg_fixnum();
- ['min_heap_size'] ->
- t_non_neg_fixnum();
- ['min_bin_vheap_size'] ->
- t_non_neg_fixnum();
- ['multi_scheduling'] ->
- t_system_multi_scheduling();
- ['schedulers_online'] ->
- t_pos_fixnum();
- ['scheduler_bind_type'] ->
- t_scheduler_bind_type_results();
- ['sequential_tracer'] ->
- t_sequential_tracer();
- ['trace_control_word'] ->
- t_integer();
- ['scheduler_wall_time'] ->
- t_boolean();
- List when is_list(List) ->
- T_system_flag_2;
- unknown ->
- T_system_flag_2
- end;
- false ->
- case t_is_integer(Flag) of % SHOULD BE: t_is_fixnum
- true ->
- t_atom('true');
- false ->
- T_system_flag_2
- end
- end
- end);
type(erlang, system_info, 1, Xs) ->
strict(arg_types(erlang, system_info, 1), Xs,
fun ([Type]) ->
@@ -1569,12 +835,8 @@ type(erlang, system_info, 1, Xs) ->
t_list(t_tuple([t_atom(),
t_list(t_tuple([t_atom(),
t_any()]))]))]);
- ['build_type'] ->
- t_system_build_type_return();
['break_ignored'] ->
t_boolean();
- ['c_compiler_used'] ->
- t_tuple([t_atom(), t_any()]);
['cpu_topology'] ->
t_system_cpu_topology();
['compat_rel'] ->
@@ -1586,9 +848,7 @@ type(erlang, system_info, 1, Xs) ->
['dist'] ->
t_binary();
['dist_ctrl'] ->
- t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port])]));
- ['driver_version'] ->
- t_string();
+ t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port()])]));
%% elib_malloc is intentionally not included,
%% because it scheduled for removal in R15.
['endian'] ->
@@ -1608,27 +868,18 @@ type(erlang, system_info, 1, Xs) ->
t_binary();
['internal_cpu_topology'] -> %% Undocumented internal feature
t_internal_cpu_topology();
- ['kernel_poll'] ->
- t_boolean();
['loaded'] ->
t_binary();
['logical_processors'] ->
t_non_neg_fixnum();
['machine'] ->
t_string();
- ['min_heap_size'] ->
- t_tuple([t_atom('min_heap_size'),
- t_non_neg_integer()]);
- ['min_bin_vheap_size'] ->
- t_tuple([t_atom('min_bin_vheap_size'),
- t_non_neg_integer()]);
['multi_scheduling'] ->
t_system_multi_scheduling();
['multi_scheduling_blockers'] ->
t_list(t_pid());
['os_type'] ->
t_tuple([t_sup([t_atom('unix'),
- t_atom('vxworks'),
t_atom('win32')]),
t_atom()]);
['os_version'] ->
@@ -1638,6 +889,12 @@ type(erlang, system_info, 1, Xs) ->
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'] ->
@@ -1667,8 +924,6 @@ type(erlang, system_info, 1, Xs) ->
t_non_neg_fixnum();
['trace_control_word'] ->
t_integer();
- ['update_cpu_info'] ->
- t_sup([t_atom('changed'), t_atom('unchanged')]);
['version'] ->
t_string();
['wordsize'] ->
@@ -1682,56 +937,13 @@ type(erlang, system_info, 1, Xs) ->
t_any() %% overapproximation as the return value might change
end
end);
-type(erlang, system_monitor, 0, Xs) ->
- strict(arg_types(erlang, system_monitor, 0), Xs,
- fun (_) -> t_system_monitor_settings() end);
-type(erlang, system_monitor, 1, Xs) ->
- strict(arg_types(erlang, system_monitor, 1), Xs,
- fun (_) -> t_system_monitor_settings() end);
-type(erlang, system_monitor, 2, Xs) ->
- strict(arg_types(erlang, system_monitor, 2), Xs,
- fun (_) -> t_system_monitor_settings() end);
-type(erlang, system_profile, 0, _) ->
- t_system_profile_return();
-type(erlang, system_profile, 2, Xs) ->
- strict(arg_types(erlang, system_profile, 2), Xs,
- fun (_) -> t_system_profile_return() end);
-type(erlang, term_to_binary, 1, Xs) ->
- strict(arg_types(erlang, term_to_binary, 1), Xs, fun (_) -> t_binary() end);
-type(erlang, term_to_binary, 2, Xs) ->
- strict(arg_types(erlang, term_to_binary, 2), Xs, fun (_) -> t_binary() end);
-type(erlang, time, 0, _) ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer(), t_non_neg_integer()]);
+%% Guard bif, needs to be here.
type(erlang, tl, 1, Xs) ->
strict(arg_types(erlang, tl, 1), Xs, fun ([X]) -> t_cons_tl(X) end);
-type(erlang, trace, 3, Xs) ->
- strict(arg_types(erlang, trace, 3), Xs, fun (_) -> t_integer() end);
-type(erlang, trace_delivered, 1, Xs) ->
- strict(arg_types(erlang, trace_delivered, 1), Xs,
- fun (_) -> t_reference() end);
-type(erlang, trace_info, 2, Xs) ->
- strict(arg_types(erlang, trace_info, 2), Xs,
- fun (_) ->
- t_tuple([t_atom(),
- t_sup([%% the following is info about a PID
- t_list(t_atom()), t_pid(), t_port(),
- %% the following is info about a func
- t_atom('global'), t_atom('local'),
- t_atom('false'), t_atom('true'),
- t_list(), t_pid(), t_port(),
- t_integer(),
- t_list(t_tuple([t_atom(), t_any()])),
- %% and this is the 'not found' value
- t_atom('undefined')])])
- end);
-type(erlang, trace_pattern, 2, Xs) ->
- strict(arg_types(erlang, trace_pattern, 2), Xs,
- fun (_) -> t_non_neg_fixnum() end); %% num of MFAs that match pattern
-type(erlang, trace_pattern, 3, Xs) ->
- strict(arg_types(erlang, trace_pattern, 3), Xs,
- fun (_) -> t_non_neg_fixnum() end); %% num of MFAs that match pattern
+%% Guard bif, needs to be here.
type(erlang, trunc, 1, Xs) ->
strict(arg_types(erlang, trunc, 1), Xs, fun (_) -> t_integer() end);
+%% Guard bif, needs to be here.
type(erlang, tuple_size, 1, Xs) ->
strict(arg_types(erlang, tuple_size, 1), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, tuple_to_list, 1, Xs) ->
@@ -1754,266 +966,10 @@ type(erlang, tuple_to_list, 1, Xs) ->
end
end
end);
-type(erlang, universaltime, 0, _) ->
- t_tuple([t_date(), t_time()]);
-type(erlang, universaltime_to_localtime, 1, Xs) ->
- strict(arg_types(erlang, universaltime_to_localtime, 1), Xs,
- fun ([T]) -> T end);
-type(erlang, universaltime_to_posixtime, 1, Xs) ->
- strict(arg_types(erlang, universaltime_to_posixtime,1), Xs,
- fun(_) -> t_integer() end);
-type(erlang, unlink, 1, Xs) ->
- strict(arg_types(erlang, unlink, 1), Xs, fun (_) -> t_atom('true') end);
-type(erlang, unregister, 1, Xs) ->
- strict(arg_types(erlang, unregister, 1), Xs, fun (_) -> t_atom('true') end);
-type(erlang, whereis, 1, Xs) ->
- strict(arg_types(erlang, whereis, 1), Xs,
- fun (_) -> t_sup([t_pid(), t_port(), t_atom('undefined')]) end);
type(erlang, yield, 0, _) -> t_atom('true');
-%%-- erl_prim_loader ----------------------------------------------------------
-type(erl_prim_loader, get_file, 1, Xs) ->
- strict(arg_types(erl_prim_loader, get_file, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_binary(), t_string()]),
- t_atom('error'))
- end);
-type(erl_prim_loader, get_path, 0, _) ->
- t_tuple([t_atom('ok'), t_list(t_string())]);
-type(erl_prim_loader, set_path, 1, Xs) ->
- strict(arg_types(erl_prim_loader, set_path, 1), Xs,
- fun (_) -> t_atom('ok') end);
-%%-- error_logger -------------------------------------------------------------
-type(error_logger, warning_map, 0, _) ->
- t_sup([t_atom('info'), t_atom('warning'), t_atom('error')]);
-%%-- erts_debug ---------------------------------------------------------------
-type(erts_debug, breakpoint, 2, Xs) ->
- strict(arg_types(erts_debug, breakpoint, 2), Xs, fun (_) -> t_fixnum() end);
-type(erts_debug, disassemble, 1, Xs) ->
- strict(arg_types(erts_debug, disassemble, 1), Xs,
- fun (_) -> t_sup([t_atom('false'),
- t_atom('undef'),
- t_tuple([t_integer(), t_binary(), t_mfa()])]) end);
-type(erts_debug, display, 1, _) ->
- t_string();
-type(erts_debug, dist_ext_to_term, 2, Xs) ->
- strict(arg_types(erts_debug, dist_ext_to_term, 2), Xs,
- fun (_) -> t_any() end);
-type(erts_debug, dump_monitors, 1, Xs) ->
- strict(arg_types(erts_debug, dump_monitors, 1), Xs,
- fun(_) -> t_atom('true') end);
-type(erts_debug, dump_links, 1, Xs) ->
- strict(arg_types(erts_debug, dump_links, 1), Xs,
- fun(_) -> t_atom('true') end);
-type(erts_debug, flat_size, 1, Xs) ->
- strict(arg_types(erts_debug, flat_size, 1), Xs, fun (_) -> t_integer() end);
-type(erts_debug, get_internal_state, 1, _) ->
- t_any();
-type(erts_debug, instructions, 0, _) ->
- t_list(t_list(t_byte()));
-type(erts_debug, lock_counters, 1, Xs) ->
- strict(arg_types(erts_debug, lock_counters, 1), Xs,
- fun ([Arg]) ->
- case t_is_atom(Arg) of
- true ->
- case t_atom_vals(Arg) of
- ['enabled'] -> t_boolean();
- ['info'] -> t_any();
- ['clear'] -> t_atom(ok);
- _ -> t_sup([t_boolean(), t_any(), t_atom('ok')])
- end;
- false ->
- case t_is_tuple(Arg) of
- true -> t_boolean();
- false -> t_sup([t_boolean(), t_any(), t_atom('ok')])
- end
- end
- end);
-type(erts_debug, same, 2, Xs) ->
- strict(arg_types(erts_debug, same, 2), Xs, fun (_) -> t_boolean() end);
-type(erts_debug, set_internal_state, 2, _) ->
- t_any();
%%-- ets ----------------------------------------------------------------------
-type(ets, all, 0, _) ->
- t_list(t_tab());
-type(ets, delete, 1, Xs) ->
- strict(arg_types(ets, delete, 1), Xs, fun (_) -> t_atom('true') end);
-type(ets, delete, 2, Xs) ->
- strict(arg_types(ets, delete, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, delete_all_objects, 1, Xs) ->
- strict(arg_types(ets, delete_all_objects, 1), Xs,
- fun (_) -> t_atom('true') end);
-type(ets, delete_object, 2, Xs) ->
- strict(arg_types(ets, delete_object, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, first, 1, Xs) ->
- strict(arg_types(ets, first, 1), Xs, fun (_) -> t_any() end);
-type(ets, give_away, 3, Xs) ->
- strict(arg_types(ets, give_away, 3), Xs, fun (_) -> t_atom('true') end);
-type(ets, info, 1, Xs) ->
- strict(arg_types(ets, info, 1), Xs,
- fun (_) ->
- t_sup(t_list(t_tuple([t_ets_info_items(), t_any()])),
- t_atom('undefined'))
- end);
-type(ets, info, 2, Xs) ->
- strict(arg_types(ets, info, 2), Xs, fun (_) -> t_any() end);
-type(ets, insert, 2, Xs) ->
- strict(arg_types(ets, insert, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, insert_new, 2, Xs) ->
- strict(arg_types(ets, insert_new, 2), Xs, fun (_) -> t_boolean() end);
-type(ets, is_compiled_ms, 1, Xs) ->
- strict(arg_types(ets, is_compiled_ms, 1), Xs, fun (_) -> t_boolean() end);
-type(ets, last, 1, Xs) ->
- type(ets, first, 1, Xs);
-type(ets, lookup, 2, Xs) ->
- strict(arg_types(ets, lookup, 2), Xs, fun (_) -> t_list(t_tuple()) end);
-type(ets, lookup_element, 3, Xs) ->
- strict(arg_types(ets, lookup_element, 3), Xs, fun (_) -> t_any() end);
-type(ets, match, 1, Xs) ->
- strict(arg_types(ets, match, 1), Xs, fun (_) -> t_matchres() end);
-type(ets, match, 2, Xs) ->
- strict(arg_types(ets, match, 2), Xs, fun (_) -> t_list() end);
-type(ets, match, 3, Xs) ->
- strict(arg_types(ets, match, 3), Xs, fun (_) -> t_matchres() end);
-type(ets, match_object, 1, Xs) -> type(ets, match, 1, Xs);
-type(ets, match_object, 2, Xs) -> type(ets, match, 2, Xs);
-type(ets, match_object, 3, Xs) -> type(ets, match, 3, Xs);
-type(ets, match_spec_compile, 1, Xs) ->
- strict(arg_types(ets, match_spec_compile, 1), Xs, fun (_) -> t_any() end);
-type(ets, match_spec_run_r, 3, Xs) ->
- strict(arg_types(ets, match_spec_run_r, 3), Xs, fun (_) -> t_list() end);
-type(ets, member, 2, Xs) ->
- strict(arg_types(ets, member, 2), Xs, fun (_) -> t_boolean() end);
-type(ets, new, 2, Xs) ->
- strict(arg_types(ets, new, 2), Xs, fun (_) -> t_tab() end);
-type(ets, next, 2, Xs) ->
- strict(arg_types(ets, next, 2), Xs,
- %% t_any below stands for: term() | '$end_of_table'
- fun (_) -> t_any() end);
-type(ets, prev, 2, Xs) -> type(ets, next, 2, Xs);
type(ets, rename, 2, Xs) ->
strict(arg_types(ets, rename, 2), Xs, fun ([_, Name]) -> Name end);
-type(ets, safe_fixtable, 2, Xs) ->
- strict(arg_types(ets, safe_fixtable, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, select, 1, Xs) ->
- strict(arg_types(ets, select, 1), Xs, fun (_) -> t_matchres() end);
-type(ets, select, 2, Xs) ->
- strict(arg_types(ets, select, 2), Xs, fun (_) -> t_list() end);
-type(ets, select, 3, Xs) ->
- strict(arg_types(ets, select, 3), Xs, fun (_) -> t_matchres() end);
-type(ets, select_count, 2, Xs) ->
- strict(arg_types(ets, select_count, 2), Xs,
- fun (_) -> t_non_neg_fixnum() end);
-type(ets, select_delete, 2, Xs) ->
- strict(arg_types(ets, select_delete, 2), Xs,
- fun (_) -> t_non_neg_fixnum() end);
-type(ets, select_reverse, 1, Xs) -> type(ets, select, 1, Xs);
-type(ets, select_reverse, 2, Xs) -> type(ets, select, 2, Xs);
-type(ets, select_reverse, 3, Xs) -> type(ets, select, 3, Xs);
-type(ets, setopts, 2, Xs) ->
- strict(arg_types(ets, setopts, 2), Xs, fun (_) -> t_atom('true') end);
-type(ets, slot, 2, Xs) ->
- strict(arg_types(ets, slot, 2), Xs,
- fun (_) -> t_sup(t_list(t_tuple()), t_atom('$end_of_table')) end);
-type(ets, update_counter, 3, Xs) ->
- strict(arg_types(ets, update_counter, 3), Xs,
- fun ([_, _, Op]) ->
- case t_is_integer(Op) of
- true -> t_integer();
- false ->
- case t_is_tuple(Op) of
- true -> t_integer();
- false ->
- case t_is_list(Op) of
- true -> t_list(t_integer());
- false ->
- case t_is_nil(Op) of
- true -> t_nil();
- false -> t_sup([t_integer(), t_list(t_integer())])
- end
- end
- end
- end
- end);
-type(ets, update_element, 3, Xs) ->
- strict(arg_types(ets, update_element, 3), Xs, fun (_) -> t_boolean() end);
-%%-- file ---------------------------------------------------------------------
-type(file, native_name_encoding, 0, _) ->
- t_file_encoding();
-%%-- prim_file ----------------------------------------------------------------
-type(prim_file, internal_name2native, 1, Xs) ->
- strict(arg_types(prim_file, internal_name2native, 1), Xs,
- fun (_) -> t_binary() end);
-type(prim_file, internal_native2name, 1, Xs) ->
- strict(arg_types(prim_file, internal_native2name, 1), Xs,
- fun (_) -> t_prim_file_name() end);
-type(prim_file, internal_normalize_utf8, 1, Xs) ->
- strict(arg_types(prim_file, internal_normalize_utf8, 1), Xs,
- fun (_) -> t_unicode_string() end);
-%%-- gen_tcp ------------------------------------------------------------------
-%% NOTE: All type information for this module added to avoid loss of precision
-type(gen_tcp, accept, 1, Xs) ->
- strict(arg_types(gen_tcp, accept, 1), Xs, fun (_) -> t_gen_tcp_accept() end);
-type(gen_tcp, accept, 2, Xs) ->
- strict(arg_types(gen_tcp, accept, 2), Xs, fun (_) -> t_gen_tcp_accept() end);
-type(gen_tcp, connect, 3, Xs) ->
- strict(arg_types(gen_tcp, connect, 3), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, connect, 4, Xs) ->
- strict(arg_types(gen_tcp, connect, 4), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, listen, 2, Xs) ->
- strict(arg_types(gen_tcp, listen, 2), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, recv, 2, Xs) ->
- strict(arg_types(gen_tcp, recv, 2), Xs, fun (_) -> t_gen_tcp_recv() end);
-type(gen_tcp, recv, 3, Xs) ->
- strict(arg_types(gen_tcp, recv, 3), Xs, fun (_) -> t_gen_tcp_recv() end);
-type(gen_tcp, send, 2, Xs) ->
- strict(arg_types(gen_tcp, send, 2), Xs,
- fun (_) ->
- t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_tcp, shutdown, 2, Xs) ->
- strict(arg_types(gen_tcp, shutdown, 2), Xs,
- fun (_) ->
- t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-%%-- gen_udp ------------------------------------------------------------------
-%% NOTE: All type information for this module added to avoid loss of precision
-type(gen_udp, open, 1, Xs) ->
- strict(arg_types(gen_udp, open, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_udp, open, 2, Xs) ->
- strict(arg_types(gen_udp, open, 2), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_inet_posix_error()]))
- end);
-type(gen_udp, recv, 2, Xs) ->
- strict(arg_types(gen_udp, recv, 2), Xs, fun (_) -> t_gen_udp_recv() end);
-type(gen_udp, recv, 3, Xs) ->
- strict(arg_types(gen_udp, recv, 3), Xs, fun (_) -> t_gen_udp_recv() end);
-type(gen_udp, send, 4, Xs) ->
- strict(arg_types(gen_udp, send, 4), Xs,
- fun (_) ->
- t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_sup(t_atom('not_owner'),
- t_inet_posix_error())]))
- end);
%%-- hipe_bifs ----------------------------------------------------------------
type(hipe_bifs, add_ref, 2, Xs) ->
strict(arg_types(hipe_bifs, add_ref, 2), Xs, fun (_) -> t_nil() end);
@@ -2115,7 +1071,7 @@ type(hipe_bifs, ref_set, 2, Xs) ->
strict(arg_types(hipe_bifs, ref_set, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, remove_refs_from, 1, Xs) ->
strict(arg_types(hipe_bifs, remove_refs_from, 1), Xs,
- fun (_) -> t_nil() end);
+ fun (_) -> t_atom('ok') end);
type(hipe_bifs, set_funinfo_native_address, 3, Xs) ->
strict(arg_types(hipe_bifs, set_funinfo_native_address, 3), Xs,
fun (_) -> t_nil() end);
@@ -2136,26 +1092,6 @@ type(hipe_bifs, write_u32, 2, Xs) ->
strict(arg_types(hipe_bifs, write_u32, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, write_u64, 2, Xs) ->
strict(arg_types(hipe_bifs, write_u64, 2), Xs, fun (_) -> t_nil() end);
-%%-- io -----------------------------------------------------------------------
-type(io, format, 1, Xs) ->
- strict(arg_types(io, format, 1), Xs, fun (_) -> t_atom('ok') end);
-type(io, format, 2, Xs) ->
- strict(arg_types(io, format, 2), Xs, fun (_) -> t_atom('ok') end);
-type(io, format, 3, Xs) ->
- strict(arg_types(io, format, 3), Xs, fun (_) -> t_atom('ok') end);
-type(io, fwrite, 1, Xs) -> type(io, format, 1, Xs); % same
-type(io, fwrite, 2, Xs) -> type(io, format, 2, Xs); % same
-type(io, fwrite, 3, Xs) -> type(io, format, 3, Xs); % same
-type(io, put_chars, 1, Xs) ->
- strict(arg_types(io, put_chars, 1), Xs, fun (_) -> t_atom('ok') end);
-type(io, put_chars, 2, Xs) ->
- strict(arg_types(io, put_chars, 2), Xs, fun (_) -> t_atom('ok') end);
-%%-- io_lib -------------------------------------------------------------------
-type(io_lib, format, 2, Xs) ->
- strict(arg_types(io_lib, format, 2), Xs,
- %% t_list() because the character list might be arbitrarily nested
- fun (_) -> t_list(t_sup(t_char(), t_list())) end);
-type(io_lib, fwrite, 2, Xs) -> type(io_lib, format, 2, Xs); % same
%%-- lists --------------------------------------------------------------------
type(lists, all, 2, Xs) ->
strict(arg_types(lists, all, 2), Xs,
@@ -2525,8 +1461,6 @@ type(lists, merge, 2, Xs) ->
end
end
end);
-%% type(lists, merge, 3, Xs) ->
-%% type(lists, merge3, 3, Xs) ->
type(lists, min, 1, Xs) ->
strict(arg_types(lists, min, 1), Xs, fun ([L]) -> t_list_elements(L) end);
type(lists, nth, 2, Xs) ->
@@ -2563,10 +1497,6 @@ type(lists, reverse, 1, Xs) ->
strict(arg_types(lists, reverse, 1), Xs, fun ([X]) -> X end);
type(lists, reverse, 2, Xs) ->
type(erlang, '++', 2, Xs); % reverse-onto is just like append
-type(lists, seq, 2, Xs) ->
- strict(arg_types(lists, seq, 2), Xs, fun (_) -> t_list(t_integer()) end);
-type(lists, seq, 3, Xs) ->
- strict(arg_types(lists, seq, 3), Xs, fun (_) -> t_list(t_integer()) end);
type(lists, sort, 1, Xs) ->
strict(arg_types(lists, sort, 1), Xs, fun ([X]) -> X end);
type(lists, sort, 2, Xs) ->
@@ -2673,95 +1603,7 @@ type(lists, zipwith, 3, Xs) ->
type(lists, zipwith3, 4, Xs) ->
strict(arg_types(lists, zipwith3, 4), Xs,
fun ([F,_As,_Bs,_Cs]) -> t_sup(t_list(t_fun_range(F)), t_nil()) end);
-%%-- math ---------------------------------------------------------------------
-type(math, acos, 1, Xs) ->
- strict(arg_types(math, acos, 1), Xs, fun (_) -> t_float() end);
-type(math, acosh, 1, Xs) ->
- strict(arg_types(math, acosh, 1), Xs, fun (_) -> t_float() end);
-type(math, asin, 1, Xs) ->
- strict(arg_types(math, asin, 1), Xs, fun (_) -> t_float() end);
-type(math, asinh, 1, Xs) ->
- strict(arg_types(math, asinh, 1), Xs, fun (_) -> t_float() end);
-type(math, atan, 1, Xs) ->
- strict(arg_types(math, atan, 1), Xs, fun (_) -> t_float() end);
-type(math, atan2, 2, Xs) ->
- strict(arg_types(math, atan2, 2), Xs, fun (_) -> t_float() end);
-type(math, atanh, 1, Xs) ->
- strict(arg_types(math, atanh, 1), Xs, fun (_) -> t_float() end);
-type(math, cos, 1, Xs) ->
- strict(arg_types(math, cos, 1), Xs, fun (_) -> t_float() end);
-type(math, cosh, 1, Xs) ->
- strict(arg_types(math, cosh, 1), Xs, fun (_) -> t_float() end);
-type(math, erf, 1, Xs) ->
- strict(arg_types(math, erf, 1), Xs, fun (_) -> t_float() end);
-type(math, erfc, 1, Xs) ->
- strict(arg_types(math, erfc, 1), Xs, fun (_) -> t_float() end);
-type(math, exp, 1, Xs) ->
- strict(arg_types(math, exp, 1), Xs, fun (_) -> t_float() end);
-type(math, log, 1, Xs) ->
- strict(arg_types(math, log, 1), Xs, fun (_) -> t_float() end);
-type(math, log10, 1, Xs) ->
- strict(arg_types(math, log10, 1), Xs, fun (_) -> t_float() end);
-type(math, pi, 0, _) -> t_float();
-type(math, pow, 2, Xs) ->
- strict(arg_types(math, pow, 2), Xs, fun (_) -> t_float() end);
-type(math, sin, 1, Xs) ->
- strict(arg_types(math, sin, 1), Xs, fun (_) -> t_float() end);
-type(math, sinh, 1, Xs) ->
- strict(arg_types(math, sinh, 1), Xs, fun (_) -> t_float() end);
-type(math, sqrt, 1, Xs) ->
- strict(arg_types(math, sqrt, 1), Xs, fun (_) -> t_float() end);
-type(math, tan, 1, Xs) ->
- strict(arg_types(math, tan, 1), Xs, fun (_) -> t_float() end);
-type(math, tanh, 1, Xs) ->
- strict(arg_types(math, tanh, 1), Xs, fun (_) -> t_float() end);
-%%-- net_kernel ---------------------------------------------------------------
-type(net_kernel, dflag_unicode_io, 1, Xs) ->
- strict(arg_types(net_kernel, dflag_unicode_io, 1), Xs,
- fun (_) -> t_boolean() end);
-%%-- ordsets ------------------------------------------------------------------
-type(ordsets, filter, 2, Xs) ->
- type(lists, filter, 2, Xs);
-type(ordsets, fold, 3, Xs) ->
- type(lists, foldl, 3, Xs);
-%%-- os -----------------------------------------------------------------------
-type(os, getenv, 0, _) -> t_list(t_string());
-type(os, getenv, 1, Xs) ->
- strict(arg_types(os, getenv, 1), Xs,
- fun (_) -> t_sup(t_string(), t_atom('false')) end);
-type(os, getpid, 0, _) -> t_string();
-type(os, putenv, 2, Xs) ->
- strict(arg_types(os, putenv, 2), Xs, fun (_) -> t_atom('true') end);
-type(os, timestamp, 0, _) ->
- t_timestamp();
-%%-- re -----------------------------------------------------------------------
-type(re, compile, 1, Xs) ->
- strict(arg_types(re, compile, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_re_MP()]),
- t_tuple([t_atom('error'), t_re_ErrorSpec()]))
- end);
-type(re, compile, 2, Xs) ->
- strict(arg_types(re, compile, 2), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom('ok'), t_re_MP()]),
- t_tuple([t_atom('error'), t_re_ErrorSpec()]))
- end);
-type(re, run, 2, Xs) ->
- strict(arg_types(re, run, 2), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('match'), t_re_Captured()]),
- t_atom('nomatch'),
- t_tuple([t_atom('error'), t_re_ErrorSpec()])])
- end);
-type(re, run, 3, Xs) ->
- strict(arg_types(re, run, 3), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('match'), t_re_Captured()]),
- t_atom('match'),
- t_atom('nomatch'),
- t_tuple([t_atom('error'), t_re_ErrorSpec()])])
- end);
+
%%-- string -------------------------------------------------------------------
type(string, chars, 2, Xs) -> % NOTE: added to avoid loss of information
strict(arg_types(string, chars, 2), Xs, fun (_) -> t_string() end);
@@ -2780,41 +1622,6 @@ type(string, chars, 3, Xs) -> % NOTE: added to avoid loss of information
end
end
end);
-type(string, concat, 2, Xs) -> % NOTE: added to avoid loss of information
- strict(arg_types(string, concat, 2), Xs, fun (_) -> t_string() end);
-type(string, equal, 2, Xs) -> % NOTE: added to avoid loss of information
- strict(arg_types(string, equal, 2), Xs, fun (_) -> t_boolean() end);
-type(string, to_float, 1, Xs) ->
- strict(arg_types(string, to_float, 1), Xs,
- fun (_) -> t_sup(t_tuple([t_float(), t_string()]),
- t_tuple([t_atom('error'),
- t_sup(t_atom('no_float'),
- t_atom('not_a_list'))]))
- end);
-type(string, to_integer, 1, Xs) ->
- strict(arg_types(string, to_integer, 1), Xs,
- fun (_) -> t_sup(t_tuple([t_integer(), t_string()]),
- t_tuple([t_atom('error'),
- t_sup(t_atom('no_integer'),
- t_atom('not_a_list'))]))
- end);
-%%-- unicode ------------------------------------------------------------------
-type(unicode, characters_to_binary, 2, Xs) ->
- strict(arg_types(unicode, characters_to_binary, 2), Xs,
- fun (_) ->
- t_sup([t_binary(),
- t_tuple([t_atom('error'), t_binary(), t_ML()]),
- t_tuple([t_atom('incomplete'), t_binary(), t_ML()])])
- end);
-type(unicode, characters_to_list, 2, Xs) ->
- strict(arg_types(unicode, characters_to_list, 2), Xs,
- fun (_) ->
- t_sup([t_string(),
- t_tuple([t_atom('error'), t_string(), t_ML()]),
- t_tuple([t_atom('incomplete'), t_string(), t_ML()])])
- end);
-type(unicode, bin_is_7bit, 1, Xs) ->
- strict(arg_types(unicode, bin_is_7bit, 1), Xs, fun (_) -> t_boolean() end);
%%-----------------------------------------------------------------------------
type(M, F, A, Xs) when is_atom(M), is_atom(F),
@@ -3269,107 +2076,6 @@ key_comparisons_fail(X0, KeyPos, TupleList) ->
-spec arg_types(atom(), atom(), arity()) -> [erl_types:erl_type()] | 'unknown'.
-%%------- binary --------------------------------------------------------------
-arg_types(binary, at, 2) ->
- [t_binary(), t_non_neg_integer()];
-arg_types(binary, bin_to_list, 1) ->
- [t_binary()];
-arg_types(binary, bin_to_list, 2) ->
- [t_binary(), t_binary_part()];
-arg_types(binary, bin_to_list, 3) ->
- [t_binary(), t_integer(), t_non_neg_integer()];
-arg_types(binary, compile_pattern, 1) ->
- [t_sup(t_binary(), t_list(t_binary()))];
-arg_types(binary, copy, 1) ->
- [t_binary()];
-arg_types(binary, copy, 2) ->
- [t_binary(), t_non_neg_integer()];
-arg_types(binary, decode_unsigned, 1) ->
- [t_binary()];
-arg_types(binary, decode_unsigned, 2) ->
- [t_binary(), t_endian()];
-arg_types(binary, encode_unsigned, 1) ->
- [t_non_neg_integer()];
-arg_types(binary, encode_unsigned, 2) ->
- [t_non_neg_integer(), t_endian()];
-arg_types(binary, first, 1) ->
- [t_binary()];
-arg_types(binary, last, 1) ->
- [t_binary()];
-arg_types(binary, list_to_bin, 1) ->
- arg_types(erlang, list_to_binary, 1);
-arg_types(binary, longest_common_prefix, 1) ->
- [t_list(t_binary())];
-arg_types(binary, longest_common_suffix, 1) ->
- [t_list(t_binary())];
-arg_types(binary, match, 2) ->
- [t_binary(), t_binary_pattern()];
-arg_types(binary, match, 3) ->
- [t_binary(), t_binary_pattern(), t_binary_options()];
-arg_types(binary, matches, 2) ->
- [t_binary(), t_binary_pattern()];
-arg_types(binary, matches, 3) ->
- [t_binary(), t_binary_pattern(), t_binary_options()];
-arg_types(binary, part, 2) ->
- arg_types(erlang, binary_part, 2);
-arg_types(binary, part, 3) ->
- arg_types(erlang, binary_part, 3);
-arg_types(binary, referenced_byte_size, 1) ->
- [t_binary()];
-%%------- code ----------------------------------------------------------------
-arg_types(code, get_chunk, 2) ->
- [t_binary(), t_string()];
-arg_types(code, is_module_native, 1) ->
- [t_atom()];
-arg_types(code, module_md5, 1) ->
- [t_binary()];
-arg_types(code, make_stub_module, 3) ->
- [t_atom(), t_binary(), t_tuple([t_list(), t_list()])];
-arg_types(code, rehash, 0) ->
- [];
-%%------- erl_ddll ------------------------------------------------------------
-arg_types(erl_ddll, demonitor, 1) ->
- arg_types(erlang, demonitor, 1);
-arg_types(erl_ddll, format_error_int, 1) ->
- [t_sup([t_atom('inconsistent'),
- t_atom('linked_in_driver'),
- t_atom('permanent'),
- t_atom('not_loaded'),
- t_atom('not_loaded_by_this_process'),
- t_atom('not_pending'),
- t_atom('already_loaded'),
- t_atom('unloading')])];
-arg_types(erl_ddll, info, 2) ->
- [t_sup([t_atom(), t_string()]),
- t_sup([t_atom('awaiting_load'),
- t_atom('awaiting_unload'),
- t_atom('driver_options'),
- t_atom('linked_in_driver'),
- t_atom('permanent'),
- t_atom('port_count'),
- t_atom('processes')])];
-arg_types(erl_ddll, loaded_drivers, 0) ->
- [];
-arg_types(erl_ddll, monitor, 2) ->
- [t_atom('driver'),
- t_tuple([t_atom(), t_sup([t_atom('loaded'), t_atom('unloaded')])])];
-arg_types(erl_ddll, try_load, 3) ->
- [t_sup([t_atom(), t_string(), t_nonempty_list(t_sup([t_atom(), t_string()]))]),
- t_sup([t_atom(), t_string()]),
- t_list(t_sup([t_tuple([t_atom('driver_options'),
- t_list(t_atom('kill_ports'))]),
- t_tuple([t_atom('monitor'),
- t_sup([t_atom('pending_driver'),
- t_atom('pending')])]),
- t_tuple([t_atom('reload'),
- t_sup([t_atom('pending_driver'),
- t_atom('pending')])])]))];
-arg_types(erl_ddll, try_unload, 2) ->
- [t_sup([t_atom(), t_string(), t_nonempty_list(t_sup([t_atom(), t_string()]))]),
- t_list(t_sup([t_atom('kill_ports'),
- t_tuple([t_atom('monitor'),
- t_sup([t_atom('pending_driver'),
- t_atom('pending')])])]))];
%%------- erlang --------------------------------------------------------------
arg_types(erlang, '!', 2) ->
Pid = t_sup([t_pid(), t_port(), t_atom(),
@@ -3431,18 +2137,11 @@ arg_types(erlang, 'bsl', 2) ->
[t_integer(), t_integer()];
arg_types(erlang, 'bnot', 1) ->
[t_integer()];
+%% Guard bif, needs to be here.
arg_types(erlang, abs, 1) ->
[t_number()];
-arg_types(erlang, adler32, 1) ->
- [t_iodata()];
-arg_types(erlang, adler32, 2) ->
- [t_adler32(), t_iodata()];
-arg_types(erlang, adler32_combine, 3) ->
- [t_adler32(), t_adler32(), t_non_neg_integer()];
arg_types(erlang, append, 2) ->
arg_types(erlang, '++', 2);
-arg_types(erlang, append_element, 2) ->
- [t_tuple(), t_any()];
arg_types(erlang, apply, 2) ->
[t_sup(t_tuple([t_module(),
t_atom()]),
@@ -3450,179 +2149,58 @@ arg_types(erlang, apply, 2) ->
t_list()];
arg_types(erlang, apply, 3) ->
[t_sup(t_atom(), t_tuple()), t_atom(), t_list()];
-arg_types(erlang, atom_to_binary, 2) ->
- [t_atom(), t_encoding_a2b()];
-arg_types(erlang, atom_to_list, 1) ->
- [t_atom()];
+%% Guard bif, needs to be here.
arg_types(erlang, binary_part, 2) ->
[t_binary(), t_tuple([t_non_neg_integer(), t_integer()])];
+%% Guard bif, needs to be here.
arg_types(erlang, binary_part, 3) ->
[t_binary(), t_non_neg_integer(), t_integer()];
-arg_types(erlang, binary_to_atom, 2) ->
- [t_binary(), t_encoding_a2b()];
-arg_types(erlang, binary_to_existing_atom, 2) ->
- arg_types(erlang, binary_to_atom, 2);
-arg_types(erlang, binary_to_list, 1) ->
- [t_binary()];
-arg_types(erlang, binary_to_list, 3) ->
- [t_binary(), t_pos_integer(), t_pos_integer()]; % I want fixnum, but cannot
-arg_types(erlang, binary_to_term, 1) ->
- [t_binary()];
-arg_types(erlang, binary_to_term, 2) ->
- [t_binary(), t_list(t_atom('safe'))];
-arg_types(erlang, bitsize, 1) -> % XXX: TAKE OUT
- arg_types(erlang, bit_size, 1);
+%% Guard bif, needs to be here.
arg_types(erlang, bit_size, 1) ->
[t_bitstr()];
-arg_types(erlang, bitstr_to_list, 1) -> % XXX: TAKE OUT
- arg_types(erlang, bitstring_to_list, 1);
-arg_types(erlang, bitstring_to_list, 1) ->
- [t_bitstr()];
-arg_types(erlang, bump_reductions, 1) ->
- [t_pos_fixnum()];
+%% Guard bif, needs to be here.
arg_types(erlang, byte_size, 1) ->
[t_binary()];
-arg_types(erlang, call_on_load_function, 1) ->
- [t_atom()];
-arg_types(erlang, cancel_timer, 1) ->
- [t_reference()];
-arg_types(erlang, check_old_code, 1) ->
- [t_atom()];
-arg_types(erlang, check_process_code, 2) ->
- [t_pid(), t_atom()];
-arg_types(erlang, crc32, 1) ->
- [t_iodata()];
-arg_types(erlang, crc32, 2) ->
- [t_crc32(), t_iodata()];
-arg_types(erlang, crc32_combine, 3) ->
- [t_crc32(), t_crc32(), t_non_neg_integer()];
-arg_types(erlang, date, 0) ->
- [];
-arg_types(erlang, decode_packet, 3) ->
- [t_decode_packet_type(), t_binary(), t_list(t_decode_packet_option())];
-arg_types(erlang, delete_module, 1) ->
- [t_atom()];
-arg_types(erlang, demonitor, 1) ->
- [t_reference()];
-arg_types(erlang, demonitor, 2) ->
- [t_reference(), t_list(t_atoms(['flush', 'info']))];
arg_types(erlang, disconnect_node, 1) ->
[t_node()];
-arg_types(erlang, display, 1) ->
- [t_any()];
-arg_types(erlang, display_nl, 0) ->
- [];
-arg_types(erlang, display_string, 1) ->
- [t_string()];
-arg_types(erlang, dist_exit, 3) ->
- [t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())];
-arg_types(erlang, dt_append_vm_tag_data, 1) ->
- [t_iodata()];
-arg_types(erlang, dt_get_tag, 0) ->
- [];
-arg_types(erlang, dt_get_tag_data, 0) ->
- [];
-arg_types(erlang, dt_prepend_vm_tag_data, 1) ->
- [t_iodata()];
-arg_types(erlang, dt_put_tag, 1) ->
- [t_sup(t_binary(), t_atom('undefined'))];
-arg_types(erlang, dt_restore_tag, 1) ->
- [t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), t_atom('true'))];
-arg_types(erlang, dt_spread_tag, 1) ->
- [t_boolean()];
-arg_types(erlang, element, 2) ->
- [t_pos_fixnum(), t_tuple()];
-arg_types(erlang, erase, 0) ->
+arg_types(erlang, halt, 0) ->
[];
-arg_types(erlang, erase, 1) ->
- [t_any()];
+arg_types(erlang, halt, 1) ->
+ [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])];
+arg_types(erlang, halt, 2) ->
+ [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]),
+ t_list(t_tuple([t_atom('flush'), t_boolean()]))];
arg_types(erlang, error, 1) ->
[t_any()];
arg_types(erlang, error, 2) ->
[t_any(), t_list()];
arg_types(erlang, exit, 1) ->
[t_any()];
-arg_types(erlang, exit, 2) ->
- [t_sup(t_pid(), t_port()), t_any()];
-arg_types(erlang, external_size, 1) ->
- [t_any()]; % takes any term as input
-arg_types(erlang, external_size, 2) ->
- [t_any(), t_list()]; % takes any term as input and a list of options
-arg_types(erlang, finish_after_on_load, 2) ->
- [t_atom(), t_boolean()];
+%% Guard bif, needs to be here.
+arg_types(erlang, element, 2) ->
+ [t_pos_fixnum(), t_tuple()];
+%% Guard bif, needs to be here.
arg_types(erlang, float, 1) ->
[t_number()];
-arg_types(erlang, float_to_list, 1) ->
- [t_float()];
-arg_types(erlang, function_exported, 3) ->
- [t_atom(), t_atom(), t_arity()];
arg_types(erlang, fun_info, 1) ->
[t_fun()];
-arg_types(erlang, fun_info, 2) ->
- [t_fun(), t_atom()];
-arg_types(erlang, fun_to_list, 1) ->
- [t_fun()];
-arg_types(erlang, garbage_collect, 0) ->
- [];
-arg_types(erlang, garbage_collect, 1) ->
- [t_pid()];
-arg_types(erlang, garbage_collect_message_area, 0) ->
- [];
-arg_types(erlang, get, 0) ->
- [];
-arg_types(erlang, get, 1) ->
- [t_any()];
arg_types(erlang, get_cookie, 0) ->
[];
-arg_types(erlang, get_keys, 1) ->
- [t_any()];
-arg_types(erlang, get_stacktrace, 0) ->
- [];
-arg_types(erlang, get_module_info, 1) ->
- [t_atom()];
-arg_types(erlang, get_module_info, 2) ->
- [t_atom(), t_module_info_2()];
-arg_types(erlang, group_leader, 0) ->
- [];
-arg_types(erlang, group_leader, 2) ->
- [t_pid(), t_pid()];
-arg_types(erlang, halt, 0) ->
- [];
-arg_types(erlang, halt, 1) ->
- [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])];
-arg_types(erlang, halt, 2) ->
- [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]),
- t_list(t_tuple([t_atom('flush'), t_boolean()]))];
-arg_types(erlang, hash, 2) ->
- [t_any(), t_integer()];
+%% Guard bif, needs to be here.
arg_types(erlang, hd, 1) ->
[t_cons()];
-arg_types(erlang, hibernate, 3) ->
- [t_atom(), t_atom(), t_list()];
arg_types(erlang, info, 1) ->
arg_types(erlang, system_info, 1); % alias
-arg_types(erlang, iolist_to_binary, 1) ->
- [t_sup(t_iolist(), t_binary())];
-arg_types(erlang, iolist_size, 1) ->
- [t_sup(t_iolist(), t_binary())];
-arg_types(erlang, integer_to_list, 1) ->
- [t_integer()];
arg_types(erlang, integer_to_list, 2) ->
[t_integer(), t_from_range(2, 36)];
-arg_types(erlang, is_alive, 0) ->
- [];
arg_types(erlang, is_atom, 1) ->
[t_any()];
arg_types(erlang, is_binary, 1) ->
[t_any()];
-arg_types(erlang, is_bitstr, 1) -> % XXX: TAKE OUT
- arg_types(erlang, is_bitstring, 1);
arg_types(erlang, is_bitstring, 1) ->
[t_any()];
arg_types(erlang, is_boolean, 1) ->
[t_any()];
-arg_types(erlang, is_builtin, 3) ->
- [t_atom(), t_atom(), t_arity()];
arg_types(erlang, is_float, 1) ->
[t_any()];
arg_types(erlang, is_function, 1) ->
@@ -3639,8 +2217,6 @@ arg_types(erlang, is_pid, 1) ->
[t_any()];
arg_types(erlang, is_port, 1) ->
[t_any()];
-arg_types(erlang, is_process_alive, 1) ->
- [t_pid()];
arg_types(erlang, is_record, 2) ->
[t_any(), t_atom()];
arg_types(erlang, is_record, 3) ->
@@ -3649,553 +2225,77 @@ arg_types(erlang, is_reference, 1) ->
[t_any()];
arg_types(erlang, is_tuple, 1) ->
[t_any()];
+%% Guard bif, needs to be here.
arg_types(erlang, length, 1) ->
[t_list()];
-arg_types(erlang, link, 1) ->
- [t_sup(t_pid(), t_port())];
-arg_types(erlang, list_to_atom, 1) ->
- [t_string()];
-arg_types(erlang, list_to_binary, 1) ->
- [t_iolist()];
-arg_types(erlang, list_to_bitstr, 1) -> % XXX: TAKE OUT
- arg_types(erlang, list_to_bitstring, 1);
-arg_types(erlang, list_to_bitstring, 1) ->
- [t_bitstrlist()];
-arg_types(erlang, list_to_existing_atom, 1) ->
- [t_string()];
-arg_types(erlang, list_to_float, 1) ->
- [t_list(t_byte())];
-arg_types(erlang, list_to_integer, 1) ->
- [t_list(t_byte())];
-arg_types(erlang, list_to_integer, 2) ->
- [t_list(t_byte()), t_from_range(2, 36)];
-arg_types(erlang, list_to_pid, 1) ->
- [t_string()];
-arg_types(erlang, list_to_tuple, 1) ->
- [t_list()];
-arg_types(erlang, load_module, 2) ->
- [t_atom(), t_binary()];
-arg_types(erlang, load_nif, 2) ->
- [t_string(), t_any()];
-arg_types(erlang, loaded, 0) ->
- [];
-arg_types(erlang, localtime, 0) ->
- [];
-arg_types(erlang, localtime_to_universaltime, 1) ->
- [t_tuple([t_date(), t_time()])];
-arg_types(erlang, localtime_to_universaltime, 2) ->
- arg_types(erlang, localtime_to_universaltime, 1) ++
- [t_sup(t_boolean(), t_atom('undefined'))];
-arg_types(erlang, make_fun, 3) ->
- [t_atom(), t_atom(), t_arity()];
-arg_types(erlang, make_ref, 0) ->
- [];
arg_types(erlang, make_tuple, 2) ->
[t_non_neg_fixnum(), t_any()]; % the value 0 is OK as first argument
arg_types(erlang, make_tuple, 3) ->
[t_non_neg_fixnum(), t_any(), t_list(t_tuple([t_pos_integer(), t_any()]))];
-arg_types(erlang, match_spec_test, 3) ->
- [t_sup(t_list(), t_tuple()),
- t_any(),
- t_sup(t_atom('table'), t_atom('trace'))];
-arg_types(erlang, md5, 1) ->
- [t_sup(t_iolist(), t_binary())];
-arg_types(erlang, md5_final, 1) ->
- [t_binary()];
-arg_types(erlang, md5_init, 0) ->
- [];
-arg_types(erlang, md5_update, 2) ->
- [t_binary(), t_sup(t_iolist(), t_binary())];
arg_types(erlang, memory, 0) ->
[];
-arg_types(erlang, memory, 1) ->
- Arg = t_atoms(['total', 'processes', 'processes_used', 'system',
- 'atom', 'atom_used', 'binary', 'code', 'ets',
- 'maximum']),
- [t_sup(Arg, t_list(Arg))];
-arg_types(erlang, module_loaded, 1) ->
- [t_atom()];
-arg_types(erlang, monitor, 2) ->
- [t_atom(), t_sup([t_pid(), t_atom(), t_tuple([t_atom(), t_node()])])];
-arg_types(erlang, monitor_node, 2) ->
- [t_node(), t_boolean()];
-arg_types(erlang, monitor_node, 3) ->
- [t_node(), t_boolean(), t_list(t_atom('allow_passive_connect'))];
arg_types(erlang, nif_error, 1) ->
[t_any()];
arg_types(erlang, nif_error, 2) ->
[t_any(), t_list()];
+%% Guard bif, needs to be here.
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, nodes, 1) ->
- NodesArg = t_atoms(['visible', 'hidden', 'connected', 'this', 'known']),
- [t_sup(NodesArg, t_list(NodesArg))];
-arg_types(erlang, now, 0) ->
- [];
-arg_types(erlang, open_port, 2) ->
- ArgT = t_sup(t_unicode_string(), t_binary()),
- [t_sup(t_atom(), t_sup([t_tuple([t_atom('spawn'), t_string()]),
- t_tuple([t_atom('spawn_driver'), t_string()]),
- t_tuple([t_atom('spawn_executable'), ArgT]),
- t_tuple([t_atom('fd'), t_integer(), t_integer()])])),
- t_list(t_sup(t_sup([t_atom('stream'),
- t_atom('exit_status'),
- t_atom('use_stdio'),
- t_atom('nouse_stdio'),
- t_atom('stderr_to_stdout'),
- t_atom('in'),
- t_atom('out'),
- t_atom('binary'),
- t_atom('eof'),
- t_atom('hide')]),
- t_sup([t_tuple([t_atom('packet'), t_integer()]),
- t_tuple([t_atom('line'), t_integer()]),
- t_tuple([t_atom('cd'), t_string()]),
- t_tuple([t_atom('env'), t_list(t_tuple(2))]), % XXX: More
- t_tuple([t_atom('args'), t_list(ArgT)]),
- t_tuple([t_atom('arg0'), ArgT])])))];
-arg_types(erlang, phash, 2) ->
- [t_any(), t_pos_integer()];
-arg_types(erlang, phash2, 1) ->
- [t_any()];
-arg_types(erlang, phash2, 2) ->
- [t_any(), t_pos_integer()];
-arg_types(erlang, pid_to_list, 1) ->
- [t_pid()];
-arg_types(erlang, port_call, 2) ->
- [t_sup(t_port(), t_atom()), t_any()];
-arg_types(erlang, port_call, 3) ->
- [t_sup(t_port(), t_atom()), t_integer(), t_any()];
-arg_types(erlang, port_close, 1) ->
- [t_sup(t_port(), t_atom())];
-arg_types(erlang, port_command, 2) ->
- [t_sup(t_port(), t_atom()), t_sup(t_iolist(), t_binary())];
-arg_types(erlang, port_command, 3) ->
- [t_sup(t_port(), t_atom()),
- t_sup(t_iolist(), t_binary()),
- t_list(t_atoms(['force', 'nosuspend']))];
-arg_types(erlang, port_connect, 2) ->
- [t_sup(t_port(), t_atom()), t_pid()];
-arg_types(erlang, port_control, 3) ->
- [t_sup(t_port(), t_atom()), t_integer(), t_sup(t_iolist(), t_binary())];
-arg_types(erlang, port_get_data, 1) ->
- [t_sup(t_port(), t_atom())];
-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'])];
-arg_types(erlang, port_to_list, 1) ->
- [t_port()];
-arg_types(erlang, ports, 0) ->
- [];
-arg_types(erlang, port_set_data, 2) ->
- [t_sup(t_port(), t_atom()), t_any()];
-arg_types(erlang, pre_loaded, 0) ->
- [];
-arg_types(erlang, process_display, 2) ->
- [t_pid(), t_atom('backtrace')];
-arg_types(erlang, process_flag, 2) ->
- [t_sup([t_atom('trap_exit'),
- t_atom('error_handler'),
- t_atom('min_heap_size'),
- t_atom('min_bin_vheap_size'),
- t_atom('priority'),
- t_atom('save_calls'),
- t_atom('sensitive'),
- t_atom('scheduler'), % undocumented
- t_atom('monitor_nodes'), % undocumented
- t_tuple([t_atom('monitor_nodes'), t_list()])]), % undocumented
- t_sup([t_boolean(), t_atom(), t_non_neg_integer()])];
-arg_types(erlang, process_flag, 3) ->
- [t_pid(), t_atom('save_calls'), t_non_neg_integer()];
-arg_types(erlang, process_info, 1) ->
- [t_pid()];
-arg_types(erlang, process_info, 2) ->
- [t_pid(), t_pinfo()];
-arg_types(erlang, processes, 0) ->
- [];
-arg_types(erlang, purge_module, 1) ->
- [t_atom()];
-arg_types(erlang, put, 2) ->
- [t_any(), t_any()];
-arg_types(erlang, raise, 3) ->
- OldStyleType = t_list(t_tuple([t_atom(), t_atom(),
- t_sup([t_arity(), t_list()])])),
- NewStyleType = type(erlang, get_stacktrace, 0, []),
- [t_raise_errorclass(), t_any(), t_sup(OldStyleType, NewStyleType)];
-arg_types(erlang, read_timer, 1) ->
- [t_reference()];
-arg_types(erlang, ref_to_list, 1) ->
- [t_reference()];
-arg_types(erlang, register, 2) ->
- [t_atom(), t_sup(t_port(), t_pid())];
-arg_types(erlang, registered, 0) ->
- [];
-arg_types(erlang, resume_process, 1) ->
- [t_pid()]; % intended for debugging only
+%% Guard bif, needs to be here.
arg_types(erlang, round, 1) ->
[t_number()];
-arg_types(erlang, posixtime_to_universaltime, 1) ->
- [t_integer()];
+%% Guard bif, needs to be here.
arg_types(erlang, self, 0) ->
[];
-arg_types(erlang, send, 2) ->
- arg_types(erlang, '!', 2); % alias
-arg_types(erlang, send, 3) ->
- arg_types(erlang, send, 2) ++ [t_list(t_sendoptions())];
-arg_types(erlang, send_after, 3) ->
- [t_non_neg_integer(), t_sup(t_pid(), t_atom()), t_any()];
-arg_types(erlang, seq_trace, 2) ->
- [t_atom(), t_sup([t_boolean(), t_tuple([t_fixnum(), t_fixnum()]), t_fixnum(), t_nil()])];
-arg_types(erlang, seq_trace_info, 1) ->
- [t_seq_trace_info()];
-arg_types(erlang, seq_trace_print, 1) ->
- [t_any()];
-arg_types(erlang, seq_trace_print, 2) ->
- [t_sup(t_atom(), t_fixnum()), t_any()];
arg_types(erlang, set_cookie, 2) ->
[t_node(), t_atom()];
arg_types(erlang, setelement, 3) ->
[t_pos_integer(), t_tuple(), t_any()];
-arg_types(erlang, setnode, 2) ->
- [t_atom(), t_integer()];
-arg_types(erlang, setnode, 3) ->
- [t_atom(), t_port(), t_tuple(4)];
+%% Guard bif, needs to be here.
arg_types(erlang, size, 1) ->
[t_sup(t_tuple(), t_binary())];
arg_types(erlang, spawn, 1) -> %% TODO: Tuple?
[t_fun()];
arg_types(erlang, spawn, 2) -> %% TODO: Tuple?
[t_node(), t_fun()];
-arg_types(erlang, spawn, 3) -> %% TODO: Tuple?
- [t_atom(), t_atom(), t_list()];
arg_types(erlang, spawn, 4) -> %% TODO: Tuple?
[t_node(), t_atom(), t_atom(), t_list()];
arg_types(erlang, spawn_link, 1) ->
arg_types(erlang, spawn, 1); % same
arg_types(erlang, spawn_link, 2) ->
arg_types(erlang, spawn, 2); % same
-arg_types(erlang, spawn_link, 3) ->
- arg_types(erlang, spawn, 3); % same
arg_types(erlang, spawn_link, 4) ->
arg_types(erlang, spawn, 4); % same
-arg_types(erlang, spawn_opt, 1) ->
- [t_tuple([t_atom(), t_atom(), t_list(), t_list(t_spawn_options())])];
-arg_types(erlang, spawn_opt, 2) ->
- [t_fun(), t_list(t_spawn_options())];
-arg_types(erlang, spawn_opt, 3) ->
- [t_atom(), t_fun(), t_list(t_spawn_options())];
-arg_types(erlang, spawn_opt, 4) ->
- [t_node(), t_atom(), t_list(), t_list(t_spawn_options())];
-arg_types(erlang, split_binary, 2) ->
- [t_binary(), t_non_neg_integer()];
-arg_types(erlang, start_timer, 3) ->
- [t_non_neg_integer(), t_sup(t_pid(), t_atom()), t_any()];
-arg_types(erlang, statistics, 1) ->
- [t_sup([t_atom('context_switches'),
- t_atom('exact_reductions'),
- t_atom('garbage_collection'),
- t_atom('io'),
- t_atom('reductions'),
- t_atom('run_queue'),
- t_atom('runtime'),
- t_atom('scheduler_wall_time'),
- t_atom('wall_clock')])];
arg_types(erlang, subtract, 2) ->
arg_types(erlang, '--', 2);
arg_types(erlang, suspend_process, 1) ->
[t_pid()];
-arg_types(erlang, suspend_process, 2) ->
- [t_pid(), t_list(t_sup([t_atom('unless_suspending'),
- t_atom('asynchronous')]))];
-arg_types(erlang, system_flag, 2) ->
- [t_sup([t_atom('backtrace_depth'),
- t_atom('cpu_topology'),
- t_atom('debug_flags'), % undocumented
- t_atom('display_items'), % undocumented
- t_atom('fullsweep_after'),
- t_atom('min_heap_size'),
- t_atom('min_bin_vheap_size'),
- t_atom('multi_scheduling'),
- t_atom('schedulers_online'),
- t_atom('scheduler_bind_type'),
- %% Undocumented; used to implement (the documented) seq_trace module.
- t_atom('sequential_tracer'),
- t_atom('trace_control_word'),
- %% 'internal_cpu_topology' is an undocumented internal feature.
- t_atom('internal_cpu_topology'),
- t_atom('scheduler_wall_time'),
- t_integer()]),
- t_sup([t_integer(),
- %% 'cpu_topology'
- t_system_cpu_topology(),
- %% 'scheduler_bind_type'
- t_scheduler_bind_type_args(),
- %% Undocumented: the following is for 'debug_flags' that
- %% takes any erlang term as flags and currently ignores it.
- %% t_any(), % commented out since it destroys the type signature
- %%
- %% Again undocumented; the following are for 'sequential_tracer'
- t_sequential_tracer(),
- %% The following two are for 'multi_scheduling'
- t_atom('block'),
- t_atom('unblock'),
- %% For 'scheduler_wall_time'
- t_atom('true'),
- t_atom('false'),
- %% The following is for 'internal_cpu_topology'
- t_internal_cpu_topology()])];
arg_types(erlang, system_info, 1) ->
[t_sup([t_atom(), % documented
t_tuple([t_atom(), t_any()]), % documented
t_tuple([t_atom(), t_atom(), t_any()]),
t_tuple([t_atom(allocator_sizes), t_reference(), t_any()])])];
-arg_types(erlang, system_monitor, 0) ->
- [];
-arg_types(erlang, system_monitor, 1) ->
- [t_system_monitor_settings()];
-arg_types(erlang, system_monitor, 2) ->
- [t_pid(), t_system_monitor_options()];
-arg_types(erlang, system_profile, 0) ->
- [];
-arg_types(erlang, system_profile, 2) ->
- [t_sup([t_pid(), t_port(), t_atom('undefined')]),
- t_system_profile_options()];
-arg_types(erlang, term_to_binary, 1) ->
- [t_any()];
-arg_types(erlang, term_to_binary, 2) ->
- [t_any(), t_list(t_sup([t_atom('compressed'),
- t_tuple([t_atom('compressed'), t_from_range(0, 9)]),
- t_tuple([t_atom('minor_version'), t_integers([0, 1])])]))];
arg_types(erlang, throw, 1) ->
[t_any()];
-arg_types(erlang, time, 0) ->
- [];
+%% Guard bif, needs to be here.
arg_types(erlang, tl, 1) ->
[t_cons()];
-arg_types(erlang, trace, 3) ->
- [t_sup(t_pid(), t_sup([t_atom('existing'), t_atom('new'), t_atom('all')])),
- t_boolean(),
- t_list(t_sup(t_atom(), t_tuple(2)))];
-arg_types(erlang, trace_delivered, 1) ->
- [t_sup(t_pid(), t_atom('all'))];
-arg_types(erlang, trace_info, 2) ->
- [t_sup([%% the following two get info about a PID
- t_pid(), t_atom('new'),
- %% while the following two get info about a func
- t_mfa(), t_atom('on_load')]),
- t_sup([%% the following are items about a PID
- t_atom('flags'), t_atom('tracer'),
- %% while the following are items about a func
- t_atom('traced'), t_atom('match_spec'), t_atom('meta'),
- t_atom('meta_match_spec'), t_atom('call_count'),
- t_atom('call_time'), t_atom('all')])];
-arg_types(erlang, trace_pattern, 2) ->
- [t_sup(t_tuple([t_atom(), t_atom(), t_sup(t_arity(), t_atom('_'))]),
- t_atom('on_load')),
- t_sup([t_boolean(), t_list(), t_atom('restart'), t_atom('pause')])];
-arg_types(erlang, trace_pattern, 3) ->
- arg_types(erlang, trace_pattern, 2) ++
- [t_list(t_sup([t_atom('global'), t_atom('local'),
- t_atom('meta'), t_tuple([t_atom('meta'), t_pid()]),
- t_atom('call_count'), t_atom('call_time')]))];
+%% Guard bif, needs to be here.
arg_types(erlang, trunc, 1) ->
[t_number()];
+%% Guard bif, needs to be here.
arg_types(erlang, tuple_size, 1) ->
[t_tuple()];
arg_types(erlang, tuple_to_list, 1) ->
[t_tuple()];
-arg_types(erlang, universaltime, 0) ->
- [];
-arg_types(erlang, universaltime_to_localtime, 1) ->
- [t_tuple([t_date(), t_time()])];
-arg_types(erlang, universaltime_to_posixtime, 1) ->
- [t_tuple([t_date(), t_time()])];
-arg_types(erlang, unlink, 1) ->
- [t_sup(t_pid(), t_port())];
-arg_types(erlang, unregister, 1) ->
- [t_atom()];
-arg_types(erlang, whereis, 1) ->
- [t_atom()];
arg_types(erlang, yield, 0) ->
[];
-%%------- erl_prim_loader -----------------------------------------------------
-arg_types(erl_prim_loader, get_file, 1) ->
- [t_sup(t_atom(), t_string())];
-arg_types(erl_prim_loader, get_path, 0) ->
- [];
-arg_types(erl_prim_loader, set_path, 1) ->
- [t_list(t_string())];
-%%------- error_logger --------------------------------------------------------
-arg_types(error_logger, warning_map, 0) ->
- [];
-%%------- erts_debug ----------------------------------------------------------
-arg_types(erts_debug, breakpoint, 2) ->
- [t_tuple([t_atom(), t_atom(), t_sup(t_integer(), t_atom('_'))]), t_boolean()];
-arg_types(erts_debug, disassemble, 1) ->
- [t_sup(t_mfa(), t_integer())];
-arg_types(erts_debug, display, 1) ->
- [t_any()];
-arg_types(erts_debug, dist_ext_to_term, 2) ->
- [t_tuple(), t_binary()];
-arg_types(erts_debug, dump_monitors, 1) ->
- [t_sup([t_pid(),t_atom()])];
-arg_types(erts_debug, dump_links, 1) ->
- [t_sup([t_pid(),t_atom(),t_port()])];
-arg_types(erts_debug, flat_size, 1) ->
- [t_any()];
-arg_types(erts_debug, get_internal_state, 1) ->
- [t_any()];
-arg_types(erts_debug, instructions, 0) ->
- [];
-arg_types(erts_debug, lock_counters, 1) ->
- [t_sup([t_atom(enabled),
- t_atom(info),
- t_atom(clear),
- t_tuple([t_atom(copy_save), t_boolean()]),
- t_tuple([t_atom(process_locks), t_boolean()])])];
-arg_types(erts_debug, same, 2) ->
- [t_any(), t_any()];
-arg_types(erts_debug, set_internal_state, 2) ->
- [t_any(), t_any()];
%%------- ets -----------------------------------------------------------------
-arg_types(ets, all, 0) ->
- [];
-arg_types(ets, delete, 1) ->
- [t_tab()];
-arg_types(ets, delete, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, delete_all_objects, 1) ->
- [t_tab()];
-arg_types(ets, delete_object, 2) ->
- [t_tab(), t_tuple()];
-arg_types(ets, first, 1) ->
- [t_tab()];
-arg_types(ets, give_away, 3) ->
- [t_tab(), t_pid(), t_any()];
-arg_types(ets, info, 1) ->
- [t_tab()];
-arg_types(ets, info, 2) ->
- [t_tab(), t_ets_info_items()];
-arg_types(ets, insert, 2) ->
- [t_tab(), t_sup(t_tuple(), t_list(t_tuple()))];
-arg_types(ets, insert_new, 2) ->
- [t_tab(), t_sup(t_tuple(), t_list(t_tuple()))];
-arg_types(ets, is_compiled_ms, 1) ->
- [t_any()];
-arg_types(ets, last, 1) ->
- arg_types(ets, first, 1);
-arg_types(ets, lookup, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, lookup_element, 3) ->
- [t_tab(), t_any(), t_pos_fixnum()];
-arg_types(ets, match, 1) ->
- [t_any()];
-arg_types(ets, match, 2) ->
- [t_tab(), t_match_pattern()];
-arg_types(ets, match, 3) ->
- [t_tab(), t_match_pattern(), t_pos_fixnum()];
-arg_types(ets, match_object, 1) ->
- arg_types(ets, match, 1);
-arg_types(ets, match_object, 2) ->
- arg_types(ets, match, 2);
-arg_types(ets, match_object, 3) ->
- arg_types(ets, match, 3);
-arg_types(ets, match_spec_compile, 1) ->
- [t_matchspecs()];
-arg_types(ets, match_spec_run_r, 3) ->
- [t_list(t_tuple()),t_matchspecs(), t_list()];
-arg_types(ets, member, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, new, 2) ->
- [t_atom(), t_ets_new_options()];
-arg_types(ets, next, 2) ->
- [t_tab(), t_any()];
-arg_types(ets, prev, 2) ->
- [t_tab(), t_any()];
arg_types(ets, rename, 2) ->
[t_atom(), t_atom()];
-arg_types(ets, safe_fixtable, 2) ->
- [t_tab(), t_boolean()];
-arg_types(ets, select, 1) ->
- [t_any()];
-arg_types(ets, select, 2) ->
- [t_tab(), t_matchspecs()];
-arg_types(ets, select, 3) ->
- [t_tab(), t_matchspecs(), t_pos_fixnum()];
-arg_types(ets, select_count, 2) ->
- [t_tab(), t_matchspecs()];
-arg_types(ets, select_delete, 2) ->
- [t_tab(), t_matchspecs()];
-arg_types(ets, select_reverse, 1) ->
- arg_types(ets, select, 1);
-arg_types(ets, select_reverse, 2) ->
- arg_types(ets, select, 2);
-arg_types(ets, select_reverse, 3) ->
- arg_types(ets, select, 3);
-arg_types(ets, slot, 2) ->
- [t_tab(), t_non_neg_fixnum()]; % 2nd arg can be 0
-arg_types(ets, setopts, 2) ->
- Opt = t_sup([t_tuple([t_atom('heir'), t_pid(), t_any()]),
- t_tuple([t_atom('heir'), t_atom('none')]),
- t_tuple([t_atom('protection'),
- t_sup([t_atom('protected'),
- t_atom('private'),
- t_atom('public')])])]),
- [t_tab(), t_sup(Opt, t_list(Opt))];
-arg_types(ets, update_counter, 3) ->
- Int = t_integer(),
- UpdateOp = t_sup(t_tuple([Int, Int]), t_tuple([Int, Int, Int, Int])),
- [t_tab(), t_any(), t_sup([UpdateOp, t_list(UpdateOp), Int])];
-arg_types(ets, update_element, 3) ->
- PosValue = t_tuple([t_integer(), t_any()]),
- [t_tab(), t_any(), t_sup(PosValue, t_list(PosValue))];
-%%------- file ----------------------------------------------------------------
-arg_types(file, native_name_encoding, 0) ->
- [];
-%%-- prim_file ----------------------------------------------------------------
-arg_types(prim_file, internal_name2native, 1) ->
- [t_prim_file_name()];
-arg_types(prim_file, internal_native2name, 1) ->
- [t_binary()];
-arg_types(prim_file, internal_normalize_utf8, 1) ->
- [t_binary()];
-%%------- gen_tcp -------------------------------------------------------------
-arg_types(gen_tcp, accept, 1) ->
- [t_socket()];
-arg_types(gen_tcp, accept, 2) ->
- [t_socket(), t_timeout()];
-arg_types(gen_tcp, connect, 3) ->
- [t_gen_tcp_address(), t_gen_tcp_port(), t_list(t_gen_tcp_connect_option())];
-arg_types(gen_tcp, connect, 4) ->
- arg_types(gen_tcp, connect, 3) ++ [t_timeout()];
-arg_types(gen_tcp, listen, 2) ->
- [t_gen_tcp_port(), t_list(t_gen_tcp_listen_option())];
-arg_types(gen_tcp, recv, 2) ->
- [t_socket(), t_non_neg_integer()];
-arg_types(gen_tcp, recv, 3) ->
- arg_types(gen_tcp, recv, 2) ++ [t_timeout()];
-arg_types(gen_tcp, send, 2) ->
- [t_socket(), t_packet()];
-arg_types(gen_tcp, shutdown, 2) ->
- [t_socket(), t_sup([t_atom('read'), t_atom('write'), t_atom('read_write')])];
-%%------- gen_udp -------------------------------------------------------------
-arg_types(gen_udp, open, 1) ->
- [t_gen_tcp_port()];
-arg_types(gen_udp, open, 2) ->
- [t_gen_tcp_port(), t_list(t_gen_udp_connect_option())];
-arg_types(gen_udp, recv, 2) ->
- arg_types(gen_tcp, recv, 2);
-arg_types(gen_udp, recv, 3) ->
- arg_types(gen_tcp, recv, 3);
-arg_types(gen_udp, send, 4) ->
- [t_socket(), t_gen_tcp_address(), t_gen_tcp_port(), t_packet()];
%%------- hipe_bifs -----------------------------------------------------------
arg_types(hipe_bifs, add_ref, 2) ->
[t_mfa(), t_tuple([t_mfa(),
@@ -4242,7 +2342,7 @@ arg_types(hipe_bifs, check_crc, 1) ->
arg_types(hipe_bifs, enter_code, 2) ->
[t_binary(), t_sup(t_nil(), t_tuple())];
arg_types(hipe_bifs, enter_sdesc, 1) ->
- [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer()])];
+ [t_tuple([t_integer(), t_integer(), t_integer(), t_integer(), t_integer(), t_mfa()])];
arg_types(hipe_bifs, find_na_or_make_stub, 2) ->
[t_mfa(), t_boolean()];
arg_types(hipe_bifs, fun_to_address, 1) ->
@@ -4293,28 +2393,6 @@ arg_types(hipe_bifs, write_u32, 2) ->
[t_integer(), t_integer()];
arg_types(hipe_bifs, write_u64, 2) ->
[t_integer(), t_integer()];
-%%------- io ------------------------------------------------------------------
-arg_types(io, format, 1) ->
- [t_io_format_string()];
-arg_types(io, format, 2) ->
- [t_io_format_string(), t_list()];
-arg_types(io, format, 3) ->
- [t_io_device(), t_io_format_string(), t_list()];
-arg_types(io, fwrite, 1) ->
- arg_types(io, format, 1);
-arg_types(io, fwrite, 2) ->
- arg_types(io, format, 2);
-arg_types(io, fwrite, 3) ->
- arg_types(io, format, 3);
-arg_types(io, put_chars, 1) ->
- [t_iodata()];
-arg_types(io, put_chars, 2) ->
- [t_io_device(), t_iodata()];
-%%------- io_lib --------------------------------------------------------------
-arg_types(io_lib, format, 2) ->
- arg_types(io, format, 2);
-arg_types(io_lib, fwrite, 2) ->
- arg_types(io_lib, format, 2);
%%------- lists ---------------------------------------------------------------
arg_types(lists, all, 2) ->
[t_fun([t_any()], t_boolean()), t_list()];
@@ -4386,10 +2464,6 @@ arg_types(lists, reverse, 1) ->
[t_list()];
arg_types(lists, reverse, 2) ->
[t_list(), t_any()];
-arg_types(lists, seq, 2) ->
- [t_integer(), t_integer()];
-arg_types(lists, seq, 3) ->
- [t_integer(), t_integer(), t_integer()];
arg_types(lists, sort, 1) ->
[t_list()];
arg_types(lists, sort, 2) ->
@@ -4418,97 +2492,12 @@ arg_types(lists, zipwith, 3) ->
[t_fun([t_any(), t_any()], t_any()), t_list(), t_list()];
arg_types(lists, zipwith3, 4) ->
[t_fun([t_any(), t_any(), t_any()], t_any()), t_list(), t_list(), t_list()];
-%%------- math ----------------------------------------------------------------
-arg_types(math, acos, 1) ->
- [t_number()];
-arg_types(math, acosh, 1) ->
- [t_number()];
-arg_types(math, asin, 1) ->
- [t_number()];
-arg_types(math, asinh, 1) ->
- [t_number()];
-arg_types(math, atan, 1) ->
- [t_number()];
-arg_types(math, atan2, 2) ->
- [t_number(), t_number()];
-arg_types(math, atanh, 1) ->
- [t_number()];
-arg_types(math, cos, 1) ->
- [t_number()];
-arg_types(math, cosh, 1) ->
- [t_number()];
-arg_types(math, erf, 1) ->
- [t_number()];
-arg_types(math, erfc, 1) ->
- [t_number()];
-arg_types(math, exp, 1) ->
- [t_number()];
-arg_types(math, log, 1) ->
- [t_number()];
-arg_types(math, log10, 1) ->
- [t_number()];
-arg_types(math, pi, 0) ->
- [];
-arg_types(math, pow, 2) ->
- [t_number(), t_number()];
-arg_types(math, sin, 1) ->
- [t_number()];
-arg_types(math, sinh, 1) ->
- [t_number()];
-arg_types(math, sqrt, 1) ->
- [t_number()];
-arg_types(math, tan, 1) ->
- [t_number()];
-arg_types(math, tanh, 1) ->
- [t_number()];
-%%-- net_kernel ---------------------------------------------------------------
-arg_types(net_kernel, dflag_unicode_io, 1) ->
- [t_pid()];
-%%------- ordsets -------------------------------------------------------------
-arg_types(ordsets, filter, 2) ->
- arg_types(lists, filter, 2);
-arg_types(ordsets, fold, 3) ->
- arg_types(lists, foldl, 3);
-%%------- os ------------------------------------------------------------------
-arg_types(os, getenv, 0) ->
- [];
-arg_types(os, getenv, 1) ->
- [t_string()];
-arg_types(os, getpid, 0) ->
- [];
-arg_types(os, putenv, 2) ->
- [t_string(), t_string()];
-arg_types(os, timestamp, 0) ->
- [];
-%%-- re -----------------------------------------------------------------------
-arg_types(re, compile, 1) ->
- [t_iodata()];
-arg_types(re, compile, 2) ->
- [t_sup(t_iodata(), t_charlist()), t_list(t_re_compile_option())];
-arg_types(re, run, 2) ->
- [t_sup(t_iodata(), t_charlist()), t_re_RE()];
-arg_types(re, run, 3) ->
- [t_sup(t_iodata(), t_charlist()), t_re_RE(), t_list(t_re_run_option())];
+
%%------- string --------------------------------------------------------------
arg_types(string, chars, 2) ->
[t_char(), t_non_neg_integer()];
arg_types(string, chars, 3) ->
[t_char(), t_non_neg_integer(), t_any()];
-arg_types(string, concat, 2) ->
- [t_string(), t_string()];
-arg_types(string, equal, 2) ->
- [t_string(), t_string()];
-arg_types(string, to_float, 1) ->
- [t_string()];
-arg_types(string, to_integer, 1) ->
- [t_string()];
-%%------- unicode -------------------------------------------------------------
-arg_types(unicode, characters_to_binary, 2) ->
- [t_ML(), t_encoding()];
-arg_types(unicode, characters_to_list, 2) ->
- [t_ML(), t_encoding()];
-arg_types(unicode, bin_is_7bit, 1) ->
- [t_binary()];
%%-----------------------------------------------------------------------------
arg_types(M, F, A) when is_atom(M), is_atom(F),
is_integer(A), 0 =< A, A =< 255 ->
@@ -4568,244 +2557,22 @@ check_fun_application(Fun, Args) ->
%% =====================================================================
-%% These are basic types that should probably be moved to erl_types
-%% =====================================================================
-
-t_socket() -> t_port(). % alias
-
-t_ip_address() ->
- T_int16 = t_from_range(0, 16#FFFF),
- t_sup(t_tuple([t_byte(), t_byte(), t_byte(), t_byte()]),
- t_tuple([T_int16, T_int16, T_int16, T_int16,
- T_int16, T_int16, T_int16, T_int16])).
-
-%% =====================================================================
%% Some basic types used in various parts of the system
%% =====================================================================
-t_date() ->
- t_tuple([t_pos_fixnum(), t_pos_fixnum(), t_pos_fixnum()]).
-
-t_time() ->
- t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]).
-
-t_timestamp() ->
- t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]).
-
-t_packet() ->
- t_sup([t_binary(), t_iolist(), t_httppacket()]).
-
-t_httppacket() ->
- t_sup([t_HttpRequest(), t_HttpResponse(),
- t_HttpHeader(), t_atom('http_eoh'), t_HttpError()]).
-
t_endian() ->
t_sup(t_atom('big'), t_atom('little')).
%% =====================================================================
-%% HTTP types documented in R12B-4
-%% =====================================================================
-
-t_HttpRequest() ->
- t_tuple([t_atom('http_request'), t_HttpMethod(), t_HttpUri(), t_HttpVersion()]).
-
-t_HttpResponse() ->
- t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_HttpString()]).
-
-t_HttpHeader() ->
- t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_HttpString()]).
-
-t_HttpError() ->
- t_tuple([t_atom('http_error'), t_HttpString()]).
-
-t_HttpMethod() ->
- t_sup(t_HttpMethodAtom(), t_HttpString()).
-
-t_HttpMethodAtom() ->
- t_atoms(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']).
-
-t_HttpUri() ->
- t_sup([t_atom('*'),
- t_tuple([t_atom('absoluteURI'),
- t_sup(t_atom('http'), t_atom('https')),
- t_HttpString(),
- t_sup(t_non_neg_integer(), t_atom('undefined')),
- t_HttpString()]),
- t_tuple([t_atom('scheme'), t_HttpString(), t_HttpString()]),
- t_tuple([t_atom('abs_path'), t_HttpString()]),
- t_HttpString()]).
-
-t_HttpVersion() ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
-
-t_HttpField() ->
- t_sup(t_HttpFieldAtom(), t_HttpString()).
-
-t_HttpFieldAtom() ->
- t_atoms(['Cache-Control', 'Connection', 'Date', 'Pragma', 'Transfer-Encoding',
- 'Upgrade', 'Via', 'Accept', 'Accept-Charset', 'Accept-Encoding',
- 'Accept-Language', 'Authorization', 'From', 'Host',
- 'If-Modified-Since', 'If-Match', 'If-None-Match', 'If-Range',
- 'If-Unmodified-Since', 'Max-Forwards', 'Proxy-Authorization',
- 'Range', 'Referer', 'User-Agent', 'Age', 'Location',
- 'Proxy-Authenticate', 'Public', 'Retry-After', 'Server', 'Vary',
- 'Warning', 'Www-Authenticate', 'Allow', 'Content-Base',
- 'Content-Encoding', 'Content-Language', 'Content-Length',
- 'Content-Location', 'Content-Md5', 'Content-Range', 'Content-Type',
- 'Etag', 'Expires', 'Last-Modified', 'Accept-Ranges',
- 'Set-Cookie', 'Set-Cookie2', 'X-Forwarded-For', 'Cookie',
- 'Keep-Alive', 'Proxy-Connection']).
-
-t_HttpString() ->
- t_sup(t_string(), t_binary()).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'binary'
-%% =====================================================================
-
-t_binary_part() ->
- t_tuple([t_non_neg_integer(), t_integer()]).
-
-t_binary_canonical_part() ->
- t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
-
-t_binary_pattern() ->
- t_sup([t_binary(),
- t_list(t_binary()),
- t_binary_compiled_pattern()]).
-
-t_binary_compiled_pattern() ->
- t_tuple([t_sup(t_atom('bm'), t_atom('ac')), t_binary()]).
-
-t_binary_options() ->
- t_list(t_tuple([t_atom('scope'), t_binary_part()])).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'code'
-%% =====================================================================
-
-t_code_load_return(Mod) ->
- t_sup(t_tuple([t_atom('module'), case t_is_atom(Mod) of
- true -> Mod;
- false -> t_atom()
- end]),
- t_tuple([t_atom('error'), t_code_load_error_rsn()])).
-
-t_code_load_error_rsn() -> % also used in erlang:load_module/2
- t_sup([t_atom('badfile'),
- t_atom('nofile'),
- t_atom('not_purged'),
- t_atom('native_code'),
- t_atom('on_load'),
- t_atom('sticky_directory')]). % only for the 'code' functions
-
-%% =====================================================================
%% These are used for the built-in functions of 'erlang'
%% =====================================================================
-t_adler32() ->
- t_non_neg_integer().
-
t_crc32() ->
t_non_neg_integer().
-t_decode_packet_option() ->
- t_sup([t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
- t_tuple([t_atom('line_length'), t_non_neg_integer()])]).
-
-t_decode_packet_type() ->
- t_sup([t_inet_setoption_packettype(), t_atom('httph'), t_atom('httph_bin')]).
-
-t_dist_exit() ->
- t_sup([t_atom('kill'), t_atom('noconnection'), t_atom('normal')]).
-
-t_match_spec_test_errors() ->
- t_list(t_sup(t_tuple([t_atom('error'), t_string()]),
- t_tuple([t_atom('warning'), t_string()]))).
-
-t_module_info_2() ->
- t_sup([t_atom('module'),
- t_atom('imports'),
- t_atom('exports'),
- t_atom('functions'),
- t_atom('attributes'),
- t_atom('compile'),
- t_atom('native_addresses')]).
-
-t_pinfo() ->
- t_sup([t_pinfo_item(), t_list(t_pinfo_item())]).
-
-t_pinfo_item() ->
- t_sup([t_atom('backtrace'),
- t_atom('current_function'),
- t_atom('dictionary'),
- t_atom('error_handler'),
- t_atom('garbage_collection'),
- t_atom('group_leader'),
- t_atom('heap_size'),
- t_atom('initial_call'),
- t_atom('last_calls'),
- t_atom('links'),
- t_atom('memory'),
- t_atom('message_queue_len'),
- t_atom('messages'),
- t_atom('monitored_by'),
- t_atom('monitors'),
- t_atom('priority'),
- t_atom('reductions'),
- t_atom('registered_name'),
- t_atom('sequential_trace_token'),
- t_atom('stack_size'),
- t_atom('status'),
- t_atom('suspending'),
- t_atom('total_heap_size'),
- t_atom('trap_exit')]).
-
-t_process_priority_level() ->
- t_sup([t_atom('max'), t_atom('high'), t_atom('normal'), t_atom('low')]).
-
-t_process_status() ->
- t_sup([t_atom('exiting'), t_atom('garbage_collecting'),
- t_atom('runnable'), t_atom('running'),
- t_atom('suspended'), t_atom('waiting')]).
-
-t_raise_errorclass() ->
- t_sup([t_atom('error'), t_atom('exit'), t_atom('throw')]).
-
-t_sendoptions() ->
- t_sup(t_atom('noconnect'), t_atom('nosuspend')).
-
-t_seq_trace_info() ->
- t_sup([t_atom('send'),
- t_atom('receive'),
- t_atom('print'),
- t_atom('timestamp'),
- t_atom('label'),
- t_atom('serial')]).
-
-%% XXX: Better if we also maintain correspondencies between infos and values
-t_seq_trace_info_returns() ->
- Values = t_sup([t_non_neg_integer(), t_boolean(),
- t_tuple([t_non_neg_integer(), t_non_neg_integer()])]),
- t_sup(t_tuple([t_seq_trace_info(), Values]), t_nil()).
-
t_sequential_tracer() ->
t_sup([t_atom('false'), t_pid(), t_port()]).
-t_spawn_options() ->
- t_sup([t_atom('link'),
- t_atom('monitor'),
- t_tuple([t_atom('priority'), t_process_priority_level()]),
- t_tuple([t_atom('min_heap_size'), t_fixnum()]),
- t_tuple([t_atom('min_bin_vheap_size'), t_fixnum()]),
- t_tuple([t_atom('fullsweep_after'), t_fixnum()])]).
-
-t_spawn_opt_return(List) ->
- case t_is_none(t_inf(t_list(t_atom('monitor')), List)) of
- true -> t_pid();
- false -> t_sup(t_pid(), t_tuple([t_pid(), t_reference()]))
- end.
-
t_system_cpu_topology() ->
t_sup(t_atom('undefined'), t_system_cpu_topology_level_entry_list()).
@@ -4842,17 +2609,6 @@ t_internal_cpu_topology() -> %% Internal undocumented type
t_non_neg_fixnum()])),
t_atom('undefined')).
-t_scheduler_bind_type_args() ->
- t_sup([t_atom('default_bind'),
- t_atom('no_node_processor_spread'),
- t_atom('no_node_thread_spread'),
- t_atom('no_spread'),
- t_atom('processor_spread'),
- t_atom('spread'),
- t_atom('thread_spread'),
- t_atom('thread_no_node_processor_spread'),
- t_atom('unbound')]).
-
t_scheduler_bind_type_results() ->
t_sup([t_atom('no_node_processor_spread'),
t_atom('no_node_thread_spread'),
@@ -4863,160 +2619,9 @@ t_scheduler_bind_type_results() ->
t_atom('thread_no_node_processor_spread'),
t_atom('unbound')]).
-t_system_monitor_settings() ->
- t_sup([t_atom('undefined'),
- t_tuple([t_pid(), t_system_monitor_options()])]).
-
-t_system_monitor_options() ->
- t_list(t_sup([t_atom('busy_port'),
- t_atom('busy_dist_port'),
- t_tuple([t_atom('long_gc'), t_integer()]),
- t_tuple([t_atom('large_heap'), t_integer()])])).
-
t_system_multi_scheduling() ->
t_sup([t_atom('blocked'), t_atom('disabled'), t_atom('enabled')]).
-t_system_profile_options() ->
- t_list(t_sup([t_atom('exclusive'),
- t_atom('runnable_ports'),
- t_atom('runnable_procs'),
- t_atom('scheduler')])).
-
-t_system_profile_return() ->
- t_sup(t_atom('undefined'),
- t_tuple([t_sup(t_pid(), t_port()), t_system_profile_options()])).
-
-t_system_build_type_return() ->
- t_sup([t_atom('opt'),
- t_atom('debug'),
- t_atom('purify'),
- t_atom('quantify'),
- t_atom('purecov'),
- t_atom('gcov'),
- t_atom('valgrind'),
- t_atom('gprof'),
- t_atom('lcnt')]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'ets'
-%% =====================================================================
-
-t_tab() ->
- t_sup(t_tid(), t_atom()).
-
-t_match_pattern() ->
- t_sup(t_atom(), t_tuple()).
-
-t_matchspecs() ->
- t_list(t_tuple([t_match_pattern(), t_list(), t_list()])).
-
-t_matchres() ->
- t_sup(t_tuple([t_list(), t_any()]), t_atom('$end_of_table')).
-
-%% From the 'ets' documentation
-%%-----------------------------
-%% Option = Type | Access | named_table | {keypos,Pos}
-%% | {heir,pid(),HeirData} | {heir,none} | Tweaks
-%% Type = set | ordered_set | bag | duplicate_bag
-%% Access = public | protected | private
-%% Tweaks = {write_concurrency,boolean()}
-%% | {read_concurrency,boolean()} | compressed
-%% Pos = integer()
-%% HeirData = term()
-t_ets_new_options() ->
- t_list(t_sup([t_atom('set'),
- t_atom('ordered_set'),
- t_atom('bag'),
- t_atom('duplicate_bag'),
- t_atom('public'),
- t_atom('protected'),
- t_atom('private'),
- t_atom('named_table'),
- t_tuple([t_atom('keypos'), t_integer()]),
- t_tuple([t_atom('heir'), t_pid(), t_any()]),
- t_tuple([t_atom('heir'), t_atom('none')]),
- t_tuple([t_atom('write_concurrency'), t_boolean()]),
- t_tuple([t_atom('read_concurrency'), t_boolean()]),
- t_atom('compressed')])).
-
-t_ets_info_items() ->
- t_sup([t_atom('fixed'),
- t_atom('safe_fixed'),
- t_atom('keypos'),
- t_atom('memory'),
- t_atom('name'),
- t_atom('named_table'),
- t_atom('node'),
- t_atom('owner'),
- t_atom('protection'),
- t_atom('size'),
- t_atom('compressed'),
- t_atom('heir'),
- t_atom('stats'),
- t_atom('type')]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'gen_tcp'
-%% =====================================================================
-
-t_gen_tcp_accept() ->
- t_sup(t_tuple([t_atom('ok'), t_socket()]),
- t_tuple([t_atom('error'), t_sup([t_atom('closed'),
- t_atom('timeout'),
- t_inet_posix_error()])])).
-
-t_gen_tcp_address() ->
- t_sup([t_string(), t_atom(), t_ip_address()]).
-
-t_gen_tcp_port() ->
- t_from_range(0, 16#FFFF).
-
-t_gen_tcp_connect_option() ->
- t_sup([t_atom('list'),
- t_atom('binary'),
- t_tuple([t_atom('ip'), t_ip_address()]),
- t_tuple([t_atom('port'), t_gen_tcp_port()]),
- t_tuple([t_atom('fd'), t_integer()]),
- t_atom('inet6'),
- t_atom('inet'),
- t_inet_setoption()]).
-
-t_gen_tcp_listen_option() ->
- t_sup([t_atom('list'),
- t_atom('binary'),
- t_tuple([t_atom('backlog'), t_non_neg_integer()]),
- t_tuple([t_atom('ip'), t_ip_address()]),
- t_tuple([t_atom('fd'), t_integer()]),
- t_atom('inet6'),
- t_atom('inet'),
- t_inet_setoption()]).
-
-t_gen_tcp_recv() ->
- t_sup(t_tuple([t_atom('ok'), t_packet()]),
- t_tuple([t_atom('error'), t_sup([t_atom('closed'),
- t_inet_posix_error()])])).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'gen_udp'
-%% =====================================================================
-
-t_gen_udp_connect_option() ->
- t_sup([t_atom('list'),
- t_atom('binary'),
- t_tuple([t_atom('ip'), t_ip_address()]),
- t_tuple([t_atom('fd'), t_integer()]),
- t_atom('inet6'),
- t_atom('inet'),
- t_inet_setoption()]).
-
-t_gen_udp_recv() ->
- t_sup(t_tuple([t_atom('ok'),
- t_tuple([t_ip_address(),
- t_gen_tcp_port(),
- t_packet()])]),
- t_tuple([t_atom('error'),
- t_sup(t_atom('not_owner'), t_inet_posix_error())])).
-
%% =====================================================================
%% These are used for the built-in functions of 'hipe_bifs'
%% =====================================================================
@@ -5049,131 +2654,6 @@ t_insn_type() ->
t_atom('closure')]).
%% =====================================================================
-%% These are used for the built-in functions of 'inet'
-%% =====================================================================
-
-t_inet_setoption() ->
- t_sup([%% first the 2-tuple options
- t_tuple([t_atom('active'), t_sup(t_boolean(), t_atom('once'))]),
- t_tuple([t_atom('broadcast'), t_boolean()]),
- t_tuple([t_atom('delay_send'), t_boolean()]),
- t_tuple([t_atom('dontroute'), t_boolean()]),
- t_tuple([t_atom('exit_on_close'), t_boolean()]),
- t_tuple([t_atom('header'), t_non_neg_integer()]),
- t_tuple([t_atom('keepalive'), t_boolean()]),
- t_tuple([t_atom('nodelay'), t_boolean()]),
- t_tuple([t_atom('packet'), t_inet_setoption_packettype()]),
- t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
- t_tuple([t_atom('read_packets'), t_non_neg_integer()]),
- t_tuple([t_atom('recbuf'), t_non_neg_integer()]),
- t_tuple([t_atom('reuseaddr'), t_boolean()]),
- t_tuple([t_atom('send_timeout'), t_non_neg_integer()]),
- t_tuple([t_atom('sndbuf'), t_non_neg_integer()]),
- t_tuple([t_atom('priority'), t_non_neg_integer()]),
- t_tuple([t_atom('tos'), t_non_neg_integer()]),
- %% and a 4-tuple option
- t_tuple([t_atom('raw'),
- t_non_neg_integer(), % protocol level
- t_non_neg_integer(), % option number
- t_binary()])]). % actual option value
-
-t_inet_setoption_packettype() ->
- t_sup([t_atom('raw'),
- t_integers([0,1,2,4]),
- t_atom('asn1'), t_atom('cdr'), t_atom('sunrm'),
- t_atom('fcgi'), t_atom('tpkt'), t_atom('line'),
- t_atom('http'),
- t_atom('http_bin')]). %% but t_atom('httph') is not needed
-
-t_inet_posix_error() ->
- t_atom(). %% XXX: Very underspecified
-
-%% =====================================================================
-%% These are used for the built-in functions of 'io'
-%% =====================================================================
-
-t_io_device() ->
- t_sup(t_atom(), t_pid()).
-
-%% The documentation in R11B-4 reads
-%% Format ::= atom() | string() | binary()
-%% but the Format can also be a (deep) list, hence the type below
-t_io_format_string() ->
- t_sup([t_atom(), t_list(), t_binary()]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 're'; the functions
-%% whose last name component starts with a capital letter are types
-%% =====================================================================
-
-t_re_MP() -> %% it's supposed to be an opaque data type
- t_tuple([t_atom('re_pattern'), t_integer(), t_integer(), t_binary()]).
-
-t_re_RE() ->
- t_sup([t_re_MP(), t_iodata(), t_charlist()]).
-
-t_re_compile_option() ->
- t_sup([t_atoms(['unicode', 'anchored', 'caseless', 'dollar_endonly',
- 'dotall', 'extended', 'firstline', 'multiline',
- 'no_auto_capture', 'dupnames', 'ungreedy']),
- t_tuple([t_atom('newline'), t_re_NLSpec()]),
- t_atoms(['bsr_anycrlf', 'bsr_unicode'])]).
-
-t_re_run_option() ->
- t_sup([t_atoms(['anchored', 'global', 'notbol', 'noteol', 'notempty']),
- t_tuple([t_atom('offset'), t_integer()]),
- t_tuple([t_atom('newline'), t_re_NLSpec()]),
- t_tuple([t_atom('capture'), t_re_ValueSpec()]),
- t_tuple([t_atom('capture'), t_re_ValueSpec(), t_re_Type()]),
- t_re_compile_option()]).
-
-t_re_ErrorSpec() ->
- t_tuple([t_string(), t_non_neg_integer()]).
-
-t_re_Type() ->
- t_atoms(['index', 'list', 'binary']).
-
-t_re_NLSpec() ->
- t_atoms(['cr', 'crlf', 'lf', 'anycrlf', 'any']).
-
-t_re_ValueSpec() ->
- t_sup(t_atoms(['all', 'all_but_first', 'first', 'none']), t_re_ValueList()).
-
-t_re_ValueList() ->
- t_list(t_sup([t_integer(), t_string(), t_atom()])).
-
-t_re_Captured() ->
- t_list(t_sup(t_re_CapturedData(), t_list(t_re_CapturedData()))).
-
-t_re_CapturedData() ->
- t_sup([t_tuple([t_integer(), t_integer()]), t_string(), t_binary()]).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'prim_file'
-%% =====================================================================
-
-t_prim_file_name() ->
- t_sup(t_unicode_string(), t_binary()).
-
-%% =====================================================================
-%% These are used for the built-in functions of 'unicode'
-%% =====================================================================
-
-t_ML() -> % a binary or a possibly deep list of integers or binaries
- t_sup(t_list(t_sup([t_integer(), t_binary(), t_list()])), t_binary()).
-
-t_encoding() ->
- t_sup([t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']),
- t_tuple([t_atom('utf16'), t_endian()]),
- t_tuple([t_atom('utf32'), t_endian()])]).
-
-t_file_encoding() ->
- t_atoms(['latin1', 'utf8']).
-
-t_encoding_a2b() -> % for the 2nd arg of atom_to_binary/2 and binary_to_atom/2
- t_atoms(['latin1', 'unicode', 'utf8']).
-
-%% =====================================================================
%% Some testing code for ranges below
%% =====================================================================
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index bc7ea17077..d1243b2325 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_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
@@ -63,7 +63,6 @@
t_boolean/0,
t_byte/0,
t_char/0,
- t_charlist/0,
t_collect_vars/1,
t_cons/0,
t_cons/2,
@@ -197,7 +196,6 @@
t_tuple_size/1,
t_tuple_sizes/1,
t_tuple_subtypes/1,
- t_unicode_string/0,
t_unify/2,
t_unify/3,
t_unit/0,
@@ -207,7 +205,7 @@
t_var/1,
t_var_name/1,
%% t_assign_variables_to_subtype/2,
- type_is_defined/3,
+ type_is_defined/4,
record_field_diffs_to_string/2,
subst_all_vars_to_any/1,
lift_list_to_pos_empty/1,
@@ -546,12 +544,12 @@ t_opaque_from_records(RecDict) ->
OpaqueRecDict =
dict:filter(fun(Key, _Value) ->
case Key of
- {opaque, _Name} -> true;
+ {opaque, _Name, _Arity} -> true;
_ -> false
end
end, RecDict),
OpaqueTypeDict =
- dict:map(fun({opaque, Name}, {Module, Type, ArgNames}) ->
+ dict:map(fun({opaque, Name, _Arity}, {Module, Type, ArgNames}) ->
case ArgNames of
[] ->
t_opaque(Module, Name, [], t_from_form(Type, RecDict));
@@ -709,8 +707,8 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
MFA = {RemMod, Name, ArgsLen},
case sets:is_element(MFA, ET) of
true ->
- case lookup_type(Name, RemDict) of
- {type, {_Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) ->
+ case lookup_type(Name, ArgsLen, RemDict) of
+ {type, {_Mod, Type, ArgNames}} ->
{NewType, NewCycle, NewRR} =
case can_unfold_more(RemType, C) of
true ->
@@ -728,7 +726,7 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
false -> RT
end,
{RT1, RetRR};
- {opaque, {Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) ->
+ {opaque, {Mod, Type, ArgNames}} ->
List = lists:zip(ArgNames, Args),
TmpVarDict = dict:from_list(List),
{Rep, NewCycle, NewRR} =
@@ -748,12 +746,6 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
{t_from_form({opaque, -1, Name, {Mod, Args, RT1}},
RemDict, TmpVarDict),
RetRR};
- {type, _} ->
- Msg = io_lib:format("Unknown remote type ~w\n", [Name]),
- throw({error, Msg});
- {opaque, _} ->
- Msg = io_lib:format("Unknown remote opaque type ~w\n", [Name]),
- throw({error, Msg});
error ->
Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
[RemMod, Name]),
@@ -1458,21 +1450,6 @@ t_is_tuple(_) -> false.
t_bitstrlist() ->
t_iolist(1, t_bitstr()).
--spec t_charlist() -> erl_type().
-
-t_charlist() ->
- t_charlist(1).
-
--spec t_charlist(non_neg_integer()) -> erl_type().
-
-t_charlist(N) when N > 0 ->
- t_maybe_improper_list(t_sup([t_unicode_char(),
- t_unicode_binary(),
- t_charlist(N-1)]),
- t_sup(t_unicode_binary(), t_nil()));
-t_charlist(0) ->
- t_maybe_improper_list(t_any(), t_sup(t_unicode_binary(), t_nil())).
-
-spec t_constant() -> erl_type().
t_constant() ->
@@ -1568,21 +1545,6 @@ t_parameterized_module() ->
t_timeout() ->
t_sup(t_non_neg_integer(), t_atom('infinity')).
--spec t_unicode_binary() -> erl_type().
-
-t_unicode_binary() ->
- t_binary(). % with characters encoded in UTF-8 coding standard
-
--spec t_unicode_char() -> erl_type().
-
-t_unicode_char() ->
- t_integer(). % representing a valid unicode codepoint
-
--spec t_unicode_string() -> erl_type().
-
-t_unicode_string() ->
- t_list(t_unicode_char()).
-
%%-----------------------------------------------------------------------------
%% Some built-in opaque types
%%
@@ -3273,6 +3235,8 @@ t_to_string(?bitstr(0, 0), _RecDict) ->
"<<>>";
t_to_string(?bitstr(8, 0), _RecDict) ->
"binary()";
+t_to_string(?bitstr(1, 0), _RecDict) ->
+ "bitstring()";
t_to_string(?bitstr(0, B), _RecDict) ->
lists:flatten(io_lib:format("<<_:~w>>", [B]));
t_to_string(?bitstr(U, 0), _RecDict) ->
@@ -3432,7 +3396,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),
@@ -3566,7 +3530,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};
@@ -3712,8 +3676,9 @@ t_from_form({type, _L, union, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
{L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict),
{t_sup(L), R};
t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
- case lookup_type(Name, RecDict) of
- {type, {_Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
+ ArgsLen = length(Args),
+ case lookup_type(Name, ArgsLen, RecDict) of
+ {type, {_Module, Type, ArgNames}} ->
case can_unfold_more({type, Name}, TypeNames) of
true ->
List = lists:zipwith(
@@ -3733,7 +3698,7 @@ t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
end;
false -> {t_any(), [{type, Name}]}
end;
- {opaque, {Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
+ {opaque, {Module, Type, ArgNames}} ->
{Rep, Rret} =
case can_unfold_more({opaque, Name}, TypeNames) of
true ->
@@ -3762,12 +3727,9 @@ t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
RecDict, VarDict)
end,
{Tret, Rret};
- {type, _} ->
- throw({error, io_lib:format("Unknown type ~w\n", [Name])});
- {opaque, _} ->
- throw({error, io_lib:format("Unknown opaque type ~w\n", [Name])});
error ->
- throw({error, io_lib:format("Unable to find type ~w\n", [Name])})
+ Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]),
+ throw({error, Msg})
end;
t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _InOpaque,
_RecDict, _VarDict) ->
@@ -3902,14 +3864,16 @@ t_form_to_string({type, _L, binary, [Base, Unit]} = Type) ->
case {U, B} of
{0, 0} -> "<<>>";
{8, 0} -> "binary()";
+ {1, 0} -> "bitstring()";
{0, B} -> lists:flatten(io_lib:format("<<_:~w>>", [B]));
{U, 0} -> lists:flatten(io_lib:format("<<_:_*~w>>", [U]));
{U, B} -> lists:flatten(io_lib:format("<<_:~w,_:_*~w>>", [B, U]))
end;
_ -> io_lib:format("Badly formed bitstr type ~w", [Type])
end;
+t_form_to_string({type, _L, bitstring, []}) -> "bitstring()";
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), ",") ++ ") -> "
@@ -3930,7 +3894,7 @@ t_form_to_string({type, _L, range, [From, To]} = Type) ->
case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
{{integer, _, FromVal}, {integer, _, ToVal}} ->
io_lib:format("~w..~w", [FromVal, ToVal]);
- _ -> io_lib:format("Bad formed type ~w",[Type])
+ _ -> io_lib:format("Badly formed type ~w",[Type])
end;
t_form_to_string({type, _L, record, [{atom, _, Name}]}) ->
io_lib:format("#~w{}", [Name]);
@@ -4018,20 +3982,20 @@ lookup_record(Tag, Arity, RecDict) when is_atom(Tag) ->
error -> error
end.
-lookup_type(Name, RecDict) ->
- case dict:find({type, Name}, RecDict) of
+lookup_type(Name, Arity, RecDict) ->
+ case dict:find({type, Name, Arity}, RecDict) of
error ->
- case dict:find({opaque, Name}, RecDict) of
+ case dict:find({opaque, Name, Arity}, RecDict) of
error -> error;
{ok, Found} -> {opaque, Found}
end;
{ok, Found} -> {type, Found}
end.
--spec type_is_defined('type' | 'opaque', atom(), dict()) -> boolean().
+-spec type_is_defined('type' | 'opaque', atom(), arity(), dict()) -> boolean().
-type_is_defined(TypeOrOpaque, Name, RecDict) ->
- dict:is_key({TypeOrOpaque, Name}, RecDict).
+type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
+ dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
can_unfold_more(TypeName, TypeNames) ->
Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end,
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index cfd22a9d8d..73ab1dbfda 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>2012</year>
+ <year>2006</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,76 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Bug fixed in hipe to where it did not allow unicode code
+ points 16#FFFE and 16#FFFF in bit syntax in natively
+ compiled modules.</p>
+ <p>
+ Own Id: OTP-10867</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in hipe compiled code related to the handling of
+ <c>is_number/1</c>. (Thanks to Sebastian Egner and
+ Johannes Wei�l for minimal test code and Kostis for quick
+ patch)</p>
+ <p>
+ Own Id: OTP-10897</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The type <c>ascii_string()</c> in the <c>base64</c>
+ module has been corrected. The type
+ <c>file:file_info()</c> has been cleaned up. The type
+ <c>file:fd()</c> has been made opaque in the
+ documentation. </p>
+ <p>
+ Own Id: OTP-10624 Aux Id: kunagi-352 [263] </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Update .gitignore (lib/hipe/boot_ebin). Thanks to Tuncer
+ Ayaz.</p>
+ <p>
+ Own Id: OTP-10705</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.9.3</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 ecf1c77abc..81249c958e 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -41,6 +41,9 @@
%%
%%-ifndef(DEBUG).
%%-define(DEBUG,6).
+%% Choose one of two tracing methods
+%%-define(DEBUG_BIF_CALL_TRACE,true).
+%%-define(IO_FORMAT_CALL_TRACE,true).
%%-endif.
-include("../main/hipe.hrl").
@@ -51,8 +54,27 @@
-define(no_debug_msg(Str,Xs),ok).
%%-define(no_debug_msg(Str,Xs),msg(Str,Xs)).
--define(mk_debugcode(MFA, Env, Code),
- case MFA of
+-ifdef(DEBUG_BIF_CALL_TRACE).
+
+%% Use BIF hipe_bifs_debug_native_called_2 to trace function calls
+mk_debug_calltrace({_M,_F,A}=MFA, Env, Code) ->
+ MFAVar = mk_var(new),
+ Ignore = mk_var(new),
+ MkMfa = hipe_icode:mk_move(MFAVar,hipe_icode:mk_const(MFA)),
+ Args = [mk_var({x,I-1}) || I <- lists:seq(1,A)],
+ ArgTup = mk_var(new),
+ MkArgTup = hipe_icode:mk_primop([ArgTup], mktuple, Args),
+ Call = hipe_icode:mk_primop([Ignore], debug_native_called,
+ [MFAVar,ArgTup]),
+ {[MkMfa,MkArgTup,Call | Code], Env}.
+
+-endif.
+
+-ifdef(IO_FORMAT_CALL_TRACE).
+
+%% Use io:format to trace function calls
+mk_debug_calltrace(MFA, Env, Code) ->
+ case MFA of
{io,_,_} ->
%% We do not want to loop infinitely if we are compiling
%% the module io.
@@ -69,7 +91,9 @@
Call =
hipe_icode:mk_call([Ignore],io,format,[StringVar,MFAVar],remote),
{[MkMfa,MkString,Call | Code], Env}
- end).
+ end.
+-endif.
+
%%-----------------------------------------------------------------------
%% Types
@@ -126,7 +150,7 @@ trans_mfa_code(M,F,A, FunBeamCode, ClosureInfo) ->
MFA = {M,F,A},
%% Debug code
?IF_DEBUG_LEVEL(5,
- {Code3,_Env3} = ?mk_debugcode(MFA, Env2, Code2),
+ {Code3,_Env3} = mk_debug_calltrace(MFA, Env1, Code2),
{Code3,_Env3} = {Code2,Env1}),
%% For stack optimization
Leafness = leafness(Code3),
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_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
index a413531c07..b0113fc556 100644
--- a/lib/hipe/icode/hipe_icode_primops.erl
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -137,7 +137,8 @@ is_safe({hipe_bs_primop, {bs_private_append, _, _}}) -> false;
is_safe({hipe_bs_primop, bs_init_writable}) -> true;
is_safe(#mkfun{}) -> true;
is_safe(#unsafe_element{}) -> true;
-is_safe(#unsafe_update_element{}) -> true.
+is_safe(#unsafe_update_element{}) -> true;
+is_safe(debug_native_called) -> false.
-spec fails(icode_funcall()) -> boolean().
@@ -237,6 +238,7 @@ fails({hipe_bs_primop, bs_init_writable}) -> true;
fails(#mkfun{}) -> false;
fails(#unsafe_element{}) -> false;
fails(#unsafe_update_element{}) -> false;
+fails(debug_native_called) -> false;
%% Apparently, we are calling fails/1 for all MFAs which are compiled.
%% This is weird and we should restructure the compiler to avoid
%% calling fails/1 for things that are not primops.
@@ -721,6 +723,8 @@ type(Primop, Args) ->
erl_types:t_any();
redtest ->
erl_types:t_any();
+ debug_native_called ->
+ erl_types:t_any();
{M, F, A} ->
erl_bif_types:type(M, F, A, Args)
end.
@@ -893,6 +897,8 @@ type(Primop) ->
erl_types:t_any();
redtest ->
erl_types:t_any();
+ debug_native_called ->
+ erl_types:t_any();
{M, F, A} ->
erl_bif_types:type(M, F, A)
end.
diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl
index c222e8a5d5..46c5a39f2c 100644
--- a/lib/hipe/icode/hipe_icode_range.erl
+++ b/lib/hipe/icode/hipe_icode_range.erl
@@ -784,6 +784,8 @@ analyse_type(Type, Info, Rewrite) ->
integer ->
TrueRange = inf(any_range(), OldVarRange),
FalseRange = inf(none_range(), OldVarRange);
+ number ->
+ TrueRange = FalseRange = OldVarRange;
_ ->
TrueRange = inf(none_range(), OldVarRange),
FalseRange = OldVarRange
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/main/hipe.erl b/lib/hipe/main/hipe.erl
index b2789978a4..6e00b13292 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -1135,7 +1135,7 @@ option_text(debug) ->
option_text(icode_range) ->
"Performs integer range analysis on the Icode level";
option_text(icode_ssa_check) ->
- "Checks whether Icode is on SSA form or not\n";
+ "Checks whether Icode is on SSA form or not";
option_text(icode_ssa_copy_prop) ->
"Performs copy propagation on Icode SSA";
option_text(icode_ssa_const_prop) ->
@@ -1143,14 +1143,14 @@ option_text(icode_ssa_const_prop) ->
option_text(icode_ssa_struct_reuse) ->
"Factors out common tuple and list constructions on Icode SSA";
option_text(icode_type) ->
- "Performs type analysis on the Icode level" ++
+ "Performs type analysis on the Icode level\n" ++
"and then simplifies the code based on the results of this analysis";
option_text(load) ->
"Automatically load the produced native code into memory";
option_text(peephole) ->
"Enables peephole optimizations";
option_text(pmatch) ->
- "Enables pattern matching compilation when compiling from Core; " ++
+ "Enables pattern matching compilation when compiling from Core;\n" ++
"has no effect when compiling from BEAM bytecode";
option_text(pp_asm) ->
"Displays assembly listing with addresses and bytecode\n" ++
@@ -1197,11 +1197,11 @@ option_text(timeout) ->
" The limit must be a non-negative integer or the atom 'infinity'.\n" ++
" The current default limit is 15 minutes (900000 ms).";
option_text(use_indexing) ->
- "Use indexing for multiple-choice branch selection.";
+ "Use indexing for multiple-choice branch selection";
option_text(use_callgraph) ->
- "Compile the functions in a module according to a reversed topological " ++
- "sorted order to gain more information when using a persistent lookup " ++
- "table for storing intra-modular type information.";
+ "Compile the functions in a module according to a reversed topological\n" ++
+ "sorted order to gain more information when using a persistent lookup\n" ++
+ "table for storing intra-modular type information";
option_text(verbose) ->
"Output information about what is being done";
option_text(Opt) when is_atom(Opt) ->
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 5f273d8251..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
@@ -396,6 +396,8 @@ gen_primop({Op,Dst,Args,Cont,Fail}, IsGuard, ConstTab) ->
[Dst1]->
hipe_tagscheme:unsafe_tag_float(Dst1, Arg)
end;
+ debug_native_called ->
+ [hipe_rtl:mk_call(Dst, Op, Args, Cont, Fail, not_remote)];
%% Only names listed above are accepted! MFA:s are not primops!
_ ->
@@ -736,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
@@ -827,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/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/vsn.mk b/lib/hipe/vsn.mk
index f3e2e695b5..6cd87708ef 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.9.3
+HIPE_VSN = 3.10.1
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 de46eadb3b..856823b1b3 100644
--- a/lib/ic/c_src/Makefile.in
+++ b/lib/ic/c_src/Makefile.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/c_src/ic.c b/lib/ic/c_src/ic.c
index 1ace9ea1af..1c9b58b21b 100644
--- a/lib/ic/c_src/ic.c
+++ b/lib/ic/c_src/ic.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index 3ac35aa0e5..92cc7cab7a 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>2012</year>
+ <year>1998</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,7 +30,48 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.2.31</title>
+ <section><title>IC 4.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Revert the structs <c>erlang_pid</c>, <c>erlang_port</c>
+ and <c>erlang_ref</c> as they were before R16A (without
+ <c>node_org_enc</c>) in order to be backward compatible
+ with user code that accesses the fields of these structs.</p>
+ <p>
+ Own Id: OTP-10885 Aux Id: seq12256 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ <item>
+ <p>
+ Adapt ic for changes in erl_interface and jinterface due
+ to utf8 atom support. This change makes ic dependent on
+ erl_interface-3.7.10 (R16) or later in order to build.</p>
+ <p>
+ Own Id: OTP-10785</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.2.31</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ic/examples/all-against-all/client.c b/lib/ic/examples/all-against-all/client.c
index e0a52b142d..a638ac6b86 100644
--- a/lib/ic/examples/all-against-all/client.c
+++ b/lib/ic/examples/all-against-all/client.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
diff --git a/lib/ic/examples/c-client/client.c b/lib/ic/examples/c-client/client.c
index 816477cf15..53dbbf9192 100644
--- a/lib/ic/examples/c-client/client.c
+++ b/lib/ic/examples/c-client/client.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
diff --git a/lib/ic/examples/c-server/client.c b/lib/ic/examples/c-server/client.c
index fa570089b5..d683d586d8 100644
--- a/lib/ic/examples/c-server/client.c
+++ b/lib/ic/examples/c-server/client.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. 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
diff --git a/lib/ic/examples/pre_post_condition/Makefile b/lib/ic/examples/pre_post_condition/Makefile
index d18f81fec9..53e647e793 100644
--- a/lib/ic/examples/pre_post_condition/Makefile
+++ b/lib/ic/examples/pre_post_condition/Makefile
@@ -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/Any.java b/lib/ic/java_src/com/ericsson/otp/ic/Any.java
index 7337241908..003ad5dd93 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/Any.java
+++ b/lib/ic/java_src/com/ericsson/otp/ic/Any.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
@@ -417,6 +417,8 @@ public class Any {
switch (__tag) {
case (com.ericsson.otp.erlang.OtpExternal.atomTag):
+ case (com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag):
+ case (com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag):
java.lang.String __elabel = _is.read_atom(); // Enumerant or Boolean
os.write_atom(__elabel);
diff --git a/lib/ic/java_src/com/ericsson/otp/ic/Environment.java b/lib/ic/java_src/com/ericsson/otp/ic/Environment.java
index f0c66f0726..fff854a5f8 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/Environment.java
+++ b/lib/ic/java_src/com/ericsson/otp/ic/Environment.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
@@ -334,6 +334,8 @@ public class Environment {
switch (tag) {
case com.ericsson.otp.erlang.OtpExternal.atomTag:
+ case com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag:
+ case com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag:
operation = is.read_atom();
break;
default:
@@ -344,6 +346,8 @@ public class Environment {
tag = is.peek();
switch (tag) {
case com.ericsson.otp.erlang.OtpExternal.atomTag:
+ case com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag:
+ case com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag:
operation = is.read_atom();
break;
default:
diff --git a/lib/ic/java_src/com/ericsson/otp/ic/Makefile b/lib/ic/java_src/com/ericsson/otp/ic/Makefile
index cf4c353f3f..273614e8d9 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/Makefile
+++ b/lib/ic/java_src/com/ericsson/otp/ic/Makefile
@@ -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/java_src/com/ericsson/otp/ic/Term.java b/lib/ic/java_src/com/ericsson/otp/ic/Term.java
index 9219cb7038..510e3b6644 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/Term.java
+++ b/lib/ic/java_src/com/ericsson/otp/ic/Term.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
@@ -57,7 +57,10 @@ final public class Term extends Any {
public boolean isAtom() {
if (ObjV == null) {
- if (tag == com.ericsson.otp.erlang.OtpExternal.atomTag)
+ if (tag == com.ericsson.otp.erlang.OtpExternal.atomTag ||
+ tag == com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag ||
+ tag == com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag)
+
return true;
return false;
diff --git a/lib/ic/java_src/com/ericsson/otp/ic/TermHelper.java b/lib/ic/java_src/com/ericsson/otp/ic/TermHelper.java
index 437d38743b..2942480283 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/TermHelper.java
+++ b/lib/ic/java_src/com/ericsson/otp/ic/TermHelper.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
@@ -69,6 +69,8 @@ public class TermHelper {
break;
case com.ericsson.otp.erlang.OtpExternal.atomTag:
+ case com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag:
+ case com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag:
_in.reset();
_value.atomV = _in.read_atom();
break;
diff --git a/lib/ic/java_src/com/ericsson/otp/ic/TypeCode.java b/lib/ic/java_src/com/ericsson/otp/ic/TypeCode.java
index 6d049f75f7..734872386b 100644
--- a/lib/ic/java_src/com/ericsson/otp/ic/TypeCode.java
+++ b/lib/ic/java_src/com/ericsson/otp/ic/TypeCode.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. 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
@@ -723,6 +723,8 @@ public class TypeCode {
switch(__tag) {
case (com.ericsson.otp.erlang.OtpExternal.atomTag):
+ case (com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag):
+ case (com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag):
__kind = TypeCode.kind(_is.read_atom());
switch(__kind.value()) {
@@ -757,6 +759,8 @@ public class TypeCode {
switch(__tag) {
case (com.ericsson.otp.erlang.OtpExternal.atomTag):
+ case (com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag):
+ case (com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag):
__kind = TypeCode.kind(_is.read_atom());
_tc = new TypeCode();
@@ -814,6 +818,9 @@ public class TypeCode {
__label.insert_string(__enum);
break;
case (com.ericsson.otp.erlang.OtpExternal.atomTag):
+ case (com.ericsson.otp.erlang.OtpExternal.atomUtf8Tag):
+ case (com.ericsson.otp.erlang.OtpExternal.smallAtomUtf8Tag):
+
java.lang.String __default = _is.read_atom();
__label.insert_atom(__default);
break;
diff --git a/lib/ic/src/Makefile b/lib/ic/src/Makefile
index 280d86a8a4..e8769d2335 100644
--- a/lib/ic/src/Makefile
+++ b/lib/ic/src/Makefile
@@ -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/test/c_client_erl_server_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
index e4f9cfdece..f910592609 100644
--- a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
index f352b91fd5..40ff898dcb 100644
--- a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2003-2010. 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
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
index b2c5b0c836..f270d2abb9 100644
--- a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
@@ -1,7 +1,7 @@
/*
* %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
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c b/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c
index 305017ae85..3cc71aa2e8 100644
--- a/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c b/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c
index c423a9e51c..96fac33b29 100644
--- a/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c
@@ -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
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 3143ab427b..89fca8d270 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
@@ -1,7 +1,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
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index 0dbf5a1f52..9fd8aedb63 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.2.31
+IC_VSN = 4.3.1
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 14ce3cbe7f..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,25 +465,23 @@ 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>
<v>MaxKeepAlive = integer() </v>
@@ -520,6 +523,13 @@ apply(Module, Function, [ReplyInfo | Args])
<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>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)
@@ -554,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 |
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 3fced5dfcd..8438961511 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>2012</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -163,11 +163,9 @@
</item>
<marker id="prop_socket_type"></marker>
- <tag>{socket_type, ip_comm | ssl | essl}</tag>
+ <tag>{socket_type, ip_comm | {essl, Config::proplist()}}</tag>
<item>
- <p>When using ssl, there are currently only one alternative.
- <c>essl</c> specifically uses the Erlang based SSL.
- <c>ssl</c> defaults to <c>essl</c>. </p>
+ <p> For ssl configuration options see <seealso marker="ssl:ssl#listen-2">ssl:listen/2</seealso> </p>
<p>Defaults to <c>ip_comm</c>. </p>
</item>
@@ -395,71 +393,7 @@ bytes
</item>
</taglist>
-
- <marker id="props_ssl"></marker>
- <p><em>ssl properties</em></p>
- <taglist>
- <marker id="prop_ssl_ca_cert_file"></marker>
- <tag>{ssl_ca_certificate_file, path()}</tag>
- <item>
- <p>Used as cacertfile option in ssl:listen/2 see
- <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- <marker id="prop_ssl_cert_file"></marker>
- <tag>{ssl_certificate_file, path()}</tag>
- <item>
- <p>Used as certfile option in ssl:listen/2 see
- <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- <marker id="prop_ssl_ciphers"></marker>
- <tag>{ssl_ciphers, list()}</tag>
- <item>
- <p>Used as ciphers option in ssl:listen/2 see
- <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- <marker id="prop_ssl_verify_client"></marker>
- <tag>{ssl_verify_client, integer()}</tag>
- <item>
- <p>Used as verify option in ssl:listen/2 see
- <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- <marker id="prop_ssl_verify_depth"></marker>
- <tag>{ssl_verify_depth, integer()}</tag>
- <item>
- <p>Used as depth option in ssl:listen/2 see
- <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- <marker id="prop_ssl_passwd_callback_funct"></marker>
- <tag>{ssl_password_callback_function, atom()}</tag>
- <item>
- <p>Used together with ssl_password_callback_module
- to retrieve a value to use as password option to ssl:listen/2
- see <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- <marker id="prop_ssl_passwd_callback_args"></marker>
- <tag>{ssl_password_callback_arguments, list()}</tag>
- <item>
- <p>Used together with ssl_password_callback_function to supply a
- list of arguments to the callback function. If not specified
- the callback function will be assumed to have arity 0. </p>
- </item>
-
- <marker id="prop_ssl_passwd_callback_mod"></marker>
- <tag>{ssl_password_callback_module, atom()}</tag>
- <item>
- <p>Used together with ssl_password_callback_function
- to retrieve a value to use as password option to ssl:listen/2
- see <seealso marker="ssl:ssl">ssl(3)</seealso>. </p>
- </item>
-
- </taglist>
-
+
<marker id="props_alias"></marker>
<p><em>URL aliasing properties - requires mod_alias</em></p>
<taglist>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index e0d6ae3454..525beecd31 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2012</year>
+ <year>2002</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,7 +33,76 @@
</header>
- <section><title>Inets 5.9.2</title>
+ <section><title>Inets 5.9.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpd: The modules option now defaults to the documented
+ value.</p>
+ <p>
+ Own Id: OTP-10844</p>
+ </item>
+ <item>
+ <p>
+ httpc: Fixed persistent connection implementation that
+ was broken by a patch to R13. The patch made persisten
+ connections behaved the same way as pipelining.</p>
+ <p>
+ Own Id: OTP-10845</p>
+ </item>
+ <item>
+ <p>
+ httpd: Simplified configuration of ssl in httpd, this
+ also enables all ssl options to be configured. The old
+ and limited way is no longer documented but will be
+ supported for backwards comatibility for some time.</p>
+ <p>
+ Own Id: OTP-10846</p>
+ </item>
+ <item>
+ <p>
+ Handle correctly the "No files found or file unavailable"
+ error code. Thanks to Serge Aleynikov</p>
+ <p>
+ Own Id: OTP-10886</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpc: The HTTP client now supports HTTPS through proxies</p>
+ <p>
+ Own Id: OTP-10256 Aux Id: kunagi-2
+ [ce2e800e-c99f-4050-a1c4-f47023d9c7aa-1] </p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Fix autoredirect for POST requests responding 303. Thanks
+ to Hans Svensson.</p>
+ <p>
+ Own Id: OTP-10765</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index fe25c23316..132a384a49 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -1899,6 +1899,10 @@ ctrl_result_response(pos_compl, #state{client = From} = State, _) ->
gen_server:reply(From, ok),
{noreply, State#state{client = undefined, caller = undefined}};
+ctrl_result_response(enofile, #state{client = From} = State, _) ->
+ gen_server:reply(From, {error, enofile}),
+ {noreply, State#state{client = undefined, caller = undefined}};
+
ctrl_result_response(Status, #state{client = From} = State, _)
when (Status =:= etnospc) orelse
(Status =:= epnospc) orelse
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl
index faeacb31ab..4bf788e946 100644
--- a/lib/inets/src/ftp/ftp_response.erl
+++ b/lib/inets/src/ftp/ftp_response.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -162,6 +162,7 @@ error_string(epath) -> "No such file or directory, already exists, "
error_string(etype) -> "No such type.";
error_string(euser) -> "User name or password not valid.";
error_string(etnospc) -> "Insufficient storage space in system.";
+error_string(enofile) -> "No files found or file unavailable";
error_string(epnospc) -> "Exceeded storage allocation "
"(for current directory or dataset).";
error_string(efnamena) -> "File name not allowed.";
@@ -180,6 +181,8 @@ interpret_status(?POS_COMPL,_,_) -> pos_compl;
interpret_status(?POS_INTERM,?AUTH_ACC,2) -> pos_interm_acct;
%% Positive Intermediate Reply
interpret_status(?POS_INTERM,_,_) -> pos_interm;
+%% No files found or file not available
+interpret_status(?TRANS_NEG_COMPL,?FILE_SYSTEM,0) -> enofile;
%% No storage area no action taken
interpret_status(?TRANS_NEG_COMPL,?FILE_SYSTEM,2) -> etnospc;
%% Temporary Error, no action taken
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index b6e7708353..ede649a5a9 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -917,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]);
@@ -979,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 923213d34d..857043bae2 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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, % {Module, 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,147 +253,133 @@ 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} = State0)
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", []),
-
- %% 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),
-
- 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,
- 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", []),
- {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}
+ ?hcrd("request sent", []),
+
+ %% Activate the request time out for the new request
+ State1 =
+ activate_request_timeout(State0#state{request = Request}),
+
+ ClientClose =
+ httpc_request:is_client_closing(Request#request.headers),
+
+ case State0#state.request of
+ #request{} = OldRequest -> %% Old request not yet finished
+ ?hcrd("old request still not finished", []),
+ %% Make sure to use the new value of timers in state
+ NewTimers = State1#state.timers,
+ NewPipeline = queue:in(Request, State1#state.pipeline),
+ NewSession =
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(NewPipeline) + 1,
+ client_close = ClientClose},
+ insert_session(NewSession, ProfileName),
+ ?hcrd("session updated", []),
+ {reply, ok, State1#state{
+ request = OldRequest,
+ 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),
+ NewTimers = Timers#timers{queue_timer = undefined},
+ ?hcrd("session created", []),
+ State = init_wait_for_response_state(Request, State1#state{session = NewSession,
+ timers = NewTimers}),
+ {reply, ok, State}
+ end;
+ {error, Reason} ->
+ ?hcri("failed sending request", [{reason, Reason}]),
+ {reply, {pipeline_failed, Reason}, State0}
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} = State0)
when Status =/= undefined ->
?hcrv("new request on a keep-alive session",
- [{request, Request},
- {profile, ProfileName},
- {status, Status}]),
-
- Address = handle_proxy(Addr, Proxy),
- case httpc_request:send(Address, Session, Request) of
- ok ->
-
- ?hcrd("request sent", []),
-
- %% 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),
+ [{request, Request},
+ {profile, ProfileName},
+ {status, Status}]),
+
+ ClientClose = httpc_request:is_client_closing(Request#request.headers),
+
+ case State0#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", []),
+ NewKeepAlive = queue:in(Request, State0#state.keep_alive),
+ NewSession =
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(NewKeepAlive) + 1,
+ client_close = ClientClose},
+ insert_session(NewSession, ProfileName),
+ ?hcrd("session updated", []),
+ {reply, ok, State0#state{keep_alive = NewKeepAlive,
+ session = NewSession}};
+ 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),
+ Address = handle_proxy(Addr, Proxy),
+ case httpc_request:send(Address, Session, Request) of
+ ok ->
+ ?hcrd("request sent", []),
- 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", []),
- {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 =
+ %% Activate the request time out for the new request
+ State1 =
+ activate_request_timeout(State0#state{request = Request}),
+ NewTimers = State1#state.timers,
+ 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}
+ State = init_wait_for_response_state(Request, State1#state{session = NewSession,
+ timers = NewTimers}),
+ {reply, ok, State};
+ {error, Reason} ->
+ ?hcri("failed sending request", [{reason, Reason}]),
+ {reply, {request_failed, Reason}, State0}
+ end
end;
-
handle_call(info, _, State) ->
Info = handler_info(State),
{reply, Info, State}.
-
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
@@ -411,25 +400,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 +435,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 +561,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 +608,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 +636,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 +659,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 +691,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 +717,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 +750,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 +795,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 +819,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 +1037,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 +1142,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 ->
@@ -1202,8 +1225,7 @@ handle_queue(#state{status = pipeline} = State, Data) ->
handle_pipeline(#state{status = pipeline,
session = Session,
profile_name = ProfileName,
- options = #options{pipeline_timeout = TimeOut}} =
- State,
+ options = #options{pipeline_timeout = TimeOut}} = State,
Data) ->
?hcrd("handle pipeline", [{profile, ProfileName},
@@ -1213,25 +1235,7 @@ handle_pipeline(#state{status = pipeline,
case queue:out(State#state.pipeline) of
{empty, _} ->
?hcrd("pipeline queue empty", []),
-
- %% The server may choose too teminate an idle pipeline
- %% in this case we want to receive the close message
- %% at once and not when trying to pipeline the next
- %% request.
- activate_once(Session),
-
- %% If a pipeline that has been idle for some time is not
- %% closed by the server, the client may want to close it.
- NewState = activate_queue_timeout(TimeOut, State),
- update_session(ProfileName, Session, #session.queue_length, 0),
- %% Note mfa will be initilized when a new request
- %% arrives.
- {noreply,
- NewState#state{request = undefined,
- mfa = undefined,
- status_line = undefined,
- headers = undefined,
- body = undefined}};
+ handle_empty_queue(Session, ProfileName, TimeOut, State);
{{value, NextRequest}, Pipeline} ->
?hcrd("pipeline queue non-empty", []),
case lists:member(NextRequest#request.id,
@@ -1249,38 +1253,17 @@ handle_pipeline(#state{status = pipeline,
Session#session{queue_length =
%% Queue + current
queue:len(Pipeline) + 1},
- insert_session(NewSession, ProfileName),
- Relaxed =
- (NextRequest#request.settings)#http_options.relaxed,
- MFA = {httpc_response,
- parse,
- [State#state.max_header_size, Relaxed]},
- NewState =
- State#state{pipeline = Pipeline,
- request = NextRequest,
- mfa = MFA,
- status_line = undefined,
- headers = undefined,
- body = undefined},
- case Data of
- <<>> ->
- activate_once(Session),
- {noreply, NewState};
- _ ->
- %% If we already received some bytes of
- %% the next response
- handle_info({httpc_handler, dummy, Data},
- NewState)
- end
+ receive_response(NextRequest,
+ NewSession, Data,
+ State#state{pipeline = Pipeline})
end
end.
-handle_keep_alive_queue(
- #state{status = keep_alive,
- session = Session,
- profile_name = ProfileName,
- options = #options{keep_alive_timeout = TimeOut}} = State,
- Data) ->
+handle_keep_alive_queue(#state{status = keep_alive,
+ session = Session,
+ profile_name = ProfileName,
+ options = #options{keep_alive_timeout = TimeOut}} = State,
+ Data) ->
?hcrd("handle keep_alive", [{profile, ProfileName},
{session, Session},
@@ -1289,25 +1272,7 @@ handle_keep_alive_queue(
case queue:out(State#state.keep_alive) of
{empty, _} ->
?hcrd("keep_alive queue empty", []),
- %% The server may choose too terminate an idle keep_alive session
- %% in this case we want to receive the close message
- %% at once and not when trying to send the next
- %% request.
- activate_once(Session),
- %% If a keep_alive session has been idle for some time is not
- %% closed by the server, the client may want to close it.
- NewState = activate_queue_timeout(TimeOut, State),
- update_session(ProfileName, Session, #session.queue_length, 0),
- %% Note mfa will be initilized when a new request
- %% arrives.
- {noreply,
- NewState#state{request = undefined,
- mfa = undefined,
- status_line = undefined,
- headers = undefined,
- body = undefined
- }
- };
+ handle_empty_queue(Session, ProfileName, TimeOut, State);
{{value, NextRequest}, KeepAlive} ->
?hcrd("keep_alive queue non-empty", []),
case lists:member(NextRequest#request.id,
@@ -1318,30 +1283,61 @@ handle_keep_alive_queue(
State#state{keep_alive = KeepAlive}, Data);
false ->
?hcrv("next request", [{request, NextRequest}]),
- Relaxed =
- (NextRequest#request.settings)#http_options.relaxed,
- MFA = {httpc_response, parse,
- [State#state.max_header_size, Relaxed]},
- NewState =
- State#state{request = NextRequest,
- keep_alive = KeepAlive,
- mfa = MFA,
- status_line = undefined,
- headers = undefined,
- body = undefined},
- case Data of
- <<>> ->
- activate_once(Session),
- {noreply, NewState};
- _ ->
- %% If we already received some bytes of
- %% the next response
- handle_info({httpc_handler, dummy, Data},
- NewState)
+ #request{address = Address} = NextRequest,
+ case httpc_request:send(Address, Session, NextRequest) of
+ ok ->
+ receive_response(NextRequest,
+ Session, <<>>,
+ State#state{keep_alive = KeepAlive});
+ {error, Reason} ->
+ {reply, {keep_alive_failed, Reason}, State}
end
end
end.
+handle_empty_queue(Session, ProfileName, TimeOut, State) ->
+ %% The server may choose too terminate an idle pipline| keep_alive session
+ %% in this case we want to receive the close message
+ %% at once and not when trying to send the next
+ %% request.
+ activate_once(Session),
+ %% If a pipline | keep_alive session has been idle for some time is not
+ %% closed by the server, the client may want to close it.
+ NewState = activate_queue_timeout(TimeOut, State),
+ update_session(ProfileName, Session, #session.queue_length, 0),
+ %% Note mfa will be initilized when a new request
+ %% arrives.
+ {noreply,
+ NewState#state{request = undefined,
+ mfa = undefined,
+ status_line = undefined,
+ headers = undefined,
+ body = undefined
+ }
+ }.
+
+receive_response(Request, Session, Data, State) ->
+ NewState = init_wait_for_response_state(Request, State),
+ gather_data(Data, Session, NewState).
+
+init_wait_for_response_state(Request, State) ->
+ Relaxed =
+ (Request#request.settings)#http_options.relaxed,
+ MFA = {httpc_response, parse,
+ [State#state.max_header_size, Relaxed]},
+ State#state{request = Request,
+ mfa = MFA,
+ status_line = undefined,
+ headers = undefined,
+ body = undefined}.
+
+gather_data(<<>>, Session, State) ->
+ activate_once(Session),
+ {noreply, State};
+gather_data(Data, _, State) ->
+ %% If we already received some bytes of
+ %% the next response
+ handle_info({httpc_handler, dummy, Data}, State).
case_insensitive_header(Str) when is_list(Str) ->
http_util:to_lower(Str);
@@ -1503,6 +1499,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 +1698,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
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 3612b331e7..c45dcab802 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -577,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),
@@ -741,7 +742,7 @@ get_manager_info(#state{handler_db = HDB,
SessionInfo = which_sessions2(SDB),
OptionsInfo =
[{Item, get_option(Item, Options)} ||
- Item <- record_info(fields, options)],
+ Item <- record_info(fields, options)],
CookieInfo = httpc_cookie:which_cookies(CDB),
[{handlers, HandlerInfo},
{sessions, SessionInfo},
@@ -769,20 +770,7 @@ get_handler_info(Tab) ->
Pattern = {'$2', '$1', '_'},
Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)],
Handlers2 = sort_handlers(Handlers1),
- Handlers3 = [{Pid, Reqs,
- try
- begin
- httpc_handler:info(Pid)
- end
- catch
- _:_ ->
- %% Why would this crash?
- %% Only if the process has died, but we don't
- %% know about it?
- []
- end} || {Pid, Reqs} <- Handlers2],
- Handlers3.
-
+ [{Pid, Reqs, httpc_handler:info(Pid)} || {Pid, Reqs} <- Handlers2].
handle_request(#request{settings =
#http_options{version = "HTTP/0.9"}} = Request,
@@ -1001,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}) ->
@@ -1027,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_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index 04976447cc..f177aac8f2 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -124,6 +124,11 @@ result(Response = {{_, Code, _}, _, _},
(Code =:= 303) orelse
(Code =:= 307) ->
redirect(Response, Request);
+result(Response = {{_, 303, _}, _, _},
+ Request = #request{settings =
+ #http_options{autoredirect = true},
+ method = post}) ->
+ redirect(Response, Request#request{method = get});
result(Response = {{_,503,_}, _, _}, Request) ->
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index 5eb827032f..df58fa1b81 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.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
@@ -23,7 +23,7 @@
-export([
start/1,
connect/3, connect/4,
- listen/2, listen/3, listen/4,
+ listen/4, listen/5,
accept/2, accept/3,
close/2,
send/3,
@@ -155,41 +155,41 @@ connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
%% reason for this to enable a HTTP-server not running as root to use
%% port 80.
%%-------------------------------------------------------------------------
-listen(SocketType, Port) ->
- listen(SocketType, undefined, Port).
+listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) ->
+ listen_ip_comm(Addr, Port, Fd, IpFamily);
+
+listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) ->
+ listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily).
-listen(ip_comm = _SocketType, Addr, Port) ->
- listen_ip_comm(Addr, Port, undefined);
+listen(ip_comm = _SocketType, Addr, Port, IpFamily) ->
+ listen_ip_comm(Addr, Port, undefined, IpFamily);
%% Wrapper for backaward compatibillity
-listen({ssl, SSLConfig}, Addr, Port) ->
+listen({ssl, SSLConfig}, Addr, Port, IpFamily) ->
?hlrt("listen (wrapper)",
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port);
+ listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily);
-listen({essl, SSLConfig}, Addr, Port) ->
+
+listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
?hlrt("listen (essl)",
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig]).
-
+ listen_ssl(Addr, Port, undefined, SSLConfig, IpFamily).
-listen(ip_comm, Addr, Port, Fd) ->
- listen_ip_comm(Addr, Port, Fd).
-
-listen_ip_comm(Addr, Port, Fd) ->
- case (catch do_listen_ip_comm(Addr, Port, Fd)) of
+listen_ip_comm(Addr, Port, Fd, IpFamily) ->
+ case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of
{'EXIT', Reason} ->
{error, {exit, Reason}};
Else ->
Else
end.
-do_listen_ip_comm(Addr, Port, Fd) ->
- {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd),
+do_listen_ip_comm(Addr, Port, Fd, IpFamily) ->
+ {NewPort, Opts} = get_socket_info(Addr, Port, Fd),
case IpFamily of
inet6fb4 ->
Opts2 = [inet6 | Opts],
@@ -222,10 +222,9 @@ do_listen_ip_comm(Addr, Port, Fd) ->
end.
-listen_ssl(Addr, Port, Opts0) ->
- IpFamily = ipfamily_default(Addr, Port),
- BaseOpts = [{backlog, 128}, {reuseaddr, true} | Opts0],
- Opts = sock_opts(Addr, BaseOpts),
+listen_ssl(Addr, Port, Fd, Opts0, IpFamily) ->
+ {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd),
+ Opts = SockOpt ++ Opts0,
case IpFamily of
inet6fb4 ->
Opts2 = [inet6 | Opts],
@@ -236,13 +235,13 @@ listen_ssl(Addr, Port, Opts0) ->
Opts3 = [inet | Opts],
?hlrt("ipv6 listen failed - try ipv4 instead",
[{reason, Reason}, {opts, Opts3}]),
- ssl:listen(Port, Opts3);
+ ssl:listen(NewPort, Opts3);
{'EXIT', Reason} ->
Opts3 = [inet | Opts],
?hlrt("ipv6 listen exit - try ipv4 instead",
[{reason, Reason}, {opts, Opts3}]),
- ssl:listen(Port, Opts3);
+ ssl:listen(NewPort, Opts3);
Other ->
?hlrt("ipv6 listen done", [{other, Other}]),
@@ -252,61 +251,21 @@ listen_ssl(Addr, Port, Opts0) ->
_ ->
Opts2 = [IpFamily | Opts],
?hlrt("listen", [{opts, Opts2}]),
- ssl:listen(Port, Opts2)
+ ssl:listen(NewPort, Opts2)
end.
-ipfamily_default(Addr, Port) ->
- httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4).
-get_socket_info(Addr, Port, Fd0) ->
+get_socket_info(Addr, Port, Fd) ->
BaseOpts = [{backlog, 128}, {reuseaddr, true}],
- IpFamilyDefault = ipfamily_default(Addr, Port),
%% The presence of a file descriptor takes precedence
- case get_fd(Port, Fd0, IpFamilyDefault) of
- {Fd, IpFamily} ->
- {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily};
+ case Fd of
undefined ->
- {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault}
+ {Port, sock_opts(Addr, BaseOpts)};
+ Fd ->
+ {0, sock_opts(Addr, [{fd, Fd} | BaseOpts])}
end.
-get_fd(Port, undefined = _Fd, IpFamilyDefault) ->
- FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)),
- case init:get_argument(FdKey) of
- {ok, [[Value]]} ->
- case string:tokens(Value, [$|]) of
- [FdStr, IpFamilyStr] ->
- {fd_of(FdStr), ip_family_of(IpFamilyStr)};
- [FdStr] ->
- {fd_of(FdStr), IpFamilyDefault};
- _ ->
- throw({error, {bad_descriptor, Value}})
- end;
- error ->
- undefined
- end;
-get_fd(_Port, Fd, IpFamilyDefault) ->
- {Fd, IpFamilyDefault}.
-
-
-fd_of(FdStr) ->
- case (catch list_to_integer(FdStr)) of
- Fd when is_integer(Fd) ->
- Fd;
- _ ->
- throw({error, {bad_descriptor, FdStr}})
- end.
-
-ip_family_of(IpFamilyStr) ->
- IpFamily = list_to_atom(IpFamilyStr),
- case lists:member(IpFamily, [inet, inet6, inet6fb4]) of
- true ->
- IpFamily;
- false ->
- throw({error, {bad_ipfamily, IpFamilyStr}})
- end.
-
-
%%-------------------------------------------------------------------------
%% accept(SocketType, ListenSocket) -> {ok, Socket} | {error, Reason}
%% accept(SocketType, ListenSocket, Timeout) -> ok | {error, Reason}
diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl
index 08ee9ee0d0..1bffcc1f12 100644
--- a/lib/inets/src/http_server/httpd_acceptor.erl
+++ b/lib/inets/src/http_server/httpd_acceptor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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,10 +24,10 @@
-include("inets_internal.hrl").
%% Internal application API
--export([start_link/5, start_link/6]).
+-export([start_link/6, start_link/7]).
%% Other exports (for spawn's etc.)
--export([acceptor_init/6, acceptor_init/7, acceptor_loop/5]).
+-export([acceptor_init/7, acceptor_init/8, acceptor_loop/6]).
%%
%% External API
@@ -35,27 +35,27 @@
%% start_link
-start_link(Manager, SocketType, Addr, Port, ConfigDb, AcceptTimeout) ->
+start_link(Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) ->
?hdrd("start link",
[{manager, Manager},
{socket_type, SocketType},
{address, Addr},
{port, Port},
{timeout, AcceptTimeout}]),
- Args = [self(), Manager, SocketType, Addr, Port, ConfigDb, AcceptTimeout],
+ Args = [self(), Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout],
proc_lib:start_link(?MODULE, acceptor_init, Args).
-start_link(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) ->
+start_link(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) ->
?hdrd("start link",
[{manager, Manager},
{socket_type, SocketType},
{listen_socket, ListenSocket},
{timeout, AcceptTimeout}]),
- Args = [self(), Manager, SocketType, ListenSocket,
+ Args = [self(), Manager, SocketType, ListenSocket, IpFamily,
ConfigDb, AcceptTimeout],
proc_lib:start_link(?MODULE, acceptor_init, Args).
-acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket},
+acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket}, IpFamily,
ConfigDb, AcceptTimeout) ->
?hdrd("acceptor init",
[{parent, Parent},
@@ -66,9 +66,9 @@ acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket},
{timeout, AcceptTimeout}]),
link(ListenOwner),
proc_lib:init_ack(Parent, {ok, self()}),
- acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout).
+ acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout).
-acceptor_init(Parent, Manager, SocketType, Addr, Port,
+acceptor_init(Parent, Manager, SocketType, Addr, Port, IpFamily,
ConfigDb, AcceptTimeout) ->
?hdrd("acceptor init",
[{parent, Parent},
@@ -77,20 +77,20 @@ acceptor_init(Parent, Manager, SocketType, Addr, Port,
{address, Addr},
{port, Port},
{timeout, AcceptTimeout}]),
- case (catch do_init(SocketType, Addr, Port)) of
+ case (catch do_init(SocketType, Addr, Port, IpFamily)) of
{ok, ListenSocket} ->
proc_lib:init_ack(Parent, {ok, self()}),
acceptor_loop(Manager, SocketType,
- ListenSocket, ConfigDb, AcceptTimeout);
+ ListenSocket, IpFamily,ConfigDb, AcceptTimeout);
Error ->
proc_lib:init_ack(Parent, Error),
error
end.
-do_init(SocketType, Addr, Port) ->
+do_init(SocketType, Addr, Port, IpFamily) ->
?hdrt("do init", []),
do_socket_start(SocketType),
- ListenSocket = do_socket_listen(SocketType, Addr, Port),
+ ListenSocket = do_socket_listen(SocketType, Addr, Port, IpFamily),
{ok, ListenSocket}.
@@ -105,9 +105,9 @@ do_socket_start(SocketType) ->
end.
-do_socket_listen(SocketType, Addr, Port) ->
+do_socket_listen(SocketType, Addr, Port, IpFamily) ->
?hdrt("do socket listen", []),
- case http_transport:listen(SocketType, Addr, Port) of
+ case http_transport:listen(SocketType, Addr, Port, IpFamily) of
{ok, ListenSocket} ->
ListenSocket;
{error, Reason} ->
@@ -121,7 +121,7 @@ do_socket_listen(SocketType, Addr, Port) ->
%% acceptor
-acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) ->
+acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) ->
?hdrd("awaiting accept",
[{manager, Manager},
{socket_type, SocketType},
@@ -133,12 +133,12 @@ acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) ->
handle_connection(Manager, ConfigDb, AcceptTimeout,
SocketType, Socket),
?MODULE:acceptor_loop(Manager, SocketType,
- ListenSocket, ConfigDb,AcceptTimeout);
+ ListenSocket, IpFamily, ConfigDb,AcceptTimeout);
{error, Reason} ->
?hdri("accept failed", [{reason, Reason}]),
handle_error(Reason, ConfigDb),
?MODULE:acceptor_loop(Manager, SocketType, ListenSocket,
- ConfigDb, AcceptTimeout);
+ IpFamily, ConfigDb, AcceptTimeout);
{'EXIT', Reason} ->
?hdri("accept exited", [{reason, Reason}]),
ReasonString =
diff --git a/lib/inets/src/http_server/httpd_acceptor_sup.erl b/lib/inets/src/http_server/httpd_acceptor_sup.erl
index 8b1e4b6c4f..df837b5a24 100644
--- a/lib/inets/src/http_server/httpd_acceptor_sup.erl
+++ b/lib/inets/src/http_server/httpd_acceptor_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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 +27,7 @@
-behaviour(supervisor).
%% API
--export([start_link/2, start_acceptor/5, start_acceptor/6, stop_acceptor/2]).
+-export([start_link/2, start_acceptor/6, start_acceptor/7, stop_acceptor/2]).
%% Supervisor callback
-export([init/1]).
@@ -43,11 +43,11 @@ start_link(Addr, Port) ->
%% Function: [start|stop]_acceptor/5
%% Description: Starts/stops an [auth | security] worker (child) process
%%----------------------------------------------------------------------
-start_acceptor(SocketType, Addr, Port, ConfigDb, AcceptTimeout) ->
- start_worker(httpd_acceptor, SocketType, Addr, Port,
+start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) ->
+ start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily,
ConfigDb, AcceptTimeout, self(), []).
-start_acceptor(SocketType, Addr, Port, ConfigDb, AcceptTimeout, ListenSocket) ->
- start_worker(httpd_acceptor, SocketType, Addr, Port,
+start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout, ListenSocket) ->
+ start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily,
ConfigDb, AcceptTimeout, ListenSocket, self(), []).
@@ -69,18 +69,18 @@ init(_) ->
make_name(Addr,Port) ->
httpd_util:make_name("httpd_acc_sup", Addr, Port).
-start_worker(M, SocketType, Addr, Port, ConfigDB, AcceptTimeout, Manager, Modules) ->
+start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, Manager, Modules) ->
SupName = make_name(Addr, Port),
- Args = [Manager, SocketType, Addr, Port, ConfigDB, AcceptTimeout],
+ Args = [Manager, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout],
Spec = {{M, Addr, Port},
{M, start_link, Args},
permanent, timer:seconds(1), worker, [M] ++ Modules},
supervisor:start_child(SupName, Spec).
-start_worker(M, SocketType, Addr, Port, ConfigDB, AcceptTimeout, ListenSocket,
+start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, ListenSocket,
Manager, Modules) ->
SupName = make_name(Addr, Port),
- Args = [Manager, SocketType, ListenSocket, ConfigDB, AcceptTimeout],
+ Args = [Manager, SocketType, ListenSocket, IpFamily, ConfigDB, AcceptTimeout],
Spec = {{M, Addr, Port},
{M, start_link, Args},
permanent, timer:seconds(1), worker, [M] ++ Modules},
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 884e3defb8..d45f3c0048 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -582,11 +582,17 @@ validate_config_params([{server_tokens, {private, Value}} | Rest])
validate_config_params([{server_tokens, Value} | _]) ->
throw({server_tokens, Value});
+validate_config_params([{socket_type, ip_comm} | Rest]) ->
+ validate_config_params(Rest);
+
validate_config_params([{socket_type, Value} | Rest])
- when (Value =:= ip_comm) orelse
- (Value =:= ssl) orelse
- (Value =:= essl) ->
+ when Value == ssl; Value == essl ->
validate_config_params(Rest);
+
+validate_config_params([{socket_type, {Value, _}} | Rest])
+ when Value == essl orelse Value == ssl ->
+ validate_config_params(Rest);
+
validate_config_params([{socket_type, Value} | _]) ->
throw({socket_type, Value});
@@ -840,9 +846,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() ->
@@ -918,6 +922,8 @@ lookup_socket_type(ConfigDB) ->
case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
ip_comm ->
ip_comm;
+ {Tag, Conf} ->
+ {Tag, Conf};
SSL when (SSL =:= ssl) orelse (SSL =:= essl) ->
SSLTag =
if
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
index b44bc77c41..672a70a394 100644
--- a/lib/inets/src/http_server/httpd_manager.erl
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -264,11 +264,12 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo]) ->
end.
do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) ->
+ IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4),
NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
ConfigDB = do_initial_store(ConfigList),
SocketType = httpd_conf:lookup_socket_type(ConfigDB),
case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
- Port, ConfigDB, AcceptTimeout) of
+ Port, IpFamily, ConfigDB, AcceptTimeout) of
{ok, _Pid} ->
Status = [{max_conn, 0},
{last_heavy_load, never},
@@ -284,11 +285,12 @@ do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) ->
end.
do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) ->
+ IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4),
NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
ConfigDB = do_initial_store(ConfigList),
SocketType = httpd_conf:lookup_socket_type(ConfigDB),
case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
- Port, ConfigDB,
+ Port, IpFamily, ConfigDB,
AcceptTimeout, ListenInfo) of
{ok, _Pid} ->
Status = [{max_conn,0}, {last_heavy_load,never},
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 2dedb088e4..6b6532266b 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -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
@@ -35,8 +35,7 @@ generate_and_send_response(#mod{init_data =
#init_data{peername = {_,"unknown"}}}) ->
ok;
generate_and_send_response(#mod{config_db = ConfigDB} = ModData) ->
- Modules = httpd_util:lookup(ConfigDB,modules,
- [mod_get, mod_head, mod_log]),
+ Modules = httpd_util:lookup(ConfigDB,modules, ?DEFAULT_MODS),
case traverse_modules(ModData, Modules) of
done ->
ok;
@@ -71,7 +70,6 @@ traverse_modules(ModData,[Module|Rest]) ->
?hdrd("traverse modules", [{callback_module, Module}]),
case (catch apply(Module, do, [ModData])) of
{'EXIT', Reason} ->
- ?hdrd("traverse modules - exit", [{reason, Reason}]),
String =
lists:flatten(
io_lib:format("traverse exit from apply: ~p:do => ~n~p",
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index 8f3e8f9500..3b1e16cf78 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -196,7 +196,8 @@ httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
end.
httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) ->
- case (Port =:= 0) orelse proplists:is_defined(fd, Config) of
+ Fd = proplists:get_value(fd, Config, undefined),
+ case Port == 0 orelse Fd =/= undefined of
true ->
httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port);
false ->
@@ -242,21 +243,27 @@ error_msg(F, A) ->
error_logger:error_msg(F ++ "~n", A).
listen(Address, Port, Config) ->
- SocketType = proplists:get_value(socket_type, Config, ip_comm),
- case http_transport:start(SocketType) of
- ok ->
- Fd = proplists:get_value(fd, Config),
- case http_transport:listen(SocketType, Address, Port, Fd) of
- {ok, ListenSocket} ->
- NewConfig = proplists:delete(port, Config),
- {ok, NewPort} = inet:port(ListenSocket),
- {NewPort, [{port, NewPort} | NewConfig], ListenSocket};
+ try socket_type(Config) of
+ SocketType ->
+ case http_transport:start(SocketType) of
+ ok ->
+ Fd = proplists:get_value(fd, Config),
+ IpFamily = proplists:get_value(ipfamily, Config, inet6fb4),
+ case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of
+ {ok, ListenSocket} ->
+ NewConfig = proplists:delete(port, Config),
+ {NewPort, _} = http_transport:sockname(SocketType, ListenSocket),
+ {NewPort, [{port, NewPort} | NewConfig], ListenSocket};
+ {error, Reason} ->
+ {error, {listen, Reason}}
+ end;
{error, Reason} ->
- {error, {listen, Reason}}
- end;
- {error, Reason} ->
+ {error, {socket_start_failed, Reason}}
+ end
+ catch
+ _:Reason ->
{error, {socket_start_failed, Reason}}
- end.
+ end.
start_listen(Address, Port, Config) ->
Pid = listen_owner(Address, Port, Config),
@@ -280,7 +287,82 @@ listen_loop() ->
ok
end.
+socket_type(Config) ->
+ SocketType = proplists:get_value(socket_type, Config, ip_comm),
+ socket_type(SocketType, Config).
+
+socket_type(ip_comm = SocketType, _) ->
+ SocketType;
+socket_type({essl, _} = SocketType, _) ->
+ SocketType;
+socket_type(_, Config) ->
+ {essl, ssl_config(Config)}.
+
+%%% Backwards compatibility
+ssl_config(Config) ->
+ ssl_certificate_key_file(Config) ++
+ ssl_verify_client(Config) ++
+ ssl_ciphers(Config) ++
+ ssl_password(Config) ++
+ ssl_verify_depth(Config) ++
+ ssl_ca_certificate_file(Config).
+
+ssl_certificate_key_file(Config) ->
+ case proplists:get_value(ssl_certificate_key_file, Config) of
+ undefined ->
+ [];
+ SSLCertificateKeyFile ->
+ [{keyfile,SSLCertificateKeyFile}]
+ end.
+ssl_verify_client(Config) ->
+ case proplists:get_value(ssl_verify_client, Config) of
+ undefined ->
+ [];
+ SSLVerifyClient ->
+ [{verify,SSLVerifyClient}]
+ end.
+ssl_ciphers(Config) ->
+ case proplists:get_value(ssl_ciphers, Config) of
+ undefined ->
+ [];
+ Ciphers ->
+ [{ciphers, Ciphers}]
+ end.
+ssl_password(Config) ->
+ case proplists:get_value(ssl_password_callback_module, Config) of
+ undefined ->
+ [];
+ Module ->
+ case proplists:get_value(ssl_password_callback_function, Config) of
+ undefined ->
+ [];
+ Function ->
+ Args = case proplists:get_value(ssl_password_callback_arguments, Config) of
+ undefined ->
+ [];
+ Arguments ->
+ [Arguments]
+ end,
+ Password = apply(Module, Function, Args),
+ [{password, Password}]
+ end
+ end.
+ssl_verify_depth(Config) ->
+ case proplists:get_value(ssl_verify_client_depth, Config) of
+ undefined ->
+ [];
+ Depth ->
+ [{depth, Depth}]
+ end.
+
+ssl_ca_certificate_file(Config) ->
+ case proplists:get_value(ssl_ca_certificate_file, Config) of
+ undefined ->
+ [];
+ File ->
+ [{cacertfile, File}]
+ end.
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index 7d68145287..22426eee79 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -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 ffd0ed622f..c63dcafa6c 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,7 +1,7 @@
%% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-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
@@ -18,109 +18,8 @@
{"%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]},
-
- {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
- {update, inets_sup, soft, 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, inets, soft_purge, soft_purge, [inets_trace]},
- {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",
- [
- {restart_application, inets}
- ]
- },
- {"5.7.2",
- [
- {restart_application, inets}
- ]
- }
- ],
+ {<<"5\\.*">>, [{restart_application, inets}]}
+ ],
[
- {"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]},
-
- {load_module, inets_app, soft_purge, soft_purge, [inets_sup]},
- {update, inets_sup, soft, 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, inets, 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",
- [
- {restart_application, inets}
- ]
- },
- {"5.7.2",
- [
- {restart_application, inets}
- ]
- }
- ]
-}.
+ {<<"5\\.*">>, [{restart_application, inets}]}
+]}.
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/test/Makefile b/lib/inets/test/Makefile
index 0fc98eff6f..dfa86906fd 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -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 \
@@ -182,7 +184,8 @@ MODULES = \
inets_app_test \
inets_appup_test \
tftp_test_lib \
- tftp_SUITE
+ tftp_SUITE \
+ uri_SUITE
EBIN = .
@@ -213,7 +216,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..5b92e551a5
--- /dev/null
+++ b/lib/inets/test/erl_make_certs.erl
@@ -0,0 +1,429 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-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%
+%%
+
+%% 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_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl
index cbc1b04bbb..16e5cdb4bc 100644
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ b/lib/inets/test/ftp_format_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -273,7 +273,7 @@ ftp_other_status_codes(Config) when is_list(Config) ->
%% 4XX
{trans_neg_compl, _ } = ftp_response:interpret("421 Foobar\r\n"),
{trans_neg_compl, _ } = ftp_response:interpret("426 Foobar\r\n"),
- {trans_neg_compl, _ } = ftp_response:interpret("450 Foobar\r\n"),
+ {enofile, _ } = ftp_response:interpret("450 Foobar\r\n"),
{trans_neg_compl, _ } = ftp_response:interpret("451 Foobar\r\n"),
{etnospc, _ } = ftp_response:interpret("452 Foobar\r\n"),
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
index ffb58c91b6..35f21cc74d 100644
--- a/lib/inets/test/ftp_suite_lib.erl
+++ b/lib/inets/test/ftp_suite_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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 1cdd96f0b0..8df5964193 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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,3057 +19,919 @@
%%
%%
-%% ts:run(inets, httpc_SUITE, [batch]).
-%%
+%% ct:run("../inets_test", httpc_SUITE).
+%%
-module(httpc_SUITE).
--include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-
-include_lib("kernel/include/file.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
-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(URL_START, "http://").
+-define(TLS_URL_START, "https://").
-define(NOT_IN_USE_PORT, 8997).
--define(LOCAL_HOST, {127,0,0,1}).
--define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1").
--define(URL_START, "http://localhost:").
--define(SSL_URL_START, "https://localhost:").
--define(CR, $\r).
-define(LF, $\n).
-define(HTTP_MAX_HEADER_SIZE, 10240).
-
-
+-record(sslsocket, {fd = nil, pid = nil}).
%%--------------------------------------------------------------------
-%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
-%% Arg - doc | suite
-%% Doc - string()
-%% Case - atom()
-%% Name of a test case function.
-%% Comment - string()
-%% Description: Returns documentation/test cases in this test suite
-%% or a skip tuple if the platform is not supported.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
+all() ->
[
- http_options,
- http_head,
- http_get,
- http_post,
- http_post_streaming,
- http_dummy_pipe,
- http_inets_pipe,
- http_trace,
- http_async,
- http_save_to_file,
- http_save_to_file_async,
- http_headers,
- http_headers_dummy,
- http_bad_response,
- http_redirect,
- http_redirect_loop,
- http_internal_server_error,
- http_userinfo, http_cookie,
- http_server_does_not_exist,
- http_invalid_http,
- http_emulate_lower_versions,
- http_relaxed,
- page_does_not_exist,
- parse_url,
- options,
- headers_as_is,
- selecting_session,
- {group, proxy},
- {group, ssl},
- {group, stream},
- {group, ipv6},
- {group, tickets},
- initial_server_connect
+ {group, http},
+ {group, sim_http},
+ {group, https},
+ {group, sim_https},
+ {group, misc}
].
-groups() ->
+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,
- essl_get,
- ssl_trace,
- essl_trace]},
- {stream, [], [http_stream,
- http_stream_once,
- proxy_stream]},
- {tickets, [], [hexed_query_otp_6191,
- empty_body_otp_6243,
- empty_response_header_otp_6830,
- transfer_encoding_otp_6807,
- proxy_not_modified_otp_6821,
- no_content_204_otp_6982,
- missing_CR_otp_7304,
- {group, otp_7883},
- {group, otp_8154},
- {group, otp_8106},
- otp_8056,
- otp_8352,
- otp_8371,
- otp_8739]},
- {otp_7883, [], [otp_7883_1,
- otp_7883_2]},
- {otp_8154, [], [otp_8154_1]},
- {otp_8106, [], [otp_8106_pid,
- otp_8106_fun,
- otp_8106_mfa]},
- {ipv6, [], [ipv6_ipcomm, ipv6_essl]}
+ {http, [], real_requests()},
+ {sim_http, [], only_simulated()},
+ {https, [], real_requests()},
+ {sim_https, [], only_simulated()},
+ {misc, [], misc()}
].
+real_requests()->
+ [
+ head,
+ get,
+ post,
+ post_stream,
+ async,
+ pipeline,
+ persistent_connection,
+ save_to_file,
+ save_to_file_async,
+ headers_as_is,
+ page_does_not_exist,
+ emulate_lower_versions,
+ headers,
+ headers_as_is,
+ empty_body,
+ stream,
+ stream_to_pid,
+ stream_through_fun,
+ stream_through_mfa,
+ streaming_error,
+ inet_opts
+ ].
-init_per_group(ipv6 = _GroupName, Config) ->
- case inets_test_lib:has_ipv6_support() of
- {ok, _} ->
- Config;
- _ ->
- {skip, "Host does not support IPv6"}
- end;
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
+only_simulated() ->
+ [
+ cookie,
+ trace,
+ stream_once,
+ no_content_204,
+ tolerate_missing_CR,
+ userinfo,
+ bad_response,
+ internal_server_error,
+ invalid_http,
+ headers_dummy,
+ empty_response_header,
+ remote_socket_close,
+ remote_socket_close_async,
+ transfer_encoding,
+ redirect_loop,
+ redirect_moved_permanently,
+ redirect_multiple_choises,
+ redirect_found,
+ redirect_see_other,
+ redirect_temporary_redirect,
+ port_in_host_header,
+ relaxed
+ ].
+misc() ->
+ [
+ server_does_not_exist,
+ timeout_memory_leak,
+ wait_for_whole_response
+ ].
%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
-
- ?PRINT_SYSTEM_INFO([]),
+init_per_suite(Config) ->
PrivDir = ?config(priv_dir, Config),
DataDir = ?config(data_dir, Config),
+ inets_test_lib:start_apps([inets]),
ServerRoot = filename:join(PrivDir, "server_root"),
DocRoot = filename:join(ServerRoot, "htdocs"),
- IpConfFile = integer_to_list(?IP_PORT) ++ ".conf",
- SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf",
-
setup_server_dirs(ServerRoot, DocRoot, DataDir),
- create_config(IpConfFile, ip_comm, ?IP_PORT, PrivDir, ServerRoot,
- DocRoot, DataDir),
- create_config(SslConfFile, ssl, ?SSL_PORT, PrivDir, ServerRoot,
- DocRoot, DataDir),
+ [{server_root, ServerRoot}, {doc_root, DocRoot} | Config].
- Cgi = case test_server:os_type() of
- {win32, _} ->
- filename:join([ServerRoot, "cgi-bin", "cgi_echo.exe"]);
- _ ->
- filename:join([ServerRoot, "cgi-bin", "cgi_echo"])
- end,
-
- {ok, FileInfo} = file:read_file_info(Cgi),
- ok = file:write_file_info(Cgi, FileInfo#file_info{mode = 8#00755}),
-
- [{has_ipv6_support, inets_test_lib:has_ipv6_support()},
- {server_root, ServerRoot},
- {doc_root, DocRoot},
- {local_port, ?IP_PORT},
- {local_ssl_port, ?SSL_PORT} | Config].
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
end_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ inets_test_lib:stop_apps([inets]),
+ PrivDir = ?config(priv_dir, Config),
inets_test_lib:del_dirs(PrivDir),
- application:stop(inets),
- application:stop(ssl),
ok.
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(Case, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
+init_per_group(misc = Group, Config) ->
+ start_apps(Group),
+ Inet = inet_version(),
+ ok = httpc:set_options([{ipfamily, Inet}]),
+ Config;
+
+init_per_group(Group, Config0) ->
+ start_apps(Group),
+ Config = proplists:delete(port, Config0),
+ Port = server_start(Group, server_config(Group, Config)),
+ [{port, Port} | Config].
+
+end_per_group(_, _Config) ->
+ ok.
-init_per_testcase(otp_8154_1 = Case, Config) ->
- init_per_testcase(Case, 5, Config);
-
-init_per_testcase(initial_server_connect = Case, Config) ->
- %% Try to check if crypto actually exist or not,
- %% this test case does not work unless it does
- try
- begin
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- inets:start(),
- Config
- end
- catch
- throw:{error, {failed_starting, App, ActualError}} ->
- tsp("init_per_testcase(~w) -> failed starting ~w: "
- "~n ~p", [Case, App, ActualError]),
- 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;
+%%--------------------------------------------------------------------
+init_per_testcase(pipeline, Config) ->
+ inets:start(httpc, [{profile, pipeline}]),
+ httpc:set_options([{pipeline_timeout, 50000},
+ {max_pipeline_length, 3}], pipeline),
-init_per_testcase(Case, Config) ->
- init_per_testcase(Case, 2, Config).
+ Config;
+init_per_testcase(persistent_connection, Config) ->
+ inets:start(httpc, [{profile, persistent}]),
+ httpc:set_options([{keep_alive_timeout, 50000},
+ {max_keep_alive_length, 3}], persistent_connection),
-init_per_testcase(Case, Timeout, Config) ->
- io:format(user,
- "~n~n*** INIT ~w:~w[~w] ***"
- "~n~n", [?MODULE, Case, Timeout]),
+ Config;
- PrivDir = ?config(priv_dir, Config),
- application:stop(inets),
- Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)),
- TmpConfig = lists:keydelete(watchdog, 1, Config),
- IpConfFile = integer_to_list(?IP_PORT) ++ ".conf",
- SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf",
-
- %% inets:enable_trace(max, io, httpd),
- %% inets:enable_trace(max, io, httpc),
- %% inets:enable_trace(max, io, all),
-
- NewConfig =
- case atom_to_list(Case) of
- [$s, $s, $l | _] ->
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- init_per_testcase_ssl(ssl, PrivDir, SslConfFile,
- [{watchdog, Dog} | TmpConfig]);
-
- [$e, $s, $s, $l | _] ->
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- 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;
+init_per_testcase(_Case, Config) ->
+ Config.
- _ ->
- %% 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
- ok ->
- Profile = ipv6,
- %% A stand-alone profile is represented by a pid()
- {ok, ProfilePid} =
- inets:start(httpc,
- [{profile, Profile},
- {data_dir, PrivDir}], stand_alone),
- ok = httpc:set_options([{ipfamily, inet6}],
- ProfilePid),
- tsp("httpc profile pid: ~p", [ProfilePid]),
- [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig]
- catch
- throw:{error, {failed_starting, App, ActualError}} ->
- tsp("init_per_testcase(~w) -> failed starting ~w: "
- "~n ~p", [Case, App, ActualError]),
- 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;
-
- _ ->
- %% Try inet6fb4 on windows...
- %% No need? Since it is set above?
-
- %% tsp("init_per_testcase -> allways try IPv6 on windows"),
- %% ?RUN_ON_WINDOWS(
- %% fun() ->
- %% tsp("init_per_testcase:set_options_fun -> "
- %% "set-option ipfamily to inet6fb4"),
- %% Res = httpc:set_options([{ipfamily, inet6fb4}]),
- %% tsp("init_per_testcase:set_options_fun -> "
- %% "~n Res: ~p", [Res]),
- %% Res
- %% end),
-
- TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig),
- %% Will start inets
- tsp("init_per_testcase -> try start server"),
- Server = start_http_server(PrivDir, IpConfFile),
- [{watchdog, Dog}, {local_server, Server} | TmpConfig2]
- end,
-
- %% <IPv6>
- %% Set default ipfamily to the same as the main server has by default
- %% This makes the client try w/ ipv6 before falling back to ipv4,
- %% as that is what the server is configured to do.
- %% Note that this is required for the tests to run on *BSD w/ ipv6 enabled
- %% as well as on Windows. The Linux behaviour of allowing ipv4 connects
- %% to ipv6 sockets is not required or even encouraged.
-
- tsp("init_per_testcase -> Options before ipfamily set: ~n~p",
- [httpc:get_options(all)]),
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- tsp("init_per_testcase -> Options after ipfamily set: ~n~p",
- [httpc:get_options(all)]),
-
- %% Note that the IPv6 test case(s) *must* use inet6,
- %% 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"
- "~n NewConfig: ~p"
- "~n~n", [Case, NewConfig]),
- NewConfig.
-
-
-init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) ->
- tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]),
- application:stop(ssl),
- Config2 = lists:keydelete(local_ssl_server, 1, Config),
- %% Will start inets
- tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)",
- [Tag]),
- Server = inets_test_lib:start_http_server(
- filename:join(PrivDir, SslConfFile), Tag),
- tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]),
- [{local_ssl_server, Server} | Config2].
-
-start_http_server(ConfDir, ConfFile) ->
- inets_test_lib:start_http_server( filename:join(ConfDir, ConfFile) ).
+end_per_testcase(pipeline, _Config) ->
+ inets:stop(httpc, pipeline);
+end_per_testcase(persistent_connection, _Config) ->
+ inets:stop(httpc, persistent);
+end_per_testcase(_Case, _Config) ->
+ ok.
%%--------------------------------------------------------------------
-%% Function: end_per_testcase(Case, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-end_per_testcase(http_save_to_file = Case, Config) ->
- io:format(user, "~n~n*** END ~w:~w ***~n~n",
- [?MODULE, Case]),
- PrivDir = ?config(priv_dir, Config),
- FullPath = filename:join(PrivDir, "dummy.html"),
- file:delete(FullPath),
- finish(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]),
- application:stop(ssl),
- tsp("end_per_testcase(~w) -> stop public_key", [Case]),
- application:stop(public_key),
- tsp("end_per_testcase(~w) -> stop crypto", [Case]),
- application:stop(crypto),
- ProfilePid = ?config(profile, Config),
- tsp("end_per_testcase(~w) -> stop httpc profile (~p)",
- [Case, ProfilePid]),
- unlink(ProfilePid),
- inets:stop(stand_alone, ProfilePid),
- tsp("end_per_testcase(~w) -> httpc profile (~p) stopped",
- [Case, ProfilePid]),
- ok;
- _ ->
- ok
- end,
- finish(Config).
-finish(Config) ->
- Dog = ?config(watchdog, Config),
- case Dog of
- undefined ->
- ok;
- _ ->
- tsp("finish -> stop watchdog (~p)", [Dog]),
- test_server:timetrap_cancel(Dog)
- end.
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
+head() ->
+ [{doc, "Test http head request against local server."}].
+head(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ {ok, {{_,200,_}, [_ | _], []}} = httpc:request(head, Request, [], []).
+%%--------------------------------------------------------------------
+get() ->
+ [{doc, "Test http get request against local server"}].
+get(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} = httpc:request(get, Request, [], []),
+ inets_test_lib:check_body(Body),
-%%-------------------------------------------------------------------------
+ {ok, {{_,200,_}, [_ | _], BinBody}} = httpc:request(get, Request, [], [{body_format, binary}]),
+ true = is_binary(BinBody).
+%%--------------------------------------------------------------------
+post() ->
+ [{"Test http post request against local server. We do in this case "
+ "only care about the client side of the the post. The server "
+ "script will not actually use the post data."}].
+post(Config) when is_list(Config) ->
+ CGI = case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/cgi_echo.exe";
+ _ ->
+ "/cgi-bin/cgi_echo"
+ end,
-http_options(doc) ->
- ["Test http options request against local server."];
-http_options(suite) ->
- [];
-http_options(Config) when is_list(Config) ->
- skip("Not supported by httpd").
+ URL = url(group_name(Config), CGI, Config),
-http_head(doc) ->
- ["Test http head request against local server."];
-http_head(suite) ->
- [];
-http_head(Config) when is_list(Config) ->
- tsp("http_head -> entry with"
- "~n Config: ~p", [Config]),
- Method = head,
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- Request = {URL, []},
- HttpOpts = [],
- Opts = [],
- VerifyResult =
- fun({ok, {{_,200,_}, [_ | _], []}}) ->
- ok;
- ({ok, UnexpectedReply}) ->
- tsp("http_head:verify_fun -> Unexpected Reply: "
- "~n ~p", [UnexpectedReply]),
- tsf({unexpected_reply, UnexpectedReply});
- ({error, Reason} = Error) ->
- tsp("http_head:verify_fun -> Error reply: "
- "~n Reason: ~p", [Reason]),
- tsf({bad_reply, Error})
- end,
- simple_request_and_verify(Config,
- Method, Request, HttpOpts, Opts, VerifyResult).
+ %% Cgi-script expects the body length to be 100
+ Body = lists:duplicate(100, "1"),
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(post, {URL, [{"expect","100-continue"}],
+ "text/plain", Body}, [], []),
-%%-------------------------------------------------------------------------
+ {ok, {{_,504,_}, [_ | _], []}} =
+ httpc:request(post, {URL, [{"expect","100-continue"}],
+ "text/plain", "foobar"}, [], []).
-http_get(doc) ->
- ["Test http get request against local server"];
-http_get(suite) ->
- [];
-http_get(Config) when is_list(Config) ->
- tsp("http_get -> entry with"
- "~n Config: ~p", [Config]),
- case ?config(local_server, Config) of
- ok ->
- tsp("local-server running"),
- Method = get,
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- Request = {URL, []},
- Timeout = timer:seconds(1),
- ConnTimeout = Timeout + timer:seconds(1),
- HttpOptions1 = [{timeout, Timeout},
- {connect_timeout, ConnTimeout}],
- Options1 = [],
- Body =
- case httpc:request(Method, Request, HttpOptions1, Options1) of
- {ok, {{_,200,_}, [_ | _], ReplyBody = [_ | _]}} ->
- ReplyBody;
- {ok, UnexpectedReply1} ->
- tsf({unexpected_reply, UnexpectedReply1});
- {error, _} = Error1 ->
- tsf({bad_reply, Error1})
- end,
-
- %% eqvivivalent to httpc:request(get, {URL, []}, [], []),
- inets_test_lib:check_body(Body),
-
- HttpOptions2 = [],
- Options2 = [{body_format, binary}],
- case httpc:request(Method, Request, HttpOptions2, Options2) of
- {ok, {{_,200,_}, [_ | _], Bin}} when is_binary(Bin) ->
- ok;
- {ok, {{_,200,_}, [_ | _], BadBin}} ->
- tsf({body_format_not_binary, BadBin});
- {ok, UnexpectedReply2} ->
- tsf({unexpected_reply, UnexpectedReply2});
- {error, _} = Error2 ->
- tsf({bad_reply, Error2})
- end;
- _ ->
- skip("Failed to start local http-server")
- end.
+%%--------------------------------------------------------------------
+post_stream() ->
+ [{"Test streaming http post request against local server. "
+ "We only care about the client side of the the post. "
+ "The server script will not actually use the post data."}].
+post_stream(Config) when is_list(Config) ->
+ CGI = case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/cgi_echo.exe";
+ _ ->
+ "/cgi-bin/cgi_echo"
+ end,
-%%-------------------------------------------------------------------------
+ URL = url(group_name(Config), CGI, Config),
-http_post(doc) ->
- ["Test http post request against local server. We do in this case "
- "only care about the client side of the the post. The server "
- "script will not actually use the post data."];
-http_post(suite) ->
- [];
-http_post(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
-
- URL = case test_server:os_type() of
- {win32, _} ->
- ?URL_START ++ integer_to_list(Port) ++
- "/cgi-bin/cgi_echo.exe";
- _ ->
- ?URL_START ++ integer_to_list(Port) ++
- "/cgi-bin/cgi_echo"
-
- end,
- %% Cgi-script expects the body length to be 100
- Body = lists:duplicate(100, "1"),
-
- {ok, {{_,200,_}, [_ | _], [_ | _]}} =
- httpc:request(post, {URL, [{"expect","100-continue"}],
- "text/plain", Body}, [], []),
-
- {ok, {{_,504,_}, [_ | _], []}} =
- httpc:request(post, {URL, [{"expect","100-continue"}],
- "text/plain", "foobar"}, [], []);
- _ ->
- skip("Failed to start local http-server")
- end.
-
-%%-------------------------------------------------------------------------
-http_post_streaming(doc) ->
- ["Test streaming http post request against local server. "
- "We only care about the client side of the the post. "
- "The server script will not actually use the post data."];
-http_post_streaming(suite) ->
- [];
-http_post_streaming(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = case test_server:os_type() of
- {win32, _} ->
- ?URL_START ++ integer_to_list(Port) ++
- "/cgi-bin/cgi_echo.exe";
- _ ->
- ?URL_START ++ integer_to_list(Port) ++
- "/cgi-bin/cgi_echo"
- end,
- %% Cgi-script expects the body length to be 100
- BodyFun = fun(0) ->
- io:format("~w:http_post_streaming_fun -> "
- "zero~n", [?MODULE]),
- eof;
- (LenLeft) ->
- io:format("~w:http_post_streaming_fun -> "
- "LenLeft: ~p~n", [?MODULE, LenLeft]),
- {ok, lists:duplicate(10, "1"), LenLeft - 10}
- end,
-
- {ok, {{_,200,_}, [_ | _], [_ | _]}} =
- httpc:request(post, {URL,
- [{"expect", "100-continue"},
- {"content-length", "100"}],
- "text/plain", {BodyFun, 100}}, [], []),
-
- {ok, {{_,504,_}, [_ | _], []}} =
- httpc:request(post, {URL,
- [{"expect", "100-continue"},
- {"content-length", "10"}],
- "text/plain", {BodyFun, 10}}, [], []);
-
- _ ->
- skip("Failed to start local http-server")
- end.
+ %% Cgi-script expects the body length to be 100
+ BodyFun = fun(0) ->
+ eof;
+ (LenLeft) ->
+ {ok, lists:duplicate(10, "1"), LenLeft - 10}
+ end,
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(post, {URL,
+ [{"expect", "100-continue"},
+ {"content-length", "100"}],
+ "text/plain", {BodyFun, 100}}, [], []),
-%%-------------------------------------------------------------------------
-http_emulate_lower_versions(doc) ->
- ["Perform request as 0.9 and 1.0 clients."];
-http_emulate_lower_versions(suite) ->
- [];
-http_emulate_lower_versions(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, Body0} =
- httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []),
- inets_test_lib:check_body(Body0),
- {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} =
- httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []),
- inets_test_lib:check_body(Body1),
- {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} =
- httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []),
- inets_test_lib:check_body(Body2);
- _->
- skip("Failed to start local http-server")
- end.
+ {ok, {{_,504,_}, [_ | _], []}} =
+ httpc:request(post, {URL,
+ [{"expect", "100-continue"},
+ {"content-length", "10"}],
+ "text/plain", {BodyFun, 10}}, [], []).
+%%--------------------------------------------------------------------
+trace() ->
+ [{doc, "Perform a TRACE request."}].
+trace(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/trace.html", Config), []},
+ case httpc:request(trace, Request, [], []) of
+ {ok, {{_,200,_}, [_ | _], "TRACE /trace.html" ++ _}} ->
+ ok;
+ Other ->
+ ct:fail({unexpected, Other})
+ end.
-%%-------------------------------------------------------------------------
+%%--------------------------------------------------------------------
-http_relaxed(doc) ->
- ["Test relaxed mode"];
-http_relaxed(suite) ->
- [];
-http_relaxed(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
- %% ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
+pipeline(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ {ok, _} = httpc:request(get, Request, [], [], pipeline),
- URL = ?URL_START ++ integer_to_list(Port) ++
- "/missing_reason_phrase.html",
-
- {error, Reason} =
- httpc:request(get, {URL, []}, [{relaxed, false}], []),
+ %% Make sure pipeline session is registerd
+ test_server:sleep(4000),
+ keep_alive_requests(Request, pipeline).
- test_server:format("Not relaxed: ~p~n", [Reason]),
-
- {ok, {{_, 200, _}, [_ | _], [_ | _]}} =
- httpc:request(get, {URL, []}, [{relaxed, true}], []),
+%%--------------------------------------------------------------------
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipv6, enabled}]),
- %% ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+persistent_connection(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ {ok, _} = httpc:request(get, Request, [], [], persistent),
+ %% Make sure pipeline session is registerd
+ test_server:sleep(4000),
+ keep_alive_requests(Request, persistent).
%%-------------------------------------------------------------------------
-http_dummy_pipe(doc) ->
- ["Test pipelining code."];
-http_dummy_pipe(suite) ->
- [];
-http_dummy_pipe(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html",
-
- test_pipeline(URL),
-
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
-
-http_inets_pipe(doc) ->
- ["Test pipelining code."];
-http_inets_pipe(suite) ->
- [];
-http_inets_pipe(Config) when is_list(Config) ->
-
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- test_pipeline(URL);
- _ ->
- skip("Failed to start local http-server")
- end.
+async() ->
+ [{doc, "Test an asynchrony http request."}].
+async(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ {ok, RequestId} =
+ httpc:request(get, Request, [], [{sync, false}]),
+ Body =
+ receive
+ {http, {RequestId, {{_, 200, _}, _, BinBody}}} ->
+ BinBody;
+ {http, Msg} ->
+ ct:fail(Msg)
+ end,
+ inets_test_lib:check_body(binary_to_list(Body)),
-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"
- "~n when profile info: ~p", [httpc:info()]),
- {ok, RequestIdA1} =
- httpc:request(get, {URL, []}, [], [{sync, false}]),
- 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}]),
- {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 -> 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}]),
- {ok, RequestIdB2} =
- httpc:request(get, {URL, []}, [], [{sync, false}]),
- {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()]),
+ {ok, NewRequestId} =
+ httpc:request(get, Request, [], [{sync, false}]),
+ ok = httpc:cancel_request(NewRequestId),
receive
- {http, {RequestIdB2, _}} ->
- tsf(http_cancel_request_failed)
+ {http, {NewRequestId, _}} ->
+ ct:fail(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 B1"
- "~n when profile info: ~p", [httpc:info()]),
- inets_test_lib:check_body(binary_to_list(Body)),
-
- 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"
- "~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]),
+ end.
+%%-------------------------------------------------------------------------
+save_to_file() ->
+ [{doc, "Test to save the http body to a file"}].
+save_to_file(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ FilePath = filename:join(PrivDir, "dummy.html"),
+ URL = url(group_name(Config), "/dummy.html", Config),
+ Request = {URL, []},
+ {ok, saved_to_file}
+ = httpc:request(get, Request, [], [{stream, FilePath}]),
+ {ok, Bin} = file:read_file(FilePath),
+ {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL),
+ Bin == Body.
+
+%%-------------------------------------------------------------------------
+save_to_file_async() ->
+ [{doc,"Test to save the http body to a file"}].
+save_to_file_async(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ FilePath = filename:join(PrivDir, "dummy.html"),
+ URL = url(group_name(Config), "/dummy.html", Config),
+ Request = {URL, []},
+ {ok, RequestId} = httpc:request(get, Request, [],
+ [{stream, FilePath},
+ {sync, false}]),
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, {RequestId, saved_to_file}} ->
+ ok;
{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}).
-
+ ct:fail(Msg)
+ end,
-
-%%-------------------------------------------------------------------------
-http_trace(doc) ->
- ["Perform a TRACE request that goes through a proxy."];
-http_trace(suite) ->
- [];
-http_trace(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- case httpc:request(trace, {URL, []}, [], []) of
- {ok, {{_,200,_}, [_ | _], "TRACE /dummy.html" ++ _}} ->
- ok;
- {ok, {{_,200,_}, [_ | _], WrongBody}} ->
- tsf({wrong_body, WrongBody});
- {ok, WrongReply} ->
- tsf({wrong_reply, WrongReply});
- Error ->
- tsf({failed, Error})
- end;
- _ ->
- skip("Failed to start local http-server")
- end.
+ {ok, Bin} = file:read_file(FilePath),
+ {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL),
+ Bin == Body.
%%-------------------------------------------------------------------------
-http_async(doc) ->
- ["Test an asynchrony http request."];
-http_async(suite) ->
- [];
-http_async(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, RequestId} =
- httpc:request(get, {URL, []}, [], [{sync, false}]),
-
- Body =
- receive
- {http, {RequestId, {{_, 200, _}, _, BinBody}}} ->
- BinBody;
- {http, Msg} ->
- tsf(Msg)
- end,
-
- inets_test_lib:check_body(binary_to_list(Body)),
-
- {ok, NewRequestId} =
- httpc:request(get, {URL, []}, [], [{sync, false}]),
- ok = httpc:cancel_request(NewRequestId),
- receive
- {http, {NewRequestId, _NewResult}} ->
- tsf(http_cancel_request_failed)
- after 3000 ->
- ok
- end;
- _ ->
- skip("Failed to start local http-server")
- end.
-
+stream() ->
+ [{doc, "Test the option stream for asynchrony requests"}].
+stream(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ stream_test(Request, {stream, self}).
%%-------------------------------------------------------------------------
-http_save_to_file(doc) ->
- ["Test to save the http body to a file"];
-http_save_to_file(suite) ->
- [];
-http_save_to_file(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- PrivDir = ?config(priv_dir, Config),
- FilePath = filename:join(PrivDir, "dummy.html"),
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, saved_to_file}
- = httpc:request(get, {URL, []}, [], [{stream, FilePath}]),
- {ok, Bin} = file:read_file(FilePath),
- {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL),
- Bin == Body;
- _ ->
- skip("Failed to start local http-server")
- end.
+stream_once() ->
+ [{doc, "Test the option stream for asynchrony requests"}].
+stream_once(Config) when is_list(Config) ->
+ Request0 = {url(group_name(Config), "/dummy.html", Config), []},
+ stream_test(Request0, {stream, {self, once}}),
+ Request1 = {url(group_name(Config), "/once.html", Config), []},
+ stream_test(Request1, {stream, {self, once}}),
-%%-------------------------------------------------------------------------
-http_save_to_file_async(doc) ->
- ["Test to save the http body to a file"];
-http_save_to_file_async(suite) ->
- [];
-http_save_to_file_async(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- PrivDir = ?config(priv_dir, Config),
- FilePath = filename:join(PrivDir, "dummy.html"),
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, RequestId} = httpc:request(get, {URL, []}, [],
- [{stream, FilePath},
- {sync, false}]),
- receive
- {http, {RequestId, saved_to_file}} ->
- ok;
- {http, Msg} ->
- tsf(Msg)
- end,
-
- {ok, Bin} = file:read_file(FilePath),
- {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL),
- Bin == Body;
- _ ->
- skip("Failed to start local http-server")
- end.
-%%-------------------------------------------------------------------------
-http_headers(doc) ->
- ["Use as many request headers as possible not used in proxy_headers"];
-http_headers(suite) ->
- [];
-http_headers(Config) when is_list(Config) ->
-
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- DocRoot = ?config(doc_root, Config),
- {ok, FileInfo} =
- file:read_file_info(filename:join([DocRoot,"dummy.html"])),
- CreatedSec =
- calendar:datetime_to_gregorian_seconds(
- FileInfo#file_info.mtime),
-
- Mod = httpd_util:rfc1123_date(
- calendar:gregorian_seconds_to_datetime(
- CreatedSec-1)),
-
- Date = httpd_util:rfc1123_date({date(), time()}),
-
- {ok, {{_,200,_}, [_ | _], [_ | _]}} =
- httpc:request(get, {URL, [{"If-Modified-Since",
- Mod},
- {"From","[email protected]"},
- {"Date", Date}
- ]}, [], []),
-
- Mod1 = httpd_util:rfc1123_date(
- calendar:gregorian_seconds_to_datetime(
- CreatedSec+1)),
-
- {ok, {{_,200,_}, [_ | _], [_ | _]}} =
- httpc:request(get, {URL, [{"If-UnModified-Since",
- Mod1}
- ]}, [], []),
-
- Tag = httpd_util:create_etag(FileInfo),
-
-
- {ok, {{_,200,_}, [_ | _], [_ | _]}} =
- httpc:request(get, {URL, [{"If-Match",
- Tag}
- ]}, [], []),
-
- {ok, {{_,200,_}, [_ | _], _}} =
- httpc:request(get, {URL, [{"If-None-Match",
- "NotEtag,NeihterEtag"},
- {"Connection", "Close"}
- ]}, [], []),
- ok;
- _ ->
- skip("Failed to start local http-server")
- end.
+ Request2 = {url(group_name(Config), "/once_chunked.html", Config), []},
+ stream_test(Request2, {stream, {self, once}}).
%%-------------------------------------------------------------------------
-http_headers_dummy(doc) ->
- ["Test the code for handling headers we do not want/can send "
- "to a real server. Note it is not logical to send"
- "all of these headers together, we only want to test that"
- "the code for handling headers will not crash."];
-http_headers_dummy(suite) ->
- [];
-http_headers_dummy(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html",
-
- Foo = http_chunk:encode("foobar") ++
- binary_to_list(http_chunk:encode_last()),
- FooBar = Foo ++ "\r\n\r\nOther:inets_test\r\n\r\n",
+redirect_multiple_choises() ->
+ [{doc, "The user agent, selection of the most appropriate choice MAY "
+ "be performed automatically."}].
+redirect_multiple_choises(Config) when is_list(Config) ->
+ URL300 = url(group_name(Config), "/300.html", Config),
- UserPasswd = base64:encode_to_string("Alladin:Sesame"),
- Auth = "Basic " ++ UserPasswd,
-
- %% The dummy server will ignore the headers, we only want to test
- %% that the client header-handling code. This would not
- %% be a vaild http-request!
- {ok, {{_,200,_}, [_ | _], [_|_]}} =
- httpc:request(post,
- {URL,
- [{"Via",
- "1.0 fred, 1.1 nowhere.com (Apache/1.1)"},
- {"Warning","1#pseudonym foobar"},
- {"Vary","*"},
- {"Upgrade","HTTP/2.0"},
- {"Pragma", "1#no-cache"},
- {"Cache-Control", "no-cache"},
- {"Connection", "close"},
- {"Date", "Sat, 29 Oct 1994 19:43:31 GMT"},
- {"Accept", " text/plain; q=0.5, text/html"},
- {"Accept-Language", "en"},
- {"Accept-Encoding","chunked"},
- {"Accept-Charset", "ISO8859-1"},
- {"Authorization", Auth},
- {"Expect", "1#100-continue"},
- {"User-Agent","inets"},
- {"Transfer-Encoding","chunked"},
- {"Range", " bytes=0-499"},
- {"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"},
- {"If-Match", "*"},
- {"Content-Type", "text/plain"},
- {"Content-Encoding", "chunked"},
- {"Content-Length", "6"},
- {"Content-Language", "en"},
- {"Content-Location", "http://www.foobar.se"},
- {"Content-MD5",
- "104528739076276072743283077410617235478"},
- {"Content-Range", "bytes 0-499/1234"},
- {"Allow", "GET"},
- {"Proxy-Authorization", Auth},
- {"Expires", "Sat, 29 Oct 1994 19:43:31 GMT"},
- {"Upgrade", "HTTP/2.0"},
- {"Last-Modified", "Sat, 29 Oct 1994 19:43:31 GMT"},
- {"Trailer","1#User-Agent"}
- ], "text/plain", FooBar},
- [], []),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
-
+ catch {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, {URL300, []}, [], []),
+ {ok, {{_,300,_}, [_ | _], _}} =
+ httpc:request(get, {URL300, []}, [{autoredirect, false}], []).
%%-------------------------------------------------------------------------
-http_bad_response(doc) ->
- ["Test what happens when the server does not follow the protocol"];
-http_bad_response(suite) ->
- [];
-http_bad_response(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html",
-
- URL1 = ?URL_START ++ integer_to_list(Port) ++ "/wrong_statusline.html",
-
- {error, timeout} = httpc:request(get, {URL, []}, [{timeout, 400}], []),
-
- {error, Reason} = httpc:request(URL1),
-
- test_server:format("Wrong Statusline: ~p~n", [Reason]),
-
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+redirect_moved_permanently() ->
+ [{doc, "If the 301 status code is received in response to a request other "
+ "than GET or HEAD, the user agent MUST NOT automatically redirect the request "
+ "unless it can be confirmed by the user, since this might change "
+ "the conditions under which the request was issued."}].
+redirect_moved_permanently(Config) when is_list(Config) ->
+ URL301 = url(group_name(Config), "/301.html", Config),
-%%-------------------------------------------------------------------------
-ssl_head(doc) ->
- ["Same as http_head/1 but over ssl sockets."];
-ssl_head(suite) ->
- [];
-ssl_head(Config) when is_list(Config) ->
- ssl_head(ssl, Config).
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, {URL301, []}, [], []),
-essl_head(doc) ->
- ["Same as http_head/1 but over ssl sockets."];
-essl_head(suite) ->
- [];
-essl_head(Config) when is_list(Config) ->
- ssl_head(essl, Config).
-
-ssl_head(SslTag, Config) ->
- tsp("ssl_head -> entry with"
- "~n SslTag: ~p"
- "~n Config: ~p", [SslTag, Config]),
- case ?config(local_ssl_server, Config) of
- ok ->
- DataDir = ?config(data_dir, Config),
- Port = ?config(local_ssl_port, Config),
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
- SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
- SSLConfig =
- case SslTag of
- ssl ->
- SSLOptions;
- essl ->
- {essl, SSLOptions}
- end,
- tsp("ssl_head -> make request using: "
- "~n URL: ~p"
- "~n SslTag: ~p"
- "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]),
- {ok, {{_,200, _}, [_ | _], []}} =
- httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []);
- {ok, _} ->
- skip("local http-server not started");
- _ ->
- skip("SSL not started")
- end.
+ {ok, {{_,200,_}, [_ | _], []}}
+ = httpc:request(head, {URL301, []}, [], []),
-
+ {ok, {{_,301,_}, [_ | _], [_|_]}}
+ = httpc:request(post, {URL301, [],"text/plain", "foobar"},
+ [], []).
%%-------------------------------------------------------------------------
-ssl_get(doc) ->
- ["Same as http_get/1 but over ssl sockets."];
-ssl_get(suite) ->
- [];
-ssl_get(Config) when is_list(Config) ->
- ssl_get(ssl, Config).
+redirect_found() ->
+ [{doc," If the 302 status code is received in response to a request other "
+ "than GET or HEAD, the user agent MUST NOT automatically redirect the "
+ "request unless it can be confirmed by the user, since this might change "
+ "the conditions under which the request was issued."}].
+redirect_found(Config) when is_list(Config) ->
-essl_get(doc) ->
- ["Same as http_get/1 but over ssl sockets."];
-essl_get(suite) ->
- [];
-essl_get(Config) when is_list(Config) ->
- ssl_get(essl, Config).
+ URL302 = url(group_name(Config), "/302.html", Config),
-ssl_get(SslTag, Config) when is_list(Config) ->
- case ?config(local_ssl_server, Config) of
- ok ->
- DataDir = ?config(data_dir, Config),
- Port = ?config(local_ssl_port, Config),
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
- SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
- SSLConfig =
- case SslTag of
- ssl ->
- SSLOptions;
- essl ->
- {essl, SSLOptions}
- end,
- tsp("ssl_get -> make request using: "
- "~n URL: ~p"
- "~n SslTag: ~p"
- "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]),
- case httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []) of
- {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} ->
- inets_test_lib:check_body(Body),
- ok;
- {ok, {StatusLine, Headers, _Body}} ->
- tsp("ssl_get -> unexpected result: "
- "~n StatusLine: ~p"
- "~n Headers: ~p", [StatusLine, Headers]),
- tsf({unexpected_response, StatusLine, Headers});
- {error, Reason} ->
- tsp("ssl_get -> request failed: "
- "~n Reason: ~p", [Reason]),
- tsf({request_failed, Reason})
- end;
- {ok, _} ->
- skip("local http-server not started");
- _ ->
- skip("SSL not started")
- end.
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, {URL302, []}, [], []),
+ {ok, {{_,200,_}, [_ | _], []}}
+ = httpc:request(head, {URL302, []}, [], []),
+ {ok, {{_,302,_}, [_ | _], [_|_]}}
+ = httpc:request(post, {URL302, [],"text/plain", "foobar"},
+ [], []).
%%-------------------------------------------------------------------------
-ssl_trace(doc) ->
- ["Same as http_trace/1 but over ssl sockets."];
-ssl_trace(suite) ->
- [];
-ssl_trace(Config) when is_list(Config) ->
- ssl_trace(ssl, Config).
+redirect_see_other() ->
+ [{doc, "The different URI SHOULD be given by the Location field in the response. "
+ "Unless the request method was HEAD, the entity of the response SHOULD contain a short "
+ "hypertext note with a hyperlink to the new URI(s). "}].
+redirect_see_other(Config) when is_list(Config) ->
-essl_trace(doc) ->
- ["Same as http_trace/1 but over ssl sockets."];
-essl_trace(suite) ->
- [];
-essl_trace(Config) when is_list(Config) ->
- ssl_trace(essl, Config).
+ URL303 = url(group_name(Config), "/303.html", Config),
-ssl_trace(SslTag, Config) when is_list(Config) ->
- case ?config(local_ssl_server, Config) of
- ok ->
- DataDir = ?config(data_dir, Config),
- Port = ?config(local_ssl_port, Config),
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
- SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
- SSLConfig =
- case SslTag of
- ssl ->
- SSLOptions;
- essl ->
- {essl, SSLOptions}
- end,
- tsp("ssl_trace -> make request using: "
- "~n URL: ~p"
- "~n SslTag: ~p"
- "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]),
- case httpc:request(trace, {URL, []}, [{ssl, SSLConfig}], []) of
- {ok, {{_,200, _}, [_ | _], "TRACE /dummy.html" ++ _}} ->
- ok;
- {ok, {{_,200,_}, [_ | _], WrongBody}} ->
- tsf({wrong_body, WrongBody});
- {ok, WrongReply} ->
- tsf({wrong_reply, WrongReply});
- Error ->
- tsf({failed, Error})
- end;
- {ok, _} ->
- skip("local http-server not started");
- _ ->
- skip("SSL not started")
- end.
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, {URL303, []}, [], []),
+ {ok, {{_,200,_}, [_ | _], []}}
+ = httpc:request(head, {URL303, []}, [], []),
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(post, {URL303, [],"text/plain", "foobar"},
+ [], []).
%%-------------------------------------------------------------------------
-http_redirect(doc) ->
- ["Test redirect with dummy server as httpd does not implement"
- " server redirect"];
-http_redirect(suite) ->
- [];
-http_redirect(Config) when is_list(Config) ->
- tsp("http_redirect -> entry with"
- "~n Config: ~p", [Config]),
- case ?config(local_server, Config) of
- ok ->
- %% tsp("http_redirect -> set ipfamily option to inet"),
- %% ok = httpc:set_options([{ipfamily, inet}]),
+redirect_temporary_redirect() ->
+ [{doc," If the 307 status code is received in response to a request other "
+ "than GET or HEAD, the user agent MUST NOT automatically redirect the request "
+ "unless it can be confirmed by the user, since this might change "
+ "the conditions under which the request was issued."}].
+redirect_temporary_redirect(Config) when is_list(Config) ->
- tsp("http_redirect -> start dummy server inet"),
- {DummyServerPid, Port} = dummy_server(ipv4),
- tsp("http_redirect -> server port = ~p", [Port]),
-
- URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html",
-
- tsp("http_redirect -> issue request 1: "
- "~n ~p", [URL300]),
- {ok, {{_,200,_}, [_ | _], [_|_]}}
- = httpc:request(get, {URL300, []}, [], []),
-
- tsp("http_redirect -> issue request 2: "
- "~n ~p", [URL300]),
- {ok, {{_,300,_}, [_ | _], _}} =
- httpc:request(get, {URL300, []}, [{autoredirect, false}], []),
-
- URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html",
-
- tsp("http_redirect -> issue request 3: "
- "~n ~p", [URL301]),
- {ok, {{_,200,_}, [_ | _], [_|_]}}
- = httpc:request(get, {URL301, []}, [], []),
-
- tsp("http_redirect -> issue request 4: "
- "~n ~p", [URL301]),
- {ok, {{_,200,_}, [_ | _], []}}
- = httpc:request(head, {URL301, []}, [], []),
-
- tsp("http_redirect -> issue request 5: "
- "~n ~p", [URL301]),
- {ok, {{_,301,_}, [_ | _], [_|_]}}
- = httpc:request(post, {URL301, [],"text/plain", "foobar"},
- [], []),
-
- URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html",
-
- tsp("http_redirect -> issue request 6: "
- "~n ~p", [URL302]),
- {ok, {{_,200,_}, [_ | _], [_|_]}}
- = httpc:request(get, {URL302, []}, [], []),
- case httpc:request(get, {URL302, []}, [], []) of
- {ok, Reply7} ->
- case Reply7 of
- {{_,200,_}, [_ | _], [_|_]} ->
- tsp("http_redirect -> "
- "expected reply for request 7"),
- ok;
- {StatusLine, Headers, Body} ->
- tsp("http_redirect -> "
- "unexpected reply for request 7: "
- "~n StatusLine: ~p"
- "~n Headers: ~p"
- "~n Body: ~p",
- [StatusLine, Headers, Body]),
- tsf({unexpected_reply, Reply7})
- end;
- Error7 ->
- tsp("http_redirect -> "
- "unexpected result for request 7: "
- "~n Error7: ~p",
- [Error7]),
- tsf({unexpected_result, Error7})
- end,
-
- tsp("http_redirect -> issue request 7: "
- "~n ~p", [URL302]),
- {ok, {{_,200,_}, [_ | _], []}}
- = httpc:request(head, {URL302, []}, [], []),
-
- tsp("http_redirect -> issue request 8: "
- "~n ~p", [URL302]),
- {ok, {{_,302,_}, [_ | _], [_|_]}}
- = httpc:request(post, {URL302, [],"text/plain", "foobar"},
- [], []),
-
- URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html",
-
- tsp("http_redirect -> issue request 9: "
- "~n ~p", [URL307]),
- {ok, {{_,200,_}, [_ | _], [_|_]}}
- = httpc:request(get, {URL307, []}, [], []),
-
- tsp("http_redirect -> issue request 10: "
- "~n ~p", [URL307]),
- {ok, {{_,200,_}, [_ | _], []}}
- = httpc:request(head, {URL307, []}, [], []),
-
- tsp("http_redirect -> issue request 11: "
- "~n ~p", [URL307]),
- {ok, {{_,307,_}, [_ | _], [_|_]}}
- = httpc:request(post, {URL307, [],"text/plain", "foobar"},
- [], []),
-
- tsp("http_redirect -> stop dummy server"),
- DummyServerPid ! stop,
- tsp("http_redirect -> reset ipfamily option (to inet6fb4)"),
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- tsp("http_redirect -> done"),
- ok;
+ URL307 = url(group_name(Config), "/307.html", Config),
- _ ->
- skip("Failed to start local http-server")
- end.
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, {URL307, []}, [], []),
+ {ok, {{_,200,_}, [_ | _], []}}
+ = httpc:request(head, {URL307, []}, [], []),
+ {ok, {{_,307,_}, [_ | _], [_|_]}}
+ = httpc:request(post, {URL307, [],"text/plain", "foobar"},
+ [], []).
%%-------------------------------------------------------------------------
-http_redirect_loop(doc) ->
- ["Test redirect loop detection"];
-http_redirect_loop(suite) ->
- [];
-http_redirect_loop(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html",
-
- {ok, {{_,300,_}, [_ | _], _}}
- = httpc:request(get, {URL, []}, [], []),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
-
-%%-------------------------------------------------------------------------
-http_internal_server_error(doc) ->
- ["Test 50X codes"];
-http_internal_server_error(suite) ->
- [];
-http_internal_server_error(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html",
-
- {ok, {{_,500,_}, [_ | _], _}}
- = httpc:request(get, {URL500, []}, [], []),
+redirect_loop() ->
+ [{"doc, Test redirect loop detection"}].
+redirect_loop(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/redirectloop.html", Config),
- URL503 = ?URL_START ++ integer_to_list(Port) ++ "/503.html",
-
- %% Used to be able to make the service available after retry.
- ets:new(unavailable, [named_table, public, set]),
- ets:insert(unavailable, {503, unavailable}),
-
- {ok, {{_,200, _}, [_ | _], [_|_]}} =
- httpc:request(get, {URL503, []}, [], []),
-
- ets:insert(unavailable, {503, long_unavailable}),
-
- {ok, {{_,503, _}, [_ | _], [_|_]}} =
- httpc:request(get, {URL503, []}, [], []),
-
- ets:delete(unavailable),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
-
+ {ok, {{_,300,_}, [_ | _], _}}
+ = httpc:request(get, {URL, []}, [], []).
%%-------------------------------------------------------------------------
-http_userinfo(doc) ->
- ["Test user info e.i. http://user:passwd@host:port/"];
-http_userinfo(suite) ->
- [];
-http_userinfo(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
-
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URLAuth = "http://alladin:sesame@localhost:"
- ++ integer_to_list(Port) ++ "/userinfo.html",
-
- {ok, {{_,200,_}, [_ | _], _}}
- = httpc:request(get, {URLAuth, []}, [], []),
-
- URLUnAuth = "http://alladin:foobar@localhost:"
- ++ integer_to_list(Port) ++ "/userinfo.html",
-
- {ok, {{_,401, _}, [_ | _], _}} =
- httpc:request(get, {URLUnAuth, []}, [], []),
-
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+cookie() ->
+ [{doc, "Test cookies."}].
+cookie(Config) when is_list(Config) ->
+ ok = httpc:set_options([{cookies, enabled}]),
+ Request0 = {url(group_name(Config), "/cookie.html", Config), []},
-%%-------------------------------------------------------------------------
-http_cookie(doc) ->
- ["Test cookies."];
-http_cookie(suite) ->
- [];
-http_cookie(Config) when is_list(Config) ->
- ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URLStart = ?URL_START
- ++ integer_to_list(Port),
-
- URLCookie = URLStart ++ "/cookie.html",
-
- {ok, {{_,200,_}, [_ | _], [_|_]}}
- = httpc:request(get, {URLCookie, []}, [], []),
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, Request0, [], []),
+ %% Populate table to be used by the "dummy" server
ets:new(cookie, [named_table, public, set]),
ets:insert(cookie, {cookies, true}),
- {ok, {{_,200,_}, [_ | _], [_|_]}}
- = httpc:request(get, {URLStart ++ "/", []}, [], []),
-
- ets:delete(cookie),
-
- ok = httpc:set_options([{cookies, disabled}]),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ Request1 = {url(group_name(Config), "/", Config), []},
-%%-------------------------------------------------------------------------
-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.
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, Request1, [], []),
+ ets:delete(cookie),
+ ok = httpc:set_options([{cookies, disabled}]).
%%-------------------------------------------------------------------------
-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.
-
+headers_as_is(doc) ->
+ ["Test the option headers_as_is"];
+headers_as_is(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/dummy.html", Config),
+ {ok, {{_,200,_}, [_|_], [_|_]}} =
+ httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]},
+ [], [{headers_as_is, true}]),
+ {ok, {{_,400,_}, [_|_], [_|_]}} =
+ httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]).
%%-------------------------------------------------------------------------
-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.
+userinfo(doc) ->
+ [{doc, "Test user info e.i. http://user:passwd@host:port/"}];
+userinfo(Config) when is_list(Config) ->
+
+ {ok,Host} = inet:gethostname(),
+
+ URLAuth = url(group_name(Config), "alladin:sesame@" ++ Host ++ ":","/userinfo.html", Config),
-%%-------------------------------------------------------------------------
-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.
-
+ {ok, {{_,200,_}, [_ | _], _}}
+ = httpc:request(get, {URLAuth, []}, [], []),
-%%-------------------------------------------------------------------------
-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.
+ URLUnAuth = url(group_name(Config), "alladin:foobar@" ++ Host ++ ":","/userinfo.html", Config),
+ {ok, {{_,401, _}, [_ | _], _}} =
+ httpc:request(get, {URLUnAuth, []}, [], []).
%%-------------------------------------------------------------------------
-http_server_does_not_exist(doc) ->
- ["Test that we get an error message back when the server "
- "does note exist."];
-http_server_does_not_exist(suite) ->
- [];
-http_server_does_not_exist(Config) when is_list(Config) ->
- {error, _} =
- httpc:request(get, {"http://localhost:" ++
- integer_to_list(?NOT_IN_USE_PORT)
- ++ "/", []},[], []),
- ok.
-
-%%-------------------------------------------------------------------------
page_does_not_exist(doc) ->
["Test that we get a 404 when the page is not found."];
-page_does_not_exist(suite) ->
- [];
page_does_not_exist(Config) when is_list(Config) ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/doesnotexist.html",
- {ok, {{_,404,_}, [_ | _], [_ | _]}}
- = httpc:request(get, {URL, []}, [], []),
- ok.
-
-
-%%-------------------------------------------------------------------------
-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.
-
-
+ URL = url(group_name(Config), "/doesnotexist.html", Config),
+ {ok, {{_,404,_}, [_ | _], [_ | _]}}
+ = httpc:request(get, {URL, []}, [], []).
%%-------------------------------------------------------------------------
-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.
-
+streaming_error(doc) ->
+ [{doc, "Only async requests can be stremed - Solves OTP-8056"}];
+streaming_error(Config) when is_list(Config) ->
+ Method = get,
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ {error, streaming_error} = httpc:request(Method, Request,
+ [], [{sync, true}, {stream, {self, once}}]),
+ {error, streaming_error} = httpc:request(Method, Request,
+ [], [{sync, true}, {stream, self}]).
%%-------------------------------------------------------------------------
-http_stream(doc) ->
- ["Test the option stream for asynchrony requests"];
-http_stream(suite) ->
- [];
-http_stream(Config) when is_list(Config) ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, {{_,200,_}, [_ | _], Body}} =
- httpc:request(get, {URL, []}, [], []),
-
- {ok, RequestId} =
- httpc:request(get, {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).
-
-
+server_does_not_exist(doc) ->
+ [{doc, "Test that we get an error message back when the server "
+ "does note exist."}];
+server_does_not_exist(Config) when is_list(Config) ->
+ {error, _} =
+ httpc:request(get, {"http://localhost:" ++
+ integer_to_list(?NOT_IN_USE_PORT)
+ ++ "/", []},[], []).
%%-------------------------------------------------------------------------
-http_stream_once(doc) ->
- ["Test the option stream for asynchrony requests"];
-http_stream_once(suite) ->
- [];
-http_stream_once(Config) when is_list(Config) ->
- p("http_stream_once -> entry with"
- "~n Config: ~p", [Config]),
-
- p("http_stream_once -> set ipfamily to inet", []),
- ok = httpc:set_options([{ipfamily, inet}]),
- p("http_stream_once -> start dummy server", []),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- PortStr = integer_to_list(Port),
- p("http_stream_once -> once", []),
- once(?URL_START ++ PortStr ++ "/once.html"),
- p("http_stream_once -> once_chunked", []),
- once(?URL_START ++ PortStr ++ "/once_chunked.html"),
- p("http_stream_once -> dummy", []),
- once(?URL_START ++ PortStr ++ "/dummy.html"),
-
- p("http_stream_once -> stop dummy server", []),
- DummyServerPid ! stop,
- p("http_stream_once -> set ipfamily to inet6fb4", []),
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- p("http_stream_once -> done", []),
- ok.
-
-once(URL) ->
- p("once -> issue sync request for ~p", [URL]),
- {ok, {{_,200,_}, [_ | _], Body}} =
- httpc:request(get, {URL, []}, [], []),
-
- p("once -> issue async (self stream) request for ~p", [URL]),
- {ok, RequestId} =
- httpc:request(get, {URL, []}, [], [{sync, false},
- {stream, {self, once}}]),
-
- p("once -> await stream_start reply for (async) request ~p", [RequestId]),
- NewPid =
- receive
- {http, {RequestId, stream_start, _Headers, Pid}} ->
- p("once -> received stream_start reply for (async) request ~p: ~p",
- [RequestId, Pid]),
- Pid;
- {http, Msg} ->
- tsf(Msg)
- end,
-
- tsp("once -> request handler: ~p", [NewPid]),
-
- p("once -> await stream reply for (async) request ~p", [RequestId]),
- BodyPart =
- receive
- {http, {RequestId, stream, BinBodyPart}} ->
- p("once -> received stream reply for (async) request ~p: "
- "~n~p", [RequestId, binary_to_list(BinBodyPart)]),
- BinBodyPart
- end,
-
- tsp("once -> first body part '~p' received", [binary_to_list(BodyPart)]),
-
- StreamedBody = receive_streamed_body(RequestId, BinBodyPart, NewPid),
-
- Body = binary_to_list(StreamedBody),
-
- p("once -> done when Bode: ~p", [Body]),
- ok.
-
+no_content_204(doc) ->
+ ["Test the case that the HTTP 204 no content header - Solves OTP 6982"];
+no_content_204(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/no_content.html", Config),
+ {ok, {{_,204,_}, [], []}} = httpc:request(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.
-
+tolerate_missing_CR() ->
+ [{doc, "Test the case that the HTTP server uses only LF instead of CRLF"
+ "as delimitor. Solves OTP-7304"}].
+tolerate_missing_CR(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/missing_CR.html", Config),
+ {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL).
%%-------------------------------------------------------------------------
-parse_url(doc) ->
- ["Test that an url is parsed correctly"];
-parse_url(suite) ->
- [];
-parse_url(Config) when is_list(Config) ->
- %% ipv6
- {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
- http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html"),
- {ok, {http,[],"[2010:836B:4179::836B:4179]",80,"/foobar.html",[]}} =
- http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html",
- [{ipv6_host_with_brackets, true}]),
- {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
- http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html",
- [{ipv6_host_with_brackets, false}]),
- {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
- http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html",
- [{foo, false}]),
- {error,
- {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} =
- http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"),
-
- %% ipv4
- {ok, {http,[],"127.0.0.1",80,"/foobar.html",[]}} =
- http_uri:parse("http://127.0.0.1/foobar.html"),
-
- %% host
- {ok, {http,[],"localhost",8888,"/foobar.html",[]}} =
- http_uri:parse("http://localhost:8888/foobar.html"),
-
- %% Userinfo
- {ok, {http,"nisse:foobar","localhost",8888,"/foobar.html",[]}} =
- http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"),
-
- %% Scheme error
- {error, no_scheme} = http_uri:parse("localhost/foobar.html"),
- {error, {malformed_url, _, _}} =
- http_uri:parse("localhost:8888/foobar.html"),
-
- %% Query
- {ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} =
- http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42"),
-
- %% Esc chars
- {ok, {http,[],"www.somedomain.com",80,"/%2Eabc",[]}} =
- http_uri:parse("http://www.somedomain.com/%2Eabc"),
- {ok, {http,[],"www.somedomain.com",80,"/%252Eabc",[]}} =
- http_uri:parse("http://www.somedomain.com/%252Eabc"),
- {ok, {http,[],"www.somedomain.com",80,"/%25abc",[]}} =
- http_uri:parse("http://www.somedomain.com/%25abc"),
- {ok, {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"}} =
- http_uri:parse("http://www.somedomain.com/%25abc?foo=bar"),
-
-
- ok.
+empty_body() ->
+ [{doc, "An empty body was not returned directly. There was a delay for several"
+ "seconds. Solves OTP-6243."}].
+empty_body(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/empty.html", Config),
+ {ok, {{_,200,_}, [_ | _], []}} =
+ httpc:request(get, {URL, []}, [{timeout, 500}], []).
%%-------------------------------------------------------------------------
-ipv6_ipcomm() ->
- [{require, ipv6_hosts}].
-ipv6_ipcomm(doc) ->
- ["Test ip_comm ipv6."];
-ipv6_ipcomm(suite) ->
- [];
-ipv6_ipcomm(Config) when is_list(Config) ->
- HTTPOptions = [],
- SocketType = ip_comm,
- Scheme = "http",
- Extra = [],
- ipv6(SocketType, Scheme, HTTPOptions, Extra, Config).
-
+transfer_encoding() ->
+ [{doc, "Transfer encoding is case insensitive. Solves OTP-6807"}].
+transfer_encoding(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/capital_transfer_encoding.html", Config),
+ {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL).
%%-------------------------------------------------------------------------
-ipv6_essl() ->
- [{require, ipv6_hosts}].
-ipv6_essl(doc) ->
- ["Test essl ipv6."];
-ipv6_essl(suite) ->
- [];
-ipv6_essl(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
- SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
- SSLConfig = {essl, SSLOptions},
- tsp("ssl_ipv6 -> make request using: "
- "~n SSLOptions: ~p", [SSLOptions]),
- HTTPOptions = [{ssl, SSLConfig}],
- SocketType = essl,
- Scheme = "https",
- Extra = SSLOptions,
- ipv6(SocketType, Scheme, HTTPOptions, Extra, Config).
-
+empty_response_header() ->
+ [{doc, "Test the case that the HTTP server does not send any headers. Solves OTP-6830"}].
+empty_response_header(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/no_headers.html", Config),
+ {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL).
%%-------------------------------------------------------------------------
-ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) ->
- %% Check if we are a IPv6 host
- tsp("ipv6 -> verify ipv6 support"),
- case inets_test_lib:has_ipv6_support(Config) of
- {ok, Addr} ->
- tsp("ipv6 -> ipv6 supported: ~p", [Addr]),
- {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra),
- Profile = ?config(profile, Config),
- URL =
- Scheme ++
- "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++
- integer_to_list(Port) ++ "/foobar.html",
- tsp("ipv6 -> issue request with: "
- "~n URL: ~p"
- "~n HTTPOptions: ~p", [URL, HTTPOptions]),
- case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of
- {ok, {{_,200,_}, [_ | _], [_|_]}} ->
- tsp("ipv6 -> expected result"),
- DummyServerPid ! stop,
- ok;
- {ok, Unexpected} ->
- tsp("ipv6 -> unexpected result: "
- "~n ~p", [Unexpected]),
- DummyServerPid ! stop,
- tsf({unexpected_result, Unexpected});
- {error, Reason} ->
- tsp("ipv6 -> error: "
- "~n Reason: ~p", [Reason]),
- DummyServerPid ! stop,
- tsf(Reason)
- end,
- ok;
- _ ->
- tsp("ipv6 -> ipv6 not supported"),
- skip("Host does not support IPv6")
- end.
+bad_response(doc) ->
+ [{doc, "Test what happens when the server does not follow the protocol"}];
+bad_response(Config) when is_list(Config) ->
-%%-------------------------------------------------------------------------
-
-headers_as_is(doc) ->
- ["Test the option headers_as_is"];
-headers_as_is(suite) ->
- [];
-headers_as_is(Config) when is_list(Config) ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, {{_,200,_}, [_|_], [_|_]}} =
- httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]},
- [], [{headers_as_is, true}]),
-
- {ok, {{_,400,_}, [_|_], [_|_]}} =
- httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]),
- ok.
+ URL0 = url(group_name(Config), "/missing_crlf.html", Config),
+ URL1 = url(group_name(Config), "/wrong_statusline.html", Config),
+ {error, timeout} = httpc:request(get, {URL0, []}, [{timeout, 400}], []),
+ {error, Reason} = httpc:request(URL1),
+ ct:print("Wrong Statusline: ~p~n", [Reason]).
%%-------------------------------------------------------------------------
-selecting_session(doc) ->
- ["Test selection of sessions - OTP-9847"];
-selecting_session(suite) ->
- [];
-selecting_session(Config) when is_list(Config) ->
- tsp("selecting_session -> entry with"
- "~n Config: ~p", [Config]),
-
- tsp("selecting_session -> set ipfamily to inet"),
- ok = httpc:set_options([{ipfamily, inet}]),
-
- tsp("selecting_session -> start server"),
- {ServerPid, Port} = otp_9847_server(),
-
- PortStr = integer_to_list(Port),
- URL = ?URL_START ++ PortStr ++ "/index.html",
-
- tsp("selecting_session -> issue the first batch (three) requests"),
- lists:foreach(fun(P) ->
- tsp("selecting_session:fun1 -> "
- "send stop request to ~p", [P]),
- P ! stop
- end,
- reqs(URL, ServerPid, 3, 3, false)),
- tsp("selecting_session -> sleep some (1) to make sure nothing lingers"),
- ?SLEEP(5000),
- tsp("selecting_session -> "
- "instruct the server to reply to the first request"),
- ServerPid ! {answer, true},
- receive
- {answer, true} ->
- tsp("selecting_session -> "
- "received ack from server to reply to the first request"),
- ok
- end,
- tsp("selecting_session -> issue the second batch (four) requests"),
- lists:foreach(fun(P) ->
- tsp("selecting_session:fun2 -> "
- "send stop request to ~p", [P]),
- P ! stop
- end,
- reqs(URL, ServerPid, 4, 1, true)),
- tsp("selecting_session -> sleep some (2) to make sure nothing lingers"),
- ?SLEEP(5000),
-
- tsp("selecting_session -> stop server"),
- ServerPid ! stop,
- tsp("selecting_session -> set ipfamily (back) to inet6fb4"),
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- tsp("selecting_session -> done"),
- ok.
-
-reqs(URL, ServerPid, NumReqs, NumHandlers, InitialSync) ->
- tsp("reqs -> entry with"
- "~n URL: ~p"
- "~n ServerPid: ~w"
- "~n NumReqs: ~w"
- "~n NumHandlers: ~w"
- "~n InitialSync: ~w",
- [URL, ServerPid, NumReqs, NumHandlers, InitialSync]),
- Handlers = reqs2(URL, NumReqs, [], InitialSync),
- tsp("reqs -> "
- "~n Handlers: ~w", [Handlers]),
- case length(Handlers) of
- NumHandlers ->
- tsp("reqs -> "
- "~n NumHandlers: ~w", [NumHandlers]),
- ServerPid ! num_handlers,
- receive
- {num_handlers, NumHandlers} ->
- tsp("reqs -> received num_handlers with"
- "~n NumHandlers: ~w", [NumHandlers]),
- Handlers;
- {num_handlers, WrongNumHandlers} ->
- tsp("reqs -> received num_handlers with"
- "~n WrongNumHandlers: ~w", [WrongNumHandlers]),
- exit({wrong_num_handlers1, WrongNumHandlers, NumHandlers})
- end;
- WrongNumHandlers ->
- tsp("reqs -> "
- "~n WrongNumHandlers: ~w", [WrongNumHandlers]),
- exit({wrong_num_handlers2, WrongNumHandlers, NumHandlers})
- end.
-
-
-reqs2(_URL, 0, Acc, _Sync) ->
- lists:reverse(Acc);
-reqs2(URL, Num, Acc, Sync) ->
- tsp("reqs2 -> entry with"
- "~n Num: ~w"
- "~n Sync: ~w", [Num, Sync]),
- case httpc:request(get, {URL, []}, [], [{sync, Sync}]) of
- {ok, _Reply} ->
- tsp("reqs2 -> successful request: ~p", [_Reply]),
- receive
- {handler, Handler, _Manager} ->
- %% This is when a new handler is created
- tsp("reqs2 -> received handler: ~p", [Handler]),
- case lists:member(Handler, Acc) of
- true ->
- tsp("reqs2 -> duplicate handler"),
- exit({duplicate_handler, Handler, Num, Acc});
- false ->
- tsp("reqs2 -> wait for data ack"),
- receive
- {data_received, Handler} ->
- tsp("reqs2 -> "
- "received data ack from ~p", [Handler]),
- case Sync of
- true ->
- reqs2(URL, Num-1, [Handler|Acc],
- false);
- false ->
- reqs2(URL, Num-1, [Handler|Acc],
- Sync)
- end
- end
- end;
-
- {data_received, Handler} ->
- tsp("reqs2 -> "
- "received data ack from ~p", [Handler]),
- reqs2(URL, Num-1, Acc, false)
-
- end;
-
- {error, Reason} ->
- tsp("reqs2 -> request ~w failed: ~p", [Num, Reason]),
- exit({request_failed, Reason, Num, Acc})
- end.
-
-otp_9847_server() ->
- TC = self(),
- Pid = spawn_link(fun() -> otp_9847_server_init(TC) end),
- receive
- {port, Port} ->
- {Pid, Port}
- end.
-
-otp_9847_server_init(TC) ->
- tsp("otp_9847_server_init -> entry with"
- "~n TC: ~p", [TC]),
- {ok, ListenSocket} =
- gen_tcp:listen(0, [binary, inet, {packet, 0},
- {reuseaddr,true},
- {active, false}]),
- tsp("otp_9847_server_init -> listen socket created: "
- "~n ListenSocket: ~p", [ListenSocket]),
- {ok, Port} = inet:port(ListenSocket),
- tsp("otp_9847_server_init -> Port: ~p", [Port]),
- TC ! {port, Port},
- otp_9847_server_main(TC, ListenSocket, false, []).
-
-otp_9847_server_main(TC, ListenSocket, Answer, Handlers) ->
- tsp("otp_9847_server_main -> entry with"
- "~n TC: ~p"
- "~n ListenSocket: ~p"
- "~n Answer: ~p"
- "~n Handlers: ~p", [TC, ListenSocket, Answer, Handlers]),
- case gen_tcp:accept(ListenSocket, 1000) of
- {ok, Sock} ->
- tsp("otp_9847_server_main -> accepted"
- "~n Sock: ~p", [Sock]),
- {Handler, Mon, Port} = otp_9847_handler(TC, Sock, Answer),
- tsp("otp_9847_server_main -> handler ~p created for ~w",
- [Handler, Port]),
- gen_tcp:controlling_process(Sock, Handler),
- tsp("otp_9847_server_main -> control transfer"),
- Handler ! owner,
- tsp("otp_9847_server_main -> "
- "handler ~p informed of owner transfer", [Handler]),
- TC ! {handler, Handler, self()},
- tsp("otp_9847_server_main -> "
- "TC ~p informed of handler ~p", [TC, Handler]),
- otp_9847_server_main(TC, ListenSocket, Answer,
- [{Handler, Mon, Sock, Port}|Handlers]);
+internal_server_error(doc) ->
+ ["Test 50X codes"];
+internal_server_error(Config) when is_list(Config) ->
- {error, timeout} ->
- tsp("otp_9847_server_main -> timeout"),
- receive
- {answer, true} ->
- tsp("otp_9847_server_main -> received answer request"),
- TC ! {answer, true},
- otp_9847_server_main(TC, ListenSocket, true, Handlers);
-
- {'DOWN', _Mon, process, Pid, _Reason} ->
- %% Could be one of the handlers
- tsp("otp_9847_server_main -> received DOWN for ~p", [Pid]),
- otp_9847_server_main(TC, ListenSocket, Answer,
- lists:keydelete(Pid, 1, Handlers));
-
- num_handlers ->
- tsp("otp_9847_server_main -> "
- "received request for number of handlers (~w)",
- [length(Handlers)]),
- TC ! {num_handlers, length(Handlers)},
- otp_9847_server_main(TC, ListenSocket, Answer, Handlers);
+ URL500 = url(group_name(Config), "/500.html", Config),
- stop ->
- tsp("otp_9847_server_main -> received stop request"),
- %% Stop all handlers (just in case)
- Pids = [Handler || {Handler, _, _} <- Handlers],
- lists:foreach(fun(Pid) -> Pid ! stop end, Pids),
- exit(normal);
+ {ok, {{_,500,_}, [_ | _], _}}
+ = httpc:request(get, {URL500, []}, [], []),
- Any ->
- tsp("otp_9847_server_main -> received"
- "~n Any: ~p", [Any]),
- exit({crap, Any})
+ URL503 = url(group_name(Config), "/503.html", Config),
- after 0 ->
- tsp("otp_9847_server_main -> nothing in queue"),
- otp_9847_server_main(TC, ListenSocket, Answer, Handlers)
- end;
-
- Error ->
- exit(Error)
- end.
+ %% Used to be able to make the service available after retry.
+ ets:new(unavailable, [named_table, public, set]),
+ ets:insert(unavailable, {503, unavailable}),
+ {ok, {{_,200, _}, [_ | _], [_|_]}} =
+ httpc:request(get, {URL503, []}, [], []),
-otp_9847_handler(TC, Sock, Answer) ->
- tsp("otp_9847_handler -> entry with"
- "~n TC: ~p"
- "~n Sock: ~p"
- "~n Answer: ~p", [TC, Sock, Answer]),
- Self = self(),
- {Pid, Mon} =
- spawn_opt(fun() ->
- otp_9847_handler_init(TC, Self, Sock, Answer)
- end,
- [monitor]),
- receive
- {port, Port} ->
- tsp("otp_9847_handler -> received port message (from ~p)"
- "~n Port: ~p", [Pid, Port]),
- {Pid, Mon, Port}
- end.
-
+ ets:insert(unavailable, {503, long_unavailable}),
-otp_9847_handler_init(TC, Server, Sock, Answer) ->
- tsp("otp_9847_handler_init -> entry with"
- "~n TC: ~p"
- "~n Server: ~p"
- "~n Sock: ~p"
- "~n Answer: ~p", [TC, Server, Sock, Answer]),
- {ok, Port} = inet:port(Sock),
- Server ! {port, Port},
- receive
- owner ->
- tsp("otp_9847_handler_init -> "
- "received owner message - activate socket"),
- inet:setopts(Sock, [{active, true}]),
- otp_9847_handler_main(TC, Server, Sock, Answer, [?HTTP_MAX_HEADER_SIZE])
- end.
-
-otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs) ->
- tsp("otp_9847_handler_main -> entry with"
- "~n TC: ~p"
- "~n Server: ~p"
- "~n Sock: ~p"
- "~n Answer: ~p"
- "~n ParseArgs: ~p", [TC, Server, Sock, Answer, ParseArgs]),
- receive
- stop ->
- tsp("otp_9847_handler_main -> received stop request"),
- exit(normal);
-
- {tcp, Sock, _Data} when Answer =:= false ->
- tsp("otp_9847_handler_main -> received tcp data - no answer"),
- TC ! {data_received, self()},
- inet:setopts(Sock, [{active, true}]),
- %% Ignore all data
- otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs);
-
- {tcp, Sock, Data} when Answer =:= true ->
- tsp("otp_9847_handler_main -> received tcp data - answer"),
- TC ! {data_received, self()},
- inet:setopts(Sock, [{active, true}]),
- NewParseArgs = otp_9847_handler_request(Sock, [Data|ParseArgs]),
- otp_9847_handler_main(TC, Server, Sock, Answer, NewParseArgs);
-
- {tcp_closed, Sock} ->
- tsp("otp_9847_handler_main -> received tcp socket closed"),
- exit(normal);
-
- {tcp_error, Sock, Reason} ->
- tsp("otp_9847_handler_main -> socket error: ~p", [Reason]),
- (catch gen_tcp:close(Sock)),
- exit(normal)
-
- %% after 30000 ->
- %% gen_tcp:close(Sock),
- %% exit(normal)
- end.
-
-otp_9847_handler_request(Sock, Args) ->
- Msg =
- case httpd_request:parse(Args) of
- {ok, {_, "/index.html" = _RelUrl, _, _, _}} ->
- B =
- "<HTML><BODY>" ++
- "...some body part..." ++
- "</BODY></HTML>",
- Len = integer_to_list(length(B)),
- "HTTP/1.1 200 ok\r\n" ++
- "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B
- end,
- gen_tcp:send(Sock, Msg),
- [?HTTP_MAX_HEADER_SIZE].
-
+ {ok, {{_,503, _}, [_ | _], [_|_]}} =
+ httpc:request(get, {URL503, []}, [], []),
+ ets:delete(unavailable).
%%-------------------------------------------------------------------------
-options(doc) ->
- ["Test the option parameters."];
-options(suite) ->
+invalid_http(doc) ->
+ ["Test parse error"];
+invalid_http(suite) ->
[];
-options(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, {{_,200,_}, [_ | _], Bin}}
- = httpc:request(get, {URL, []}, [{foo, bar}],
- %% Ignore unknown options
- [{body_format, binary}, {foo, bar}]),
-
- true = is_binary(Bin),
- {ok, {200, [_|_]}}
- = httpc:request(get, {URL, []}, [{timeout, infinity}],
- [{full_result, false}]);
- _ ->
- skip("Failed to start local http-server")
- end.
+invalid_http(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/invalid_http.html", Config),
-%%-------------------------------------------------------------------------
-
-http_invalid_http(doc) ->
- ["Test parse error"];
-http_invalid_http(suite) ->
- [];
-http_invalid_http(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html",
-
{error, {could_not_parse_as_http, _} = Reason} =
httpc:request(get, {URL, []}, [], []),
-
- test_server:format("Parse error: ~p ~n", [Reason]),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ ct:print("Parse error: ~p ~n", [Reason]).
%%-------------------------------------------------------------------------
+emulate_lower_versions(doc) ->
+ [{doc, "Perform request as 0.9 and 1.0 clients."}];
+emulate_lower_versions(Config) when is_list(Config) ->
--define(GOOGLE, "www.google.com").
-
-hexed_query_otp_6191(doc) ->
- [];
-hexed_query_otp_6191(suite) ->
- [];
-hexed_query_otp_6191(Config) when is_list(Config) ->
- Google = ?GOOGLE,
- GoogleSearch = "http://" ++ Google ++ "/search",
- Search1 = "?hl=en&q=a%D1%85%D1%83%D0%B9&btnG=Google+Search",
- URI1 = GoogleSearch ++ Search1,
- Search2 = "?hl=en&q=%25%25",
- URI2 = GoogleSearch ++ Search2,
- Search3 = "?hl=en&q=%foo",
- URI3 = GoogleSearch ++ Search3,
-
- Verify1 =
- fun({http, [], ?GOOGLE, 80, "/search", _}) -> ok;
- (_) -> error
- end,
- Verify2 = Verify1,
- Verify3 = Verify1,
- verify_uri(URI1, Verify1),
- verify_uri(URI2, Verify2),
- verify_uri(URI3, Verify3),
- ok.
-
-verify_uri(URI, Verify) ->
- case http_uri:parse(URI) of
- {ok, ParsedURI} ->
- case Verify(ParsedURI) of
- ok ->
- ok;
- error ->
- Reason = {unexpected_parse_result, URI, ParsedURI},
- ERROR = {error, Reason},
- throw(ERROR)
- end;
- {error, _} = ERROR ->
- throw(ERROR)
- end.
+ URL = url(group_name(Config), "/dummy.html", Config),
+ {ok, Body0} =
+ httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []),
+ inets_test_lib:check_body(Body0),
+ {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} =
+ httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []),
+ inets_test_lib:check_body(Body1),
+ {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} =
+ httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []),
+ inets_test_lib:check_body(Body2).
%%-------------------------------------------------------------------------
-empty_body_otp_6243(doc) ->
- ["An empty body was not returned directly. There was a delay for several"
- "seconds."];
-empty_body_otp_6243(suite) ->
- [];
-empty_body_otp_6243(Config) when is_list(Config) ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/empty.html",
- {ok, {{_,200,_}, [_ | _], []}} =
- httpc:request(get, {URL, []}, [{timeout, 500}], []).
+relaxed(doc) ->
+ ["Test relaxed mode"];
+relaxed(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/missing_reason_phrase.html", Config),
-%%-------------------------------------------------------------------------
+ {error, Reason} =
+ httpc:request(get, {URL, []}, [{relaxed, false}], []),
-transfer_encoding_otp_6807(doc) ->
- ["Transfer encoding is case insensitive"];
-transfer_encoding_otp_6807(suite) ->
- [];
-transfer_encoding_otp_6807(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++
- "/capital_transfer_encoding.html",
- {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ ct:print("Not relaxed: ~p~n", [Reason]),
+ {ok, {{_, 200, _}, [_ | _], [_ | _]}} =
+ httpc:request(get, {URL, []}, [{relaxed, true}], []).
%%-------------------------------------------------------------------------
-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.
+headers() ->
+ [{doc,"Use as many request headers as possible not used in proxy_headers"}].
+headers(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/dummy.html", Config),
+ DocRoot = ?config(doc_root, Config),
-%%-------------------------------------------------------------------------
+ {ok, FileInfo} =
+ file:read_file_info(filename:join([DocRoot,"dummy.html"])),
+ CreatedSec =
+ calendar:datetime_to_gregorian_seconds(
+ FileInfo#file_info.mtime),
-empty_response_header_otp_6830(doc) ->
- ["Test the case that the HTTP server does not send any headers"];
-empty_response_header_otp_6830(suite) ->
- [];
-empty_response_header_otp_6830(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html",
- {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ Mod = httpd_util:rfc1123_date(
+ calendar:gregorian_seconds_to_datetime(
+ CreatedSec-1)),
+ Date = httpd_util:rfc1123_date({date(), time()}),
-%%-------------------------------------------------------------------------
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(get, {URL, [{"If-Modified-Since",
+ Mod},
+ {"From","[email protected]"},
+ {"Date", Date}
+ ]}, [], []),
-no_content_204_otp_6982(doc) ->
- ["Test the case that the HTTP 204 no content header"];
-no_content_204_otp_6982(suite) ->
- [];
-no_content_204_otp_6982(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html",
- {ok, {{_,204,_}, [], []}} = httpc:request(URL),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ Mod1 = httpd_util:rfc1123_date(
+ calendar:gregorian_seconds_to_datetime(
+ CreatedSec+1)),
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(get, {URL, [{"If-UnModified-Since",
+ Mod1}
+ ]}, [], []),
-%%-------------------------------------------------------------------------
+ Tag = httpd_util:create_etag(FileInfo),
-missing_CR_otp_7304(doc) ->
- ["Test the case that the HTTP server uses only LF instead of CRLF"
- "as delimitor"];
-missing_CR_otp_7304(suite) ->
- [];
-missing_CR_otp_7304(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html",
- {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL),
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(get, {URL, [{"If-Match",
+ Tag}
+ ]}, [], []),
+ {ok, {{_,200,_}, [_ | _], _}} =
+ httpc:request(get, {URL, [{"If-None-Match",
+ "NotEtag,NeihterEtag"},
+ {"Connection", "Close"}
+ ]}, [], []).
%%-------------------------------------------------------------------------
+headers_dummy() ->
+ ["Test the code for handling headers we do not want/can send "
+ "to a real server. Note it is not logical to send"
+ "all of these headers together, we only want to test that"
+ "the code for handling headers will not crash."].
+headers_dummy(Config) when is_list(Config) ->
-otp_7883_1(doc) ->
- ["OTP-7883-sync"];
-otp_7883_1(suite) ->
- [];
-otp_7883_1(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
-
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
- {error, socket_closed_remotely} = httpc:request(URL),
- DummyServerPid ! stop,
-
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ URL = url(group_name(Config), "/dummy_headers.html", Config),
-otp_7883_2(doc) ->
- ["OTP-7883-async"];
-otp_7883_2(suite) ->
- [];
-otp_7883_2(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipfamily, inet}]),
+ Foo = http_chunk:encode("foobar") ++
+ binary_to_list(http_chunk:encode_last()),
+ FooBar = Foo ++ "\r\n\r\nOther:inets_test\r\n\r\n",
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
- Method = get,
- Request = {URL, []},
- HttpOptions = [],
- Options = [{sync, false}],
- Profile = httpc:default_profile(),
- {ok, RequestId} =
- httpc:request(Method, Request, HttpOptions, Options, Profile),
- ok =
- receive
- {http, {RequestId, {error, socket_closed_remotely}}} ->
- ok
- end,
- DummyServerPid ! stop,
+ UserPasswd = base64:encode_to_string("Alladin:Sesame"),
+ Auth = "Basic " ++ UserPasswd,
- ok = httpc:set_options([{ipfamily, inet6fb4}]),
- ok.
+ %% The dummy server will ignore the headers, we only want to test
+ %% that the client header-handling code. This would not
+ %% be a vaild http-request!
+ {ok, {{_,200,_}, [_ | _], [_|_]}} =
+ httpc:request(post,
+ {URL,
+ [{"Via",
+ "1.0 fred, 1.1 nowhere.com (Apache/1.1)"},
+ {"Warning","1#pseudonym foobar"},
+ {"Vary","*"},
+ {"Upgrade","HTTP/2.0"},
+ {"Pragma", "1#no-cache"},
+ {"Cache-Control", "no-cache"},
+ {"Connection", "close"},
+ {"Date", "Sat, 29 Oct 1994 19:43:31 GMT"},
+ {"Accept", " text/plain; q=0.5, text/html"},
+ {"Accept-Language", "en"},
+ {"Accept-Encoding","chunked"},
+ {"Accept-Charset", "ISO8859-1"},
+ {"Authorization", Auth},
+ {"Expect", "1#100-continue"},
+ {"User-Agent","inets"},
+ {"Transfer-Encoding","chunked"},
+ {"Range", " bytes=0-499"},
+ {"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"},
+ {"If-Match", "*"},
+ {"Content-Type", "text/plain"},
+ {"Content-Encoding", "chunked"},
+ {"Content-Length", "6"},
+ {"Content-Language", "en"},
+ {"Content-Location", "http://www.foobar.se"},
+ {"Content-MD5",
+ "104528739076276072743283077410617235478"},
+ {"Content-Range", "bytes 0-499/1234"},
+ {"Allow", "GET"},
+ {"Proxy-Authorization", Auth},
+ {"Expires", "Sat, 29 Oct 1994 19:43:31 GMT"},
+ {"Upgrade", "HTTP/2.0"},
+ {"Last-Modified", "Sat, 29 Oct 1994 19:43:31 GMT"},
+ {"Trailer","1#User-Agent"}
+ ], "text/plain", FooBar},
+ [], []).
%%-------------------------------------------------------------------------
+remote_socket_close(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/just_close.html", Config),
+ {error, socket_closed_remotely} = httpc:request(URL).
-otp_8154_1(doc) ->
- ["OTP-8154"];
-otp_8154_1(suite) ->
- [];
-otp_8154_1(Config) when is_list(Config) ->
- start_inets(),
- ReqSeqNumServer = start_sequence_number_server(),
- RespSeqNumServer = start_sequence_number_server(),
- {ok, Server, Port} = start_slow_server(RespSeqNumServer),
- Clients = run_clients(105, Port, ReqSeqNumServer),
- %% ok = wait_for_clients(Clients),
- ok = wait4clients(Clients, timer:minutes(3)),
- Server ! shutdown,
- RespSeqNumServer ! shutdown,
- ReqSeqNumServer ! shutdown,
- ok.
-
-start_inets() ->
- inets:start(),
- ok.
-
-
-%% -----------------------------------------------------
-%% A sequence number handler
-%% The purpose is to be able to pair requests with responses.
-
-start_sequence_number_server() ->
- proc_lib:spawn(fun() -> loop_sequence_number(1) end).
-
-loop_sequence_number(N) ->
- receive
- shutdown ->
- ok;
- {From, get_next} ->
- From ! {next_is, N},
- loop_sequence_number(N + 1)
- end.
-
-get_next_sequence_number(SeqNumServer) ->
- SeqNumServer ! {self(), get_next},
- receive {next_is, N} -> N end.
-
-%% -----------------------------------------------------
-%% Client part
-%% Sends requests randomly parallel
-
-run_clients(NumClients, ServerPort, SeqNumServer) ->
- io:format("start clients when"
- "~n NumClients: ~w"
- "~n ServerPort: ~w"
- "~n SeqNumServer: ~w"
- "~n", [NumClients, ServerPort, SeqNumServer]),
- set_random_seed(),
- lists:map(
- fun(Id) ->
- io:format("starting client ~w~n", [Id]),
- Req = f("req~3..0w", [get_next_sequence_number(SeqNumServer)]),
- Url = f(?URL_START ++ "~w/~s", [ServerPort, Req]),
- Pid = proc_lib:spawn(
- fun() ->
- io:format("[~w] client started - "
- "issue request~n", [Id]),
- case httpc:request(Url) of
- {ok, {{_,200,_}, _, Resp}} ->
- io:format("[~w] 200 response: "
- "~p~n", [Id, Resp]),
- case lists:prefix(Req++"->", Resp) of
- true -> exit(normal);
- false -> exit({bad_resp,Req,Resp})
- end;
- {ok, {{_,EC,Reason},_,Resp}} ->
- io:format("[~w] ~w response: "
- "~s~n~s~n",
- [Id, EC, Reason, Resp]),
- exit({bad_resp,Req,Resp});
- Crap ->
- io:format("[~w] bad response: ~p",
- [Id, Crap]),
- exit({bad_resp, Req, Crap})
- end
- end),
- MRef = erlang:monitor(process, Pid),
- timer:sleep(10 + random:uniform(1334)),
- {Id, Pid, MRef}
-
- end,
- lists:seq(1, NumClients)).
-
-%% wait_for_clients(Clients) ->
-%% lists:foreach(
-%% fun({Id, Pid, MRef}) ->
-%% io:format("waiting for client ~w termination~n", [Id]),
-%% receive
-%% {'DOWN', MRef, process, Pid, normal} ->
-%% io:format("waiting for clients: "
-%% "normal exit from ~w (~p)~n",
-%% [Id, Pid]),
-%% ok;
-%% {'DOWN', MRef, process, Pid, Reason} ->
-%% io:format("waiting for clients: "
-%% "unexpected exit from ~w (~p):"
-%% "~n Reason: ~p"
-%% "~n", [Id, Pid, Reason]),
-%% erlang:error(Reason)
-%% end
-%% end,
-%% Clients).
-
-
-wait4clients([], _Timeout) ->
- ok;
-wait4clients(Clients, Timeout) when Timeout > 0 ->
- io:format("wait4clients -> entry with"
- "~n length(Clients): ~w"
- "~n Timeout: ~w"
- "~n", [length(Clients), Timeout]),
- T = t(),
- receive
- {'DOWN', _MRef, process, Pid, normal} ->
- case lists:keysearch(Pid, 2, Clients) of
- {value, {Id, _, _}} ->
- io:format("receive normal exit message "
- "from client ~p (~p)", [Id, Pid]),
- NewClients =
- lists:keydelete(Id, 1, Clients),
- wait4clients(NewClients,
- Timeout - (t() - T));
- false ->
- io:format("receive normal exit message "
- "from unknown process: ~p", [Pid]),
- wait4clients(Clients, Timeout - (t() - T))
- end;
-
- {'DOWN', _MRef, process, Pid, Reason} ->
- case lists:keysearch(Pid, 2, Clients) of
- {value, {Id, _, _}} ->
- io:format("receive bad exit message "
- "from client ~p (~p):"
- "~n ~p", [Id, Pid, Reason]),
- erlang:error({bad_client_termination, Id, Reason});
- false ->
- io:format("receive normal exit message "
- "from unknown process: ~p", [Pid]),
- wait4clients(Clients, Timeout - (t() - T))
- end
-
- after Timeout ->
- erlang:error({client_timeout, Clients})
- end;
-wait4clients(Clients, _) ->
- erlang:error({client_timeout, Clients}).
-
-
-%% Time in milli seconds
-t() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
-
-
-%% -----------------------------------------------------
-%% Webserver part:
-%% Implements a web server that sends responses one character
-%% at a time, with random delays between the characters.
-
-start_slow_server(SeqNumServer) ->
- io:format("start slow server when"
- "~n SeqNumServer: ~w"
- "~n", [SeqNumServer]),
- proc_lib:start(
- erlang, apply, [fun() -> init_slow_server(SeqNumServer) end, []]).
-
-init_slow_server(SeqNumServer) ->
- io:format("[webserver ~w] init slow server"
- "~n", [SeqNumServer]),
- {ok, LSock} = gen_tcp:listen(0, [binary, {packet,0}, {active,true},
- {backlog, 100}]),
- io:format("[webserver ~w] LSock: ~p"
- "~n", [SeqNumServer, LSock]),
- {ok, {_IP, Port}} = inet:sockname(LSock),
- io:format("[webserver ~w] Port: ~w"
- "~n", [SeqNumServer, Port]),
- proc_lib:init_ack({ok, self(), Port}),
- loop_slow_server(LSock, SeqNumServer).
-
-loop_slow_server(LSock, SeqNumServer) ->
- io:format("[webserver ~w] entry with"
- "~n LSock: ~p"
- "~n", [SeqNumServer, LSock]),
- Master = self(),
- Acceptor = proc_lib:spawn(
- fun() -> client_handler(Master, LSock, SeqNumServer) end),
- io:format("[webserver ~w] acceptor started"
- "~n Acceptor: ~p"
- "~n", [SeqNumServer, Acceptor]),
- receive
- {accepted, Acceptor} ->
- io:format("[webserver ~w] accepted"
- "~n", [SeqNumServer]),
- loop_slow_server(LSock, SeqNumServer);
- shutdown ->
- gen_tcp:close(LSock),
- exit(Acceptor, kill)
- end.
-
-
-%% Handle one client connection
-client_handler(Master, LSock, SeqNumServer) ->
- io:format("[acceptor ~w] await accept"
- "~n", [SeqNumServer]),
- {ok, CSock} = gen_tcp:accept(LSock),
- io:format("[acceptor ~w] accepted"
- "~n CSock: ~p"
- "~n", [SeqNumServer, CSock]),
- Master ! {accepted, self()},
- set_random_seed(),
- loop_client(1, CSock, SeqNumServer).
+%%-------------------------------------------------------------------------
-loop_client(N, CSock, SeqNumServer) ->
- %% Await request, don't bother parsing it too much,
- %% assuming the entire request arrives in one packet.
- io:format("[acceptor ~w] await request"
- "~n N: ~p"
- "~n", [SeqNumServer, N]),
+remote_socket_close_async(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/just_close.html", Config), []},
+ Options = [{sync, false}],
+ Profile = httpc:default_profile(),
+ {ok, RequestId} =
+ httpc:request(get, Request, [], Options, Profile),
receive
- {tcp, CSock, Req} ->
- ReqNum = parse_req_num(Req),
- RespSeqNum = get_next_sequence_number(SeqNumServer),
- Response = f("~s->resp~3..0w/~2..0w", [ReqNum, RespSeqNum, N]),
- Txt = f("Slow server (~p) got ~p, answering with ~p",
- [self(), Req, Response]),
- io:format("~s...~n", [Txt]),
- slowly_send_response(CSock, Response),
- case parse_connection_type(Req) of
- keep_alive ->
- io:format("~s...done~n", [Txt]),
- loop_client(N+1, CSock, SeqNumServer);
- close ->
- io:format("~s...done (closing)~n", [Txt]),
- gen_tcp:close(CSock)
- end
+ {http, {RequestId, {error, socket_closed_remotely}}} ->
+ ok
end.
-slowly_send_response(CSock, Answer) ->
- Response = f("HTTP/1.1 200 OK\r\nContent-Length: ~w\r\n\r\n~s",
- [length(Answer), Answer]),
- lists:foreach(
- fun(Char) ->
- timer:sleep(random:uniform(500)),
- gen_tcp:send(CSock, <<Char>>)
- end,
- Response).
+%%-------------------------------------------------------------------------
-parse_req_num(Request) ->
- Opts = [caseless,{capture,all_but_first,list}],
- {match, [ReqNum]} = re:run(Request, "GET /(.*) HTTP", Opts),
- ReqNum.
+stream_to_pid(Config) when is_list(Config) ->
+ ReceiverPid = create_receiver(pid),
+ Receiver = ReceiverPid,
-parse_connection_type(Request) ->
- Opts = [caseless,{capture,all_but_first,list}],
- {match,[CType]} = re:run(Request, "connection: *(keep-alive|close)", Opts),
- case string:to_lower(CType) of
- "close" -> close;
- "keep-alive" -> keep_alive
- end.
+ stream(ReceiverPid, Receiver, Config),
+ stop_receiver(ReceiverPid).
-set_random_seed() ->
- {_, _, Micros} = now(),
- A = erlang:phash2([make_ref(), self(), Micros]),
- random:seed(A, A, A).
+stream_through_fun(Config) when is_list(Config) ->
+ ReceiverPid = create_receiver(function),
+ Receiver = stream_deliver_fun(ReceiverPid),
-f(F, A) -> lists:flatten(io_lib:format(F,A)).
+ stream(ReceiverPid, Receiver, Config),
+ stop_receiver(ReceiverPid).
+stream_through_mfa(Config) when is_list(Config) ->
+ ReceiverPid = create_receiver(mfa),
+ Receiver = {?MODULE, stream_deliver, [mfa, ReceiverPid]},
+ stream(ReceiverPid, Receiver, Config).
%%-------------------------------------------------------------------------
+inet_opts(Config) when is_list(Config) ->
+ MaxSessions = 5,
+ MaxKeepAlive = 10,
+ KeepAliveTimeout = timer:minutes(2),
+ ConnOptions = [{max_sessions, MaxSessions},
+ {max_keep_alive_length, MaxKeepAlive},
+ {keep_alive_timeout, KeepAliveTimeout}],
+ httpc:set_options(ConnOptions),
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ Timeout = timer:seconds(1),
+ ConnTimeout = Timeout + timer:seconds(1),
+ HttpOptions = [{timeout, Timeout}, {connect_timeout, ConnTimeout}],
+ Options0 = [{socket_opts, [{tos, 87},
+ {recbuf, 16#FFFF},
+ {sndbuf, 16#FFFF}]}],
-otp_8106_pid(doc) ->
- ["OTP-8106 - deliver reply info using \"other\" pid"];
-otp_8106_pid(suite) ->
- [];
-otp_8106_pid(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- ReceiverPid = create_receiver(pid),
- Receiver = ReceiverPid,
-
- otp8106(ReceiverPid, Receiver, Config),
+ {ok, {{_,200,_}, [_ | _], ReplyBody0 = [_ | _]}} = httpc:request(get, Request, HttpOptions, Options0),
+ inets_test_lib:check_body(ReplyBody0),
- stop_receiver(ReceiverPid),
-
- ok;
- _ ->
- skip("Failed to start local http-server")
- end.
+ Options1 = [{socket_opts, [{tos, 84},
+ {recbuf, 32#1FFFF},
+ {sndbuf, 32#1FFFF}]}],
+ {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} = httpc:request(get, Request, [], Options1),
+ inets_test_lib:check_body(ReplyBody1).
+%%-------------------------------------------------------------------------
+port_in_host_header(Config) when is_list(Config) ->
-otp_8106_fun(doc) ->
- ["OTP-8106 - deliver reply info using fun"];
-otp_8106_fun(suite) ->
- [];
-otp_8106_fun(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- ReceiverPid = create_receiver(function),
- Receiver = otp_8106_deliver_fun(ReceiverPid),
-
- otp8106(ReceiverPid, Receiver, Config),
+ Request = {url(group_name(Config), "/ensure_host_header_with_port.html", Config), []},
+ {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []),
+ inets_test_lib:check_body(Body).
- stop_receiver(ReceiverPid),
-
- ok;
- _ ->
- skip("Failed to start local http-server")
+%%-------------------------------------------------------------------------
+timeout_memory_leak() ->
+ [{doc, "Check OTP-8739"}].
+timeout_memory_leak(Config) when is_list(Config) ->
+ {_DummyServerPid, Port} = otp_8739_dummy_server(),
+ {ok,Host} = inet:gethostname(),
+ Request = {?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ "/dummy.html", []},
+ case httpc:request(get, Request, [{connect_timeout, 500}, {timeout, 1}], [{sync, true}]) of
+ {error, timeout} ->
+ %% And now we check the size of the handler db
+ Info = httpc:info(),
+ ct:print("Info: ~p", [Info]),
+ {value, {handlers, Handlers}} =
+ lists:keysearch(handlers, 1, Info),
+ case Handlers of
+ [] ->
+ ok;
+ _ ->
+ ct:fail({unexpected_handlers, Handlers})
+ end;
+ Unexpected ->
+ ct:fail({unexpected, Unexpected})
end.
+%%--------------------------------------------------------------------
-otp_8106_mfa(doc) ->
- ["OTP-8106 - deliver reply info using mfa callback"];
-otp_8106_mfa(suite) ->
- [];
-otp_8106_mfa(Config) when is_list(Config) ->
- case ?config(local_server, Config) of
- ok ->
- ReceiverPid = create_receiver(mfa),
- Receiver = {?MODULE, otp_8106_deliver, [mfa, ReceiverPid]},
-
- otp8106(ReceiverPid, Receiver, Config),
-
- stop_receiver(ReceiverPid),
-
- ok;
- _ ->
- skip("Failed to start local http-server")
- end.
-
-
- otp8106(ReceiverPid, Receiver, Config) ->
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- Request = {URL, []},
- HTTPOptions = [],
- Options = [{sync, false}, {receiver, Receiver}],
+wait_for_whole_response() ->
+ [{doc, "Check OTP-8154"}].
+wait_for_whole_response(Config) when is_list(Config) ->
- {ok, RequestId} =
- httpc:request(get, Request, HTTPOptions, Options),
+ ReqSeqNumServer = start_sequence_number_server(),
+ RespSeqNumServer = start_sequence_number_server(),
+ {ok, Server, Port} = start_slow_server(RespSeqNumServer),
+ Clients = run_clients(105, Port, ReqSeqNumServer),
+ ok = wait4clients(Clients, timer:minutes(3)),
+ Server ! shutdown,
+ RespSeqNumServer ! shutdown,
+ ReqSeqNumServer ! shutdown.
- Body =
- receive
+%%--------------------------------------------------------------------
+%% Internal Functions ------------------------------------------------
+%%--------------------------------------------------------------------
+stream(ReceiverPid, Receiver, Config) ->
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ Options = [{sync, false}, {receiver, Receiver}],
+ {ok, RequestId} =
+ httpc:request(get, Request, [], Options),
+ Body =
+ receive
{reply, ReceiverPid, {RequestId, {{_, 200, _}, _, B}}} ->
B;
{reply, ReceiverPid, Msg} ->
- tsf(Msg);
+ ct:fail(Msg);
{bad_reply, ReceiverPid, Msg} ->
- tsf(Msg)
+ ct:fail(Msg)
end,
- inets_test_lib:check_body(binary_to_list(Body)),
- ok.
-
+ inets_test_lib:check_body(binary_to_list(Body)).
create_receiver(Type) ->
- Parent = self(),
+ Parent = self(),
Receiver = fun() -> receiver(Type, Parent) end,
spawn_link(Receiver).
@@ -3079,8 +941,7 @@ stop_receiver(Pid) ->
receiver(Type, Parent) ->
receive
{stop, Parent} ->
- exit(normal);
-
+ ok;
{http, ReplyInfo} when (Type =:= pid) ->
Parent ! {reply, self(), ReplyInfo},
receiver(Type, Parent);
@@ -3088,258 +949,116 @@ receiver(Type, Parent) ->
{Type, ReplyInfo} ->
Parent ! {reply, self(), ReplyInfo},
receiver(Type, Parent);
-
+
Crap ->
Parent ! {reply, self(), {bad_reply, Crap}},
receiver(Type, Parent)
end.
+stream_deliver_fun(ReceiverPid) ->
+ fun(ReplyInfo) -> stream_deliver(ReplyInfo, function, ReceiverPid) end.
-otp_8106_deliver_fun(ReceiverPid) ->
- fun(ReplyInfo) -> otp_8106_deliver(ReplyInfo, function, ReceiverPid) end.
-
-otp_8106_deliver(ReplyInfo, Type, ReceiverPid) ->
+stream_deliver(ReplyInfo, Type, ReceiverPid) ->
ReceiverPid ! {Type, ReplyInfo},
ok.
+stream_test(Request, To) ->
+ {ok, {{_,200,_}, [_ | _], Body}} =
+ httpc:request(get, Request, [], []),
+ {ok, RequestId} =
+ httpc:request(get, Request, [], [{sync, false}, To]),
+ StreamedBody =
+ receive
+ {http, {RequestId, stream_start, _Headers}} ->
+ receive_streamed_body(RequestId, <<>>);
+ {http, {RequestId, stream_start, _Headers, Pid}} ->
+ receive_streamed_body(RequestId, <<>>, Pid);
+ {http, Msg} ->
+ ct:fail(Msg)
+ end,
-%%-------------------------------------------------------------------------
-
-otp_8056(doc) ->
- "OTP-8056";
-otp_8056(suite) ->
- [];
-otp_8056(Config) when is_list(Config) ->
- Method = get,
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- Request = {URL, []},
- HTTPOptions = [],
- Options1 = [{sync, true}, {stream, {self, once}}],
- Options2 = [{sync, true}, {stream, self}],
- {error, streaming_error} = httpc:request(Method, Request,
- HTTPOptions, Options1),
- tsp("request 1 failed as expected"),
- {error, streaming_error} = httpc:request(Method, Request,
- HTTPOptions, Options2),
- tsp("request 2 failed as expected"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-otp_8352(doc) ->
- "OTP-8352";
-otp_8352(suite) ->
- [];
-otp_8352(Config) when is_list(Config) ->
- tsp("otp_8352 -> entry with"
- "~n Config: ~p", [Config]),
- case ?config(local_server, Config) of
- ok ->
- tsp("local-server running"),
-
- tsp("initial profile info(1): ~p", [httpc:info()]),
-
- MaxSessions = 5,
- MaxKeepAlive = 10,
- KeepAliveTimeout = timer:minutes(2),
- ConnOptions = [{max_sessions, MaxSessions},
- {max_keep_alive_length, MaxKeepAlive},
- {keep_alive_timeout, KeepAliveTimeout}],
- httpc:set_options(ConnOptions),
-
- Method = get,
- Port = ?config(local_port, Config),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- Request = {URL, []},
- Timeout = timer:seconds(1),
- ConnTimeout = Timeout + timer:seconds(1),
- HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}],
- Options1 = [{socket_opts, [{tos, 87},
- {recbuf, 16#FFFF},
- {sndbuf, 16#FFFF}]}],
- case httpc:request(Method, Request, HttpOptions1, Options1) of
- {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} ->
- %% equivaliant to httpc:request(get, {URL, []}, [], []),
- inets_test_lib:check_body(ReplyBody1);
- {ok, UnexpectedReply1} ->
- tsf({unexpected_reply, UnexpectedReply1});
- {error, _} = Error1 ->
- tsf({bad_reply, Error1})
- end,
-
- tsp("profile info (2): ~p", [httpc:info()]),
-
- HttpOptions2 = [],
- Options2 = [{socket_opts, [{tos, 84},
- {recbuf, 32#1FFFF},
- {sndbuf, 32#1FFFF}]}],
- case httpc:request(Method, Request, HttpOptions2, Options2) of
- {ok, {{_,200,_}, [_ | _], ReplyBody2 = [_ | _]}} ->
- %% equivaliant to httpc:request(get, {URL, []}, [], []),
- inets_test_lib:check_body(ReplyBody2);
- {ok, UnexpectedReply2} ->
- tsf({unexpected_reply, UnexpectedReply2});
- {error, _} = Error2 ->
- tsf({bad_reply, Error2})
- end,
- tsp("profile info (3): ~p", [httpc:info()]),
- ok;
-
- _ ->
- skip("Failed to start local http-server")
- end.
-
-
-%%-------------------------------------------------------------------------
-
-otp_8371(doc) ->
- ["OTP-8371"];
-otp_8371(suite) ->
- [];
-otp_8371(Config) when is_list(Config) ->
- ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
- {DummyServerPid, Port} = dummy_server(ipv4),
-
- URL = ?URL_START ++ integer_to_list(Port) ++
- "/ensure_host_header_with_port.html",
-
- case httpc:request(get, {URL, []}, [], []) of
- {ok, Result} ->
- case Result of
- {{_, 200, _}, _Headers, Body} ->
- tsp("expected response with"
- "~n Body: ~p", [Body]),
- ok;
- {StatusLine, Headers, Body} ->
- tsp("expected response with"
- "~n StatusLine: ~p"
- "~n Headers: ~p"
- "~n Body: ~p", [StatusLine, Headers, Body]),
- tsf({unexpected_result,
- [{status_line, StatusLine},
- {headers, Headers},
- {body, Body}]});
- _ ->
- tsf({unexpected_result, Result})
- end;
- Error ->
- tsf({request_failed, Error})
- end,
+ Body == binary_to_list(StreamedBody).
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipv6, enabled}]),
+url(http, End, Config) ->
+ Port = ?config(port, Config),
+ {ok,Host} = inet:gethostname(),
+ ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
+url(https, End, Config) ->
+ Port = ?config(port, Config),
+ {ok,Host} = inet:gethostname(),
+ ?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
+url(sim_http, End, Config) ->
+ url(http, End, Config);
+url(sim_https, End, Config) ->
+ url(https, End, Config).
+url(http, UserInfo, End, Config) ->
+ Port = ?config(port, Config),
+ ?URL_START ++ UserInfo ++ integer_to_list(Port) ++ End;
+url(https, UserInfo, End, Config) ->
+ Port = ?config(port, Config),
+ ?TLS_URL_START ++ UserInfo ++ integer_to_list(Port) ++ End;
+url(sim_http, UserInfo, End, Config) ->
+ url(http, UserInfo, End, Config);
+url(sim_https, UserInfo, End, Config) ->
+ url(https, UserInfo, End, Config).
+
+group_name(Config) ->
+ GroupProp = ?config(tc_group_properties, Config),
+ proplists:get_value(name, GroupProp).
+
+server_start(sim_http, _) ->
+ Inet = inet_version(),
+ ok = httpc:set_options([{ipfamily, Inet}]),
+ {_Pid, Port} = dummy_server(Inet),
+ Port;
+
+server_start(sim_https, SslConfig) ->
+ Inet = inet_version(),
+ ok = httpc:set_options([{ipfamily, Inet}]),
+ {_Pid, Port} = dummy_server(ssl, Inet, SslConfig),
+ Port;
+
+server_start(_, HttpdConfig) ->
+ {ok, Pid} = inets:start(httpd, HttpdConfig),
+ Serv = inets:services_info(),
+ {value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv),
+ proplists:get_value(port, Info).
+
+server_config(http, Config) ->
+ ServerRoot = ?config(server_root, Config),
+ [{port, 0},
+ {server_name,"httpc_test"},
+ {server_root, ServerRoot},
+ {document_root, ?config(doc_root, Config)},
+ {bind_address, any},
+ {ipfamily, inet_version()},
+ {mime_type, "text/plain"},
+ {script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}}
+ ];
+
+server_config(https, Config) ->
+ [{socket_type, {essl, ssl_config(Config)}} | server_config(http, Config)];
+server_config(sim_https, Config) ->
+ ssl_config(Config);
+server_config(_, _) ->
+ [].
+
+start_apps(https) ->
+ inets_test_lib:start_apps([crypto, public_key, ssl]);
+start_apps(_) ->
ok.
-
-%%-------------------------------------------------------------------------
-
-otp_8739(doc) ->
- ["OTP-8739"];
-otp_8739(suite) ->
- [];
-otp_8739(Config) when is_list(Config) ->
- {_DummyServerPid, Port} = otp_8739_dummy_server(),
- URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- Method = get,
- Request = {URL, []},
- HttpOptions = [{connect_timeout, 500}, {timeout, 1}],
- Options = [{sync, true}],
- case httpc:request(Method, Request, HttpOptions, Options) of
- {error, timeout} ->
- %% And now we check the size of the handler db
- Info = httpc:info(),
- tsp("Info: ~p", [Info]),
- {value, {handlers, Handlers}} =
- lists:keysearch(handlers, 1, Info),
- case Handlers of
- [] ->
- ok;
- _ ->
- tsf({unexpected_handlers, Handlers})
- end;
- Unexpected ->
- tsf({unexpected, Unexpected})
- end.
-
-
-otp_8739_dummy_server() ->
- Parent = self(),
- Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end),
- receive
- {port, Port} ->
- {Pid, Port}
- end.
-
-otp_8739_dummy_server_init(Parent) ->
- {ok, ListenSocket} =
- gen_tcp:listen(0, [binary, inet, {packet, 0},
- {reuseaddr,true},
- {active, false}]),
- {ok, Port} = inet:port(ListenSocket),
- Parent ! {port, Port},
- otp_8739_dummy_server_main(Parent, ListenSocket).
-
-otp_8739_dummy_server_main(_Parent, ListenSocket) ->
- case gen_tcp:accept(ListenSocket) of
- {ok, Sock} ->
- %% Ignore the request, and simply wait for the socket to close
- receive
- {tcp_closed, Sock} ->
- (catch gen_tcp:close(ListenSocket)),
- exit(normal);
- {tcp_error, Sock, Reason} ->
- tsp("socket error: ~p", [Reason]),
- (catch gen_tcp:close(ListenSocket)),
- exit(normal)
- after 10000 ->
- %% Just in case
- (catch gen_tcp:close(Sock)),
- (catch gen_tcp:close(ListenSocket)),
- exit(timeout)
- end;
- Error ->
- exit(Error)
- end.
-
-
-%%-------------------------------------------------------------------------
-
-initial_server_connect(doc) ->
- ["If this test cases times out the init of httpc_handler process is"
- "blocking the manager/client process (implementation dependent which) but nither"
- "should be blocked."];
-initial_server_connect(suite) ->
- [];
-initial_server_connect(Config) when is_list(Config) ->
+ssl_config(Config) ->
DataDir = ?config(data_dir, Config),
- ok = httpc:set_options([{ipfamily, inet}]),
-
- CertFile = filename:join(DataDir, "ssl_server_cert.pem"),
- SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
-
- {DummyServerPid, Port} = dummy_ssl_server_hang(self(), ipv4, SSLOptions),
-
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/index.html",
-
- httpc:request(get, {URL, []}, [{ssl,{essl,[]}}], [{sync, false}]),
-
- [{session_cookies,[]}] = httpc:which_cookies(),
+ [{certfile, filename:join(DataDir, "ssl_server_cert.pem")},
+ {verify, verify_none}
+ ].
- DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]).
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
- ConfDir = filename:join(ServerRoot, "conf"),
CgiDir = filename:join(ServerRoot, "cgi-bin"),
ok = file:make_dir(ServerRoot),
ok = file:make_dir(DocRoot),
- ok = file:make_dir(ConfDir),
ok = file:make_dir(CgiDir),
{ok, Files} = file:list_dir(DataDir),
@@ -3362,86 +1081,41 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
end,
inets_test_lib:copy_file(Cgi, DataDir, CgiDir),
- inets_test_lib:copy_file("mime.types", DataDir, ConfDir).
-
-create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot,
- SSLDir) ->
- MaxHdrSz = io_lib:format("~p", [256]),
- MaxHdrAct = io_lib:format("~p", [close]),
- SSL =
- case ComType of
- ssl ->
- [cline(["SSLCertificateFile ",
- filename:join(SSLDir, "ssl_server_cert.pem")]),
- cline(["SSLCertificateKeyFile ",
- filename:join(SSLDir, "ssl_server_cert.pem")]),
- cline(["SSLVerifyClient 0"])];
- _ ->
- []
- end,
+ AbsCgi = filename:join([CgiDir, Cgi]),
+ {ok, FileInfo} = file:read_file_info(AbsCgi),
+ ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}).
- Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
- " mod_include mod_dir mod_get mod_head"
- " mod_log mod_disk_log mod_trace",
-
- %% BindAddress = "*|inet", % Force the use of IPv4
- BindAddress = "*", % This corresponds to using IpFamily inet6fb4
-
- HttpConfig = [
- cline(["BindAddress ", BindAddress]),
- cline(["Port ", integer_to_list(Port)]),
- cline(["ServerName ", "httpc_test"]),
- cline(["SocketType ", atom_to_list(ComType)]),
- cline([Mod_order]),
- cline(["ServerRoot ", ServerRoot]),
- cline(["DocumentRoot ", DocRoot]),
- cline(["MaxHeaderSize ",MaxHdrSz]),
- cline(["MaxHeaderAction ",MaxHdrAct]),
- cline(["DirectoryIndex ", "index.html "]),
- cline(["DefaultType ", "text/plain"]),
- cline(["ScriptAlias /cgi-bin/ ",
- filename:join(ServerRoot, "cgi-bin"), "/"]),
- SSL],
- ConfigFile = filename:join([PrivDir,FileName]),
- {ok, Fd} = file:open(ConfigFile, [write]),
- ok = file:write(Fd, lists:flatten(HttpConfig)),
- ok = file:close(Fd).
-
-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}} ->
- receive_streamed_body(RequestId,
- <<Body/binary, BinBodyPart/binary>>);
- {http, {RequestId, stream_end, _Headers}} ->
- Body;
- {http, Msg} ->
- tsf(Msg)
- end.
+keep_alive_requests(Request, Profile) ->
+ {ok, RequestIdA0} =
+ httpc:request(get, Request, [], [{sync, false}], Profile),
+ {ok, RequestIdA1} =
+ httpc:request(get, Request, [], [{sync, false}], Profile),
+ {ok, RequestIdA2} =
+ httpc:request(get, Request, [], [{sync, false}], Profile),
-receive_streamed_body(RequestId, Body, Pid) ->
- httpc:stream_next(Pid),
- test_server:format("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]),
- receive
- {http, {RequestId, stream, BinBodyPart}} ->
- receive_streamed_body(RequestId,
- <<Body/binary, BinBodyPart/binary>>,
- Pid);
- {http, {RequestId, stream_end, _Headers}} ->
- Body;
- {http, Msg} ->
- tsf(Msg)
+ receive_replys([RequestIdA0, RequestIdA1, RequestIdA2]),
+
+ {ok, RequestIdB0} =
+ httpc:request(get, Request, [], [{sync, false}], Profile),
+ {ok, RequestIdB1} =
+ httpc:request(get, Request, [], [{sync, false}], Profile),
+ {ok, RequestIdB2} =
+ httpc:request(get, Request, [], [{sync, false}], Profile),
+
+ ok = httpc:cancel_request(RequestIdB1, Profile),
+ ct:print("Cancel ~p~n", [RequestIdB1]),
+ receive_replys([RequestIdB0, RequestIdB2]).
+
+
+receive_replys([]) ->
+ ok;
+receive_replys([ID|IDs]) ->
+ receive
+ {http, {ID, {{_, 200, _}, [_|_], _}}} ->
+ receive_replys(IDs);
+ {http, {Other, {{_, 200, _}, [_|_], _}}} ->
+ ct:fail({recived_canceld_id, Other})
end.
%% Perform a synchronous stop
@@ -3452,55 +1126,46 @@ dummy_server_stop(Pid) ->
ok
end.
-dummy_server(IpV) ->
- dummy_server(self(), ip_comm, IpV, []).
+inet_version() ->
+ inet. %% Just run inet for now
+ %% case gen_tcp:listen(0,[inet6]) of
+ %% {ok, S} ->
+ %% gen_tcp:close(S),
+ %% inet6;
+ %% _ ->
+ %% inet
+ %%end.
-dummy_server(SocketType, IpV, Extra) ->
- dummy_server(self(), SocketType, IpV, Extra).
+dummy_server(Inet) ->
+ dummy_server(self(), ip_comm, Inet, []).
-dummy_server(Caller, SocketType, IpV, Extra) ->
- Args = [Caller, SocketType, IpV, Extra],
+dummy_server(SocketType, Inet, Extra) ->
+ dummy_server(self(), SocketType, Inet, Extra).
+
+dummy_server(Caller, SocketType, Inet, Extra) ->
+ Args = [Caller, SocketType, Inet, Extra],
Pid = spawn(httpc_SUITE, dummy_server_init, Args),
receive
{port, Port} ->
{Pid, Port}
end.
-dummy_server_init(Caller, ip_comm, IpV, _) ->
+dummy_server_init(Caller, ip_comm, Inet, _) ->
BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}],
- {ok, ListenSocket} =
- case IpV of
- ipv4 ->
- tsp("ip_comm ipv4 listen", []),
- gen_tcp:listen(0, [inet | BaseOpts]);
- ipv6 ->
- tsp("ip_comm ipv6 listen", []),
- gen_tcp:listen(0, [inet6 | BaseOpts])
- end,
+ {ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
{ok, Port} = inet:port(ListenSocket),
- tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]),
Caller ! {port, Port},
dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
[], ListenSocket);
-dummy_server_init(Caller, essl, IpV, SSLOptions) ->
- BaseOpts = [{ssl_imp, new},
- {backlog, 128}, binary, {reuseaddr,true}, {active, false} |
+
+dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
+ BaseOpts = [binary, {reuseaddr,true}, {active, false} |
SSLOptions],
- dummy_ssl_server_init(Caller, BaseOpts, IpV).
-
-dummy_ssl_server_init(Caller, BaseOpts, IpV) ->
- {ok, ListenSocket} =
- case IpV of
- ipv4 ->
- tsp("dummy_ssl_server_init -> ssl ipv4 listen", []),
- ssl:listen(0, [inet | BaseOpts]);
- ipv6 ->
- tsp("dummy_ssl_server_init -> ssl ipv6 listen", []),
- ssl:listen(0, [inet6 | BaseOpts])
- end,
- tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]),
+ dummy_ssl_server_init(Caller, BaseOpts, Inet).
+
+dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
+ {ok, ListenSocket} = ssl:listen(0, [Inet | BaseOpts]),
{ok, {_, Port}} = ssl:sockname(ListenSocket),
- tsp("dummy_ssl_server_init -> Port: ~p", [Port]),
Caller ! {port, Port},
dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
[], ListenSocket).
@@ -3508,85 +1173,56 @@ dummy_ssl_server_init(Caller, BaseOpts, IpV) ->
dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) ->
receive
stop ->
- tsp("dummy_ipcomm_server_loop -> stop handlers", []),
lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
{stop, From} ->
- tsp("dummy_ipcomm_server_loop -> "
- "stop command from ~p for handlers (~p)", [From, Handlers]),
Stopper = fun(Handler) -> Handler ! stop end,
lists:foreach(Stopper, Handlers),
From ! {stopped, self()}
after 0 ->
- tsp("dummy_ipcomm_server_loop -> await accept", []),
{ok, Socket} = gen_tcp:accept(ListenSocket),
- tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]),
HandlerPid = dummy_request_handler(MFA, Socket),
- tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]),
gen_tcp:controlling_process(Socket, HandlerPid),
- tsp("dummy_ipcomm_server_loop -> "
- "control transfered to handler", []),
HandlerPid ! ipcomm_controller,
- tsp("dummy_ipcomm_server_loop -> "
- "handler informed about control transfer", []),
dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers],
- ListenSocket)
+ ListenSocket)
end.
dummy_ssl_server_loop(MFA, Handlers, ListenSocket) ->
receive
stop ->
- tsp("dummy_ssl_server_loop -> stop handlers", []),
lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
{stop, From} ->
- tsp("dummy_ssl_server_loop -> "
- "stop command from ~p for handlers (~p)", [From, Handlers]),
Stopper = fun(Handler) -> Handler ! stop end,
lists:foreach(Stopper, Handlers),
From ! {stopped, self()}
after 0 ->
- tsp("dummy_ssl_server_loop -> await accept", []),
{ok, Socket} = ssl:transport_accept(ListenSocket),
- tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]),
HandlerPid = dummy_request_handler(MFA, Socket),
- tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]),
ssl:controlling_process(Socket, HandlerPid),
- tsp("dummy_ssl_server_loop -> control transfered to handler", []),
HandlerPid ! ssl_controller,
- tsp("dummy_ssl_server_loop -> "
- "handler informed about control transfer", []),
dummy_ssl_server_loop(MFA, [HandlerPid | Handlers],
ListenSocket)
end.
dummy_request_handler(MFA, Socket) ->
- tsp("spawn request handler", []),
spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]).
dummy_request_handler_init(MFA, Socket) ->
SockType =
receive
ipcomm_controller ->
- tsp("dummy_request_handler_init -> "
- "received ip_comm controller - activate", []),
inet:setopts(Socket, [{active, true}]),
ip_comm;
ssl_controller ->
- tsp("dummy_request_handler_init -> "
- "received ssl controller - activate", []),
ssl:setopts(Socket, [{active, true}]),
ssl
end,
dummy_request_handler_loop(MFA, SockType, Socket).
dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) ->
- tsp("dummy_request_handler_loop -> entry with"
- "~n Module: ~p"
- "~n Function: ~p"
- "~n Args: ~p", [Module, Function, Args]),
receive
{Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) ->
- tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]),
- case handle_request(Module, Function, [Data | Args], Socket, Proto) of
+ case handle_request(Module, Function, [Data | Args], Socket) of
stop when Proto =:= tcp ->
gen_tcp:close(Socket);
stop when Proto =:= ssl ->
@@ -3600,49 +1236,26 @@ dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) ->
ssl:close(Socket)
end.
-
-mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end;
-mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end.
-
-mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end;
-mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end.
-
-handle_request(Module, Function, Args, Socket, Proto) ->
- Close = mk_close(Proto),
- Send = mk_send(Proto),
- handle_request(Module, Function, Args, Socket, Close, Send).
-
-handle_request(Module, Function, Args, Socket, Close, Send) ->
- tsp("handle_request -> entry with"
- "~n Module: ~p"
- "~n Function: ~p"
- "~n Args: ~p", [Module, Function, Args]),
+handle_request(Module, Function, Args, Socket) ->
case Module:Function(Args) of
{ok, Result} ->
- tsp("handle_request -> ok"
- "~n Result: ~p", [Result]),
- case (catch handle_http_msg(Result, Socket, Close, Send)) of
+ case handle_http_msg(Result, Socket) of
stop ->
stop;
<<>> ->
- tsp("handle_request -> empty data"),
{httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]};
Data ->
handle_request(httpd_request, parse,
- [Data |[?HTTP_MAX_HEADER_SIZE]], Socket,
- Close, Send)
+ [Data |[?HTTP_MAX_HEADER_SIZE]], Socket)
end;
NewMFA ->
- tsp("handle_request -> "
- "~n NewMFA: ~p", [NewMFA]),
NewMFA
end.
-handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) ->
- tsp("handle_http_msg -> entry with: "
- "~n RelUri: ~p"
- "~n Headers: ~p"
- "~n Body: ~p", [RelUri, Headers, Body]),
+handle_http_msg({Method, RelUri, _, {_, Headers}, Body}, Socket) ->
+
+ ct:print("Request: ~p ~p", [Method, RelUri]),
+
NextRequest =
case RelUri of
"/dummy_headers.html" ->
@@ -3663,217 +1276,69 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) ->
end
end,
- tsp("handle_http_msg -> NextRequest: ~p", [NextRequest]),
case (catch ets:lookup(cookie, cookies)) of
[{cookies, true}]->
- tsp("handle_http_msg -> check cookies ~p", []),
check_cookie(Headers);
_ ->
ok
end,
-
+
+ {ok, {_, Port}} = sockname(Socket),
+
+
DefaultResponse = "HTTP/1.1 200 ok\r\n" ++
"Content-Length:32\r\n\r\n"
"<HTML><BODY>foobar</BODY></HTML>",
- Msg =
- case RelUri of
- "/just_close.html" ->
- close;
- "/no_content.html" ->
- "HTTP/1.0 204 No Content\r\n\r\n";
- "/no_headers.html" ->
- "HTTP/1.0 200 OK\r\n\r\nTEST";
- "/ensure_host_header_with_port.html" ->
- %% tsp("handle_http_msg -> validate host with port"),
- case ensure_host_header_with_port(Headers) of
- true ->
- B =
- "<HTML><BODY>" ++
- "host with port" ++
- "</BODY></HTML>",
- Len = integer_to_list(length(B)),
- "HTTP/1.1 200 ok\r\n" ++
- "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B;
- false ->
- B =
- "<HTML><BODY>" ++
- "Internal Server Error - host without port" ++
- "</BODY></HTML>",
- Len = integer_to_list(length(B)),
- "HTTP/1.1 500 Internal Server Error\r\n" ++
- "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B
- end;
- "/300.html" ->
- NewUri = ?URL_START ++
- integer_to_list(?IP_PORT) ++ "/dummy.html",
- "HTTP/1.1 300 Multiple Choices\r\n" ++
- "Location:" ++ NewUri ++ "\r\n" ++
- "Content-Length:0\r\n\r\n";
- "/301.html" ->
- NewUri = ?URL_START ++
- integer_to_list(?IP_PORT) ++ "/dummy.html",
- "HTTP/1.1 301 Moved Permanently\r\n" ++
- "Location:" ++ NewUri ++ "\r\n" ++
- "Content-Length:80\r\n\r\n" ++
- "<HTML><BODY><a href=" ++ NewUri ++
- ">New place</a></BODY></HTML>";
- "/302.html" ->
- NewUri = ?URL_START ++
- integer_to_list(?IP_PORT) ++ "/dummy.html",
- "HTTP/1.1 302 Found \r\n" ++
- "Location:" ++ NewUri ++ "\r\n" ++
- "Content-Length:80\r\n\r\n" ++
- "<HTML><BODY><a href=" ++ NewUri ++
- ">New place</a></BODY></HTML>";
- "/307.html" ->
- NewUri = ?URL_START ++
- integer_to_list(?IP_PORT) ++ "/dummy.html",
- "HTTP/1.1 307 Temporary Rediect \r\n" ++
- "Location:" ++ NewUri ++ "\r\n" ++
- "Content-Length:80\r\n\r\n" ++
- "<HTML><BODY><a href=" ++ NewUri ++
- ">New place</a></BODY></HTML>";
- "/500.html" ->
- "HTTP/1.1 500 Internal Server Error\r\n" ++
- "Content-Length:47\r\n\r\n" ++
- "<HTML><BODY>Internal Server Error</BODY></HTML>";
- "/503.html" ->
- case ets:lookup(unavailable, 503) of
- [{503, unavailable}] ->
- ets:insert(unavailable, {503, available}),
- "HTTP/1.1 503 Service Unavailable\r\n" ++
- "Retry-After:5\r\n" ++
- "Content-Length:47\r\n\r\n" ++
- "<HTML><BODY>Internal Server Error</BODY></HTML>";
- [{503, available}] ->
- DefaultResponse;
- [{503, long_unavailable}] ->
- "HTTP/1.1 503 Service Unavailable\r\n" ++
- "Retry-After:120\r\n" ++
- "Content-Length:47\r\n\r\n" ++
- "<HTML><BODY>Internal Server Error</BODY></HTML>"
- end;
- "/redirectloop.html" -> %% Create a potential endless loop!
- {ok, Port} = inet:port(Socket),
- NewUri = ?URL_START ++
- integer_to_list(Port) ++ "/redirectloop.html",
- "HTTP/1.1 300 Multiple Choices\r\n" ++
- "Location:" ++ NewUri ++ "\r\n" ++
- "Content-Length:0\r\n\r\n";
- "/userinfo.html" ->
- Challange = "HTTP/1.1 401 Unauthorized \r\n" ++
- "WWW-Authenticate:Basic" ++"\r\n" ++
- "Content-Length:0\r\n\r\n",
- case auth_header(Headers) of
- {ok, Value} ->
- handle_auth(Value, Challange, DefaultResponse);
- _ ->
- Challange
- end;
- "/dummy_headers.html" ->
- %% The client will only care about the Transfer-Encoding
- %% header the rest of these headers are left to the
- %% user to evaluate. This is not a valid response
- %% it only tests that the header handling code works.
- Head = "HTTP/1.1 200 ok\r\n" ++
- "Content-Length:32\r\n" ++
- "Pragma:1#no-cache\r\n" ++
- "Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++
- "Warning:1#pseudonym foobar\r\n" ++
- "Vary:*\r\n" ++
- "Trailer:Other:inets_test\r\n" ++
- "Upgrade:HTTP/2.0\r\n" ++
- "Age:4711\r\n" ++
- "Transfer-Encoding:chunked\r\n" ++
- "Content-Encoding:foo\r\n" ++
- "Content-Language:en\r\n" ++
- "Content-Location:http://www.foobar.se\r\n" ++
- "Content-MD5:104528739076276072743283077410617235478\r\n"
- ++
- "Content-Range:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++
- "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++
- "Proxy-Authenticate:#1Basic" ++
- "\r\n\r\n",
- Send(Socket, Head),
- Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- Send(Socket, http_chunk:encode("obar</BODY></HTML>")),
- http_chunk:encode_last();
- "/capital_transfer_encoding.html" ->
- Head = "HTTP/1.1 200 ok\r\n" ++
- "Transfer-Encoding:Chunked\r\n\r\n",
- Send(Socket, Head),
- Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- Send(Socket, http_chunk:encode("obar</BODY></HTML>")),
- http_chunk:encode_last();
- "/cookie.html" ->
- "HTTP/1.1 200 ok\r\n" ++
- "set-cookie:" ++ "test_cookie=true; path=/;" ++
- "max-age=60000\r\n" ++
- "Content-Length:32\r\n\r\n"++
- "<HTML><BODY>foobar</BODY></HTML>";
- "/missing_crlf.html" ->
- "HTTP/1.1 200 ok" ++
- "Content-Length:32\r\n" ++
- "<HTML><BODY>foobar</BODY></HTML>";
- "/wrong_statusline.html" ->
- "ok 200 HTTP/1.1\r\n\r\n" ++
- "Content-Length:32\r\n\r\n" ++
- "<HTML><BODY>foobar</BODY></HTML>";
- "/once_chunked.html" ->
- Head = "HTTP/1.1 200 ok\r\n" ++
- "Transfer-Encoding:Chunked\r\n\r\n",
- Send(Socket, Head),
- Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- Send(Socket,
- http_chunk:encode("obar</BODY></HTML>")),
- http_chunk:encode_last();
- "/once.html" ->
- Head = "HTTP/1.1 200 ok\r\n" ++
- "Content-Length:32\r\n\r\n",
- Send(Socket, Head),
- Send(Socket, "<HTML><BODY>fo"),
- test_server:sleep(1000),
- Send(Socket, "ob"),
- test_server:sleep(1000),
- Send(Socket, "ar</BODY></HTML>");
- "/invalid_http.html" ->
- "HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++
- "Transfer-Encoding:chunked\r\n\r\n";
- "/missing_reason_phrase.html" ->
- "HTTP/1.1 200\r\n" ++
- "Content-Length: 32\r\n\r\n"
- "<HTML><BODY>foobar</BODY></HTML>";
- "/missing_CR.html" ->
- "HTTP/1.1 200 ok\n" ++
- "Content-Length:32\r\n\n"
- "<HTML><BODY>foobar</BODY></HTML>";
- _ ->
- DefaultResponse
- end,
-
- tsp("handle_http_msg -> Msg: ~p", [Msg]),
+ Msg = handle_uri(Method,RelUri, Port, Headers, Socket, DefaultResponse),
+
case Msg of
ok ->
- %% Previously, this resulted in an {error, einval}. Now what?
ok;
close ->
%% Nothing to send, just close
- Close(Socket);
+ close(Socket);
_ when is_list(Msg) orelse is_binary(Msg) ->
- Send(Socket, Msg)
+ case Msg of
+ [] ->
+ ct:print("Empty Msg", []);
+ _ ->
+ ct:print("Response: ~p", [Msg]),
+ send(Socket, Msg)
+ end
end,
- tsp("handle_http_msg -> done"),
NextRequest.
+dummy_ssl_server_hang(Caller, Inet, SslOpt) ->
+ Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, Inet, SslOpt]),
+ receive
+ {port, Port} ->
+ {Pid, Port}
+ end.
+
+dummy_ssl_server_hang_init(Caller, Inet, SslOpt) ->
+ {ok, ListenSocket} =
+ ssl:listen(0, [binary, Inet, {packet, 0},
+ {reuseaddr,true},
+ {active, false}] ++ SslOpt),
+ {ok, {_,Port}} = ssl:sockname(ListenSocket),
+ Caller ! {port, Port},
+ {ok, AcceptSocket} = ssl:transport_accept(ListenSocket),
+ dummy_ssl_server_hang_loop(AcceptSocket).
+
+dummy_ssl_server_hang_loop(_) ->
+ %% Do not do ssl:ssl_accept as we
+ %% want to time out the underlying gen_tcp:connect
+ receive
+ stop ->
+ ok
+ end.
+
ensure_host_header_with_port([]) ->
false;
ensure_host_header_with_port(["host: " ++ Host| _]) ->
case string:tokens(Host, [$:]) of
- [ActualHost, Port] ->
- tsp("ensure_host_header_with_port -> "
- "~n ActualHost: ~p"
- "~n Port: ~p", [ActualHost, Port]),
+ [_ActualHost, _Port] ->
true;
_ ->
false
@@ -3891,15 +1356,15 @@ auth_header([_ | Tail]) ->
handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) ->
case string:tokens(base64:decode_to_string(UserInfo), ":") of
["alladin", "sesame"] = Auth ->
- test_server:format("Auth: ~p~n", [Auth]),
+ ct:print("Auth: ~p~n", [Auth]),
DefaultResponse;
Other ->
- test_server:format("UnAuth: ~p~n", [Other]),
+ ct:print("UnAuth: ~p~n", [Other]),
Challange
end.
check_cookie([]) ->
- tsf(no_cookie_header);
+ ct:fail(no_cookie_header);
check_cookie(["cookie:" ++ _Value | _]) ->
ok;
check_cookie([_Head | Tail]) ->
@@ -3912,122 +1377,532 @@ content_length(["content-length:" ++ Value | _]) ->
content_length([_Head | Tail]) ->
content_length(Tail).
-provocate_not_modified_bug(Url) ->
- Timeout = 15000, %% 15s should be plenty
+handle_uri(_,"/just_close.html",_,_,_,_) ->
+ close;
+handle_uri(_,"/no_content.html",_,_,_,_) ->
+ "HTTP/1.0 204 No Content\r\n\r\n";
+
+handle_uri(_,"/no_headers.html",_,_,_,_) ->
+ "HTTP/1.0 200 OK\r\n\r\nTEST";
+
+handle_uri("TRACE","/trace.html",_,_,_,_) ->
+ Body = "TRACE /trace.html simulate HTTP TRACE ",
+ "HTTP/1.1 200 OK\r\n" ++ "Content-Length:" ++ integer_to_list(length(Body)) ++ "\r\n\r\n" ++ Body;
+
+handle_uri(_,"/ensure_host_header_with_port.html",_,Headers,_,_) ->
+ case ensure_host_header_with_port(Headers) of
+ true ->
+ B =
+ "<HTML><BODY>" ++
+ "host with port" ++
+ "</BODY></HTML>",
+ Len = integer_to_list(length(B)),
+ "HTTP/1.1 200 ok\r\n" ++
+ "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B;
+ false ->
+ B =
+ "<HTML><BODY>" ++
+ "Internal Server Error - host without port" ++
+ "</BODY></HTML>",
+ Len = integer_to_list(length(B)),
+ "HTTP/1.1 500 Internal Server Error\r\n" ++
+ "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B
+ end;
- {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
+handle_uri(_,"/300.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ Body = "<HTML><BODY><a href=" ++ NewUri ++
+ ">New place</a></BODY></HTML>",
+ "HTTP/1.1 300 Multiple Choices\r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:" ++ integer_to_list(length(Body))
+ ++ "\r\n\r\n" ++ Body;
+
+handle_uri("HEAD","/301.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ "HTTP/1.1 301 Moved Permanently\r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:0\r\n\r\n";
+
+handle_uri(_,"/301.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ Body = "<HTML><BODY><a href=" ++ NewUri ++
+ ">New place</a></BODY></HTML>",
+ "HTTP/1.1 301 Moved Permanently\r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:" ++ integer_to_list(length(Body))
+ ++ "\r\n\r\n" ++ Body;
+
+handle_uri("HEAD","/302.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ "HTTP/1.1 302 Found \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:0\r\n\r\n";
+
+handle_uri(_,"/302.html",Port, _,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ Body = "<HTML><BODY><a href=" ++ NewUri ++
+ ">New place</a></BODY></HTML>",
+ "HTTP/1.1 302 Found \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:" ++ integer_to_list(length(Body))
+ ++ "\r\n\r\n" ++ Body;
+
+handle_uri("HEAD","/303.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ "HTTP/1.1 302 See Other \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:0\r\n\r\n";
+handle_uri(_,"/303.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ Body = "<HTML><BODY><a href=" ++ NewUri ++
+ ">New place</a></BODY></HTML>",
+ "HTTP/1.1 303 See Other \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:" ++ integer_to_list(length(Body))
+ ++ "\r\n\r\n" ++ Body;
+handle_uri("HEAD","/307.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ "HTTP/1.1 307 Temporary Rediect \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:0\r\n\r\n";
+handle_uri(_,"/307.html",Port,_,Socket,_) ->
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/dummy.html",
+ Body = "<HTML><BODY><a href=" ++ NewUri ++
+ ">New place</a></BODY></HTML>",
+ "HTTP/1.1 307 Temporary Rediect \r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:" ++ integer_to_list(length(Body))
+ ++ "\r\n\r\n" ++ Body;
+
+handle_uri(_,"/500.html",_,_,_,_) ->
+ "HTTP/1.1 500 Internal Server Error\r\n" ++
+ "Content-Length:47\r\n\r\n" ++
+ "<HTML><BODY>Internal Server Error</BODY></HTML>";
+
+handle_uri(_,"/503.html",_,_,_,DefaultResponse) ->
+ case ets:lookup(unavailable, 503) of
+ [{503, unavailable}] ->
+ ets:insert(unavailable, {503, available}),
+ "HTTP/1.1 503 Service Unavailable\r\n" ++
+ "Retry-After:5\r\n" ++
+ "Content-Length:47\r\n\r\n" ++
+ "<HTML><BODY>Internal Server Error</BODY></HTML>";
+ [{503, available}] ->
+ DefaultResponse;
+ [{503, long_unavailable}] ->
+ "HTTP/1.1 503 Service Unavailable\r\n" ++
+ "Retry-After:120\r\n" ++
+ "Content-Length:47\r\n\r\n" ++
+ "<HTML><BODY>Internal Server Error</BODY></HTML>"
+ end;
+
+handle_uri(_,"/redirectloop.html",Port,_,Socket,_) ->
+ %% Create a potential endless loop!
+ NewUri = url_start(Socket) ++
+ integer_to_list(Port) ++ "/redirectloop.html",
+ Body = "<HTML><BODY><a href=" ++ NewUri ++
+ ">New place</a></BODY></HTML>",
+ "HTTP/1.1 300 Multiple Choices\r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:" ++ integer_to_list(length(Body))
+ ++ "\r\n\r\n" ++ Body;
+
+handle_uri(_,"/userinfo.html", _,Headers,_, DefaultResponse) ->
+ Challange = "HTTP/1.1 401 Unauthorized \r\n" ++
+ "WWW-Authenticate:Basic" ++"\r\n" ++
+ "Content-Length:0\r\n\r\n",
+ case auth_header(Headers) of
+ {ok, Value} ->
+ handle_auth(Value, Challange, DefaultResponse);
+ _ ->
+ Challange
+ end;
+
+handle_uri(_,"/dummy_headers.html",_,_,Socket,_) ->
+ %% The client will only care about the Transfer-Encoding
+ %% header the rest of these headers are left to the
+ %% user to evaluate. This is not a valid response
+ %% it only tests that the header handling code works.
+ Head = "HTTP/1.1 200 ok\r\n" ++
+ "Content-Length:32\r\n" ++
+ "Pragma:1#no-cache\r\n" ++
+ "Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++
+ "Warning:1#pseudonym foobar\r\n" ++
+ "Vary:*\r\n" ++
+ "Trailer:Other:inets_test\r\n" ++
+ "Upgrade:HTTP/2.0\r\n" ++
+ "Age:4711\r\n" ++
+ "Transfer-Encoding:chunked\r\n" ++
+ "Content-Encoding:foo\r\n" ++
+ "Content-Language:en\r\n" ++
+ "Content-Location:http://www.foobar.se\r\n" ++
+ "Content-MD5:104528739076276072743283077410617235478\r\n"
+ ++
+ "Content-Range:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++
+ "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++
+ "Proxy-Authenticate:#1Basic" ++
+ "\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ http_chunk:encode_last();
+
+handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:Chunked\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ http_chunk:encode_last();
+
+handle_uri(_,"/cookie.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "set-cookie:" ++ "test_cookie=true; path=/;" ++
+ "max-age=60000\r\n" ++
+ "Content-Length:32\r\n\r\n"++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
+handle_uri(_,"/missing_crlf.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok" ++
+ "Content-Length:32\r\n" ++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
+handle_uri(_,"/wrong_statusline.html",_,_,_,_) ->
+ "ok 200 HTTP/1.1\r\n\r\n" ++
+ "Content-Length:32\r\n\r\n" ++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
+handle_uri(_,"/once_chunked.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:Chunked\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ send(Socket,
+ http_chunk:encode("obar</BODY></HTML>")),
+ http_chunk:encode_last();
+
+handle_uri(_,"/once.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 200 ok\r\n" ++
+ "Content-Length:32\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, "<HTML><BODY>fo"),
+ test_server:sleep(1000),
+ send(Socket, "ob"),
+ test_server:sleep(1000),
+ send(Socket, "ar</BODY></HTML>");
+
+handle_uri(_,"/invalid_http.html",_,_,_,_) ->
+ "HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++
+ "Transfer-Encoding:chunked\r\n\r\n";
+
+handle_uri(_,"/missing_reason_phrase.html",_,_,_,_) ->
+ "HTTP/1.1 200\r\n" ++
+ "Content-Length: 32\r\n\r\n"
+ "<HTML><BODY>foobar</BODY></HTML>";
+
+handle_uri(_,"/missing_CR.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\n" ++
+ "Content-Length:32\r\n\n" ++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
+handle_uri("HEAD",_,_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "Content-Length:0\r\n\r\n";
+handle_uri(_,_,_,_,_,DefaultResponse) ->
+ DefaultResponse.
+
+url_start(#sslsocket{}) ->
+ {ok,Host} = inet:gethostname(),
+ ?TLS_URL_START ++ Host ++ ":";
+url_start(_) ->
+ {ok,Host} = inet:gethostname(),
+ ?URL_START ++ Host ++ ":".
+
+send(#sslsocket{} = S, Msg) ->
+ ssl:send(S, Msg);
+send(S, Msg) ->
+ gen_tcp:send(S, Msg).
+
+close(#sslsocket{} = S) ->
+ ssl:close(S);
+close(S) ->
+ gen_tcp:close(S).
+
+sockname(#sslsocket{}= S) ->
+ ssl:sockname(S);
+sockname(S) ->
+ inet:sockname(S).
+
+receive_streamed_body(RequestId, Body) ->
+ receive
+ {http, {RequestId, stream, BinBodyPart}} ->
+ receive_streamed_body(RequestId,
+ <<Body/binary, BinBodyPart/binary>>);
+ {http, {RequestId, stream_end, _Headers}} ->
+ Body;
+ {http, Msg} ->
+ ct:fail(Msg)
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
+receive_streamed_body(RequestId, Body, Pid) ->
+ httpc:stream_next(Pid),
+ ct:print("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]),
+ receive
+ {http, {RequestId, stream, BinBodyPart}} ->
+ receive_streamed_body(RequestId,
+ <<Body/binary, BinBodyPart/binary>>,
+ Pid);
+ {http, {RequestId, stream_end, _Headers}} ->
+ Body;
+ {http, Msg} ->
+ ct:fail(Msg)
+ end.
+
+%% -----------------------------------------------------
+%% A sequence number handler
+%% The purpose is to be able to pair requests with responses.
+
+start_sequence_number_server() ->
+ proc_lib:spawn(fun() -> loop_sequence_number(1) end).
+
+loop_sequence_number(N) ->
+ receive
+ shutdown ->
+ ok;
+ {From, get_next} ->
+ From ! {next_is, N},
+ loop_sequence_number(N + 1)
end.
+get_next_sequence_number(SeqNumServer) ->
+ SeqNumServer ! {self(), get_next},
+ receive {next_is, N} -> N end.
-%% -------------------------------------------------------------------------
-
-simple_request_and_verify(Config,
- Method, Request, HttpOpts, Opts, VerifyResult)
- when (is_list(Config) andalso
- is_atom(Method) andalso
- is_list(HttpOpts) andalso
- is_list(Opts) andalso
- is_function(VerifyResult, 1)) ->
- tsp("request_and_verify -> entry with"
- "~n Method: ~p"
- "~n Request: ~p"
- "~n HttpOpts: ~p"
- "~n Opts: ~p", [Method, Request, HttpOpts, Opts]),
- case ?config(local_server, Config) of
- ok ->
- tsp("request_and_verify -> local-server running"),
- Result = (catch httpc:request(Method, Request, HttpOpts, Opts)),
- VerifyResult(Result);
- _ ->
- tsp("request_and_verify -> local-server *not* running - skip"),
- hard_skip("Local http-server not running")
+%% -----------------------------------------------------
+%% Client part
+%% Sends requests randomly parallel
+
+run_clients(NumClients, ServerPort, SeqNumServer) ->
+ {ok,Host} = inet:gethostname(),
+ set_random_seed(),
+ lists:map(
+ fun(Id) ->
+ Req = lists:flatten(io_lib:format("req~3..0w", [get_next_sequence_number(SeqNumServer)])),
+ Url = ?URL_START ++ Host ++ ":" ++ integer_to_list(ServerPort) ++ "/" ++ Req,
+ Pid = proc_lib:spawn(
+ fun() ->
+ case httpc:request(Url) of
+ {ok, {{_,200,_}, _, Resp}} ->
+ ct:print("[~w] 200 response: "
+ "~p~n", [Id, Resp]),
+ case lists:prefix(Req++"->", Resp) of
+ true -> exit(normal);
+ false -> exit({bad_resp,Req,Resp})
+ end;
+ {ok, {{_,EC,Reason},_,Resp}} ->
+ ct:print("[~w] ~w response: "
+ "~s~n~s~n",
+ [Id, EC, Reason, Resp]),
+ exit({bad_resp,Req,Resp});
+ Crap ->
+ ct:print("[~w] bad response: ~p",
+ [Id, Crap]),
+ exit({bad_resp, Req, Crap})
+ end
+ end),
+ MRef = erlang:monitor(process, Pid),
+ timer:sleep(10 + random:uniform(1334)),
+ {Id, Pid, MRef}
+ end,
+ lists:seq(1, NumClients)).
+
+wait4clients([], _Timeout) ->
+ ok;
+wait4clients(Clients, Timeout) when Timeout > 0 ->
+ Time = now_ms(),
+ receive
+ {'DOWN', _MRef, process, Pid, normal} ->
+ {value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients),
+ NewClients = lists:keydelete(Id, 1, Clients),
+ wait4clients(NewClients, Timeout - (now_ms() - Time));
+ {'DOWN', _MRef, process, Pid, Reason} ->
+ {value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients),
+ ct:fail({bad_client_termination, Id, Reason})
+ after Timeout ->
+ ct:fail({client_timeout, Clients})
+ end;
+wait4clients(Clients, _) ->
+ ct:fail({client_timeout, Clients}).
+
+
+%% -----------------------------------------------------
+%% Webserver part:
+%% Implements a web server that sends responses one character
+%% at a time, with random delays between the characters.
+
+start_slow_server(SeqNumServer) ->
+ proc_lib:start(
+ erlang, apply, [fun() -> init_slow_server(SeqNumServer) end, []]).
+
+init_slow_server(SeqNumServer) ->
+ Inet = inet_version(),
+ {ok, LSock} = gen_tcp:listen(0, [binary, Inet, {packet,0}, {active,true},
+ {backlog, 100}]),
+ {ok, {_IP, Port}} = inet:sockname(LSock),
+ proc_lib:init_ack({ok, self(), Port}),
+ loop_slow_server(LSock, SeqNumServer).
+
+loop_slow_server(LSock, SeqNumServer) ->
+ Master = self(),
+ Acceptor = proc_lib:spawn(
+ fun() -> client_handler(Master, LSock, SeqNumServer) end),
+ receive
+ {accepted, Acceptor} ->
+ loop_slow_server(LSock, SeqNumServer);
+ shutdown ->
+ gen_tcp:close(LSock),
+ exit(Acceptor, kill)
end.
+%% Handle one client connection
+client_handler(Master, LSock, SeqNumServer) ->
+ {ok, CSock} = gen_tcp:accept(LSock),
+ Master ! {accepted, self()},
+ set_random_seed(),
+ loop_client(1, CSock, SeqNumServer).
+
+loop_client(N, CSock, SeqNumServer) ->
+ %% Await request, don't bother parsing it too much,
+ %% assuming the entire request arrives in one packet.
+ receive
+ {tcp, CSock, Req} ->
+ ReqNum = parse_req_num(Req),
+ RespSeqNum = get_next_sequence_number(SeqNumServer),
+ Response = lists:flatten(io_lib:format("~s->resp~3..0w/~2..0w", [ReqNum, RespSeqNum, N])),
+ Txt = lists:flatten(io_lib:format("Slow server (~p) got ~p, answering with ~p",
+ [self(), Req, Response])),
+ ct:print("~s...~n", [Txt]),
+ slowly_send_response(CSock, Response),
+ case parse_connection_type(Req) of
+ keep_alive ->
+ ct:print("~s...done~n", [Txt]),
+ loop_client(N+1, CSock, SeqNumServer);
+ close ->
+ ct:print("~s...done (closing)~n", [Txt]),
+ gen_tcp:close(CSock)
+ end
+ end.
+slowly_send_response(CSock, Answer) ->
+ Response = lists:flatten(io_lib:format("HTTP/1.1 200 OK\r\nContent-Length: ~w\r\n\r\n~s",
+ [length(Answer), Answer])),
+ lists:foreach(
+ fun(Char) ->
+ timer:sleep(random:uniform(500)),
+ gen_tcp:send(CSock, <<Char>>)
+ end,
+ Response).
-not_implemented_yet() ->
- exit(not_implemented_yet).
+parse_req_num(Request) ->
+ Opts = [caseless,{capture,all_but_first,list}],
+ {match, [ReqNum]} = re:run(Request, "GET /(.*) HTTP", Opts),
+ ReqNum.
-p(F) ->
- p(F, []).
+parse_connection_type(Request) ->
+ Opts = [caseless,{capture,all_but_first,list}],
+ {match,[CType]} = re:run(Request, "connection: *(keep-alive|close)", Opts),
+ case string:to_lower(CType) of
+ "close" -> close;
+ "keep-alive" -> keep_alive
+ end.
-p(F, A) ->
- io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]).
+%% Time in milli seconds
+now_ms() ->
+ {A,B,C} = erlang:now(),
+ A*1000000000+B*1000+(C div 1000).
-tsp(F) ->
- inets_test_lib:tsp("[~w]" ++ F, [?MODULE]).
-tsp(F, A) ->
- inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]).
+set_random_seed() ->
+ {_, _, Micros} = now(),
+ A = erlang:phash2([make_ref(), self(), Micros]),
+ random:seed(A, A, A).
-tsf(Reason) ->
- test_server:fail(Reason).
+otp_8739(doc) ->
+ ["OTP-8739"];
+otp_8739(suite) ->
+ [];
+otp_8739(Config) when is_list(Config) ->
+ {_DummyServerPid, Port} = otp_8739_dummy_server(),
+ {ok,Host} = inet:gethostname(),
+ URL = ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ "/dummy.html",
+ Method = get,
+ Request = {URL, []},
+ HttpOptions = [{connect_timeout, 500}, {timeout, 1}],
+ Options = [{sync, true}],
+ case httpc:request(Method, Request, HttpOptions, Options) of
+ {error, timeout} ->
+ %% And now we check the size of the handler db
+ Info = httpc:info(),
+ ct:print("Info: ~p", [Info]),
+ {value, {handlers, Handlers}} =
+ lists:keysearch(handlers, 1, Info),
+ case Handlers of
+ [] ->
+ ok;
+ _ ->
+ ct:fail({unexpected_handlers, Handlers})
+ end;
+ Unexpected ->
+ ct:fail({unexpected, Unexpected})
+ end.
-dummy_ssl_server_hang(Caller, IpV, SslOpt) ->
- Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]),
+otp_8739_dummy_server() ->
+ Parent = self(),
+ Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end),
receive
{port, Port} ->
{Pid, Port}
end.
-dummy_ssl_server_hang_init(Caller, IpV, SslOpt) ->
+otp_8739_dummy_server_init(Parent) ->
+ Inet = inet_version(),
{ok, ListenSocket} =
- case IpV of
- ipv4 ->
- ssl:listen(0, [binary, inet, {packet, 0},
- {reuseaddr,true},
- {active, false}] ++ SslOpt);
- ipv6 ->
- ssl:listen(0, [binary, inet6, {packet, 0},
- {reuseaddr,true},
- {active, false}] ++ SslOpt)
- end,
- {ok, {_,Port}} = ssl:sockname(ListenSocket),
- tsp("dummy_ssl_server_hang_init -> Port: ~p", [Port]),
- Caller ! {port, Port},
- {ok, AcceptSocket} = ssl:transport_accept(ListenSocket),
- dummy_ssl_server_hang_loop(AcceptSocket).
+ gen_tcp:listen(0, [binary, Inet, {packet, 0},
+ {reuseaddr,true},
+ {active, false}]),
+ {ok, Port} = inet:port(ListenSocket),
+ Parent ! {port, Port},
+ otp_8739_dummy_server_main(Parent, ListenSocket).
-dummy_ssl_server_hang_loop(_) ->
- %% Do not do ssl:ssl_accept as we
- %% want to time out the underlying gen_tcp:connect
- receive
- stop ->
- ok
+otp_8739_dummy_server_main(_Parent, ListenSocket) ->
+ case gen_tcp:accept(ListenSocket) of
+ {ok, Sock} ->
+ %% Ignore the request, and simply wait for the socket to close
+ receive
+ {tcp_closed, Sock} ->
+ (catch gen_tcp:close(ListenSocket)),
+ exit(normal);
+ {tcp_error, Sock, Reason} ->
+ ct:fail("socket error: ~p", [Reason]),
+ (catch gen_tcp:close(ListenSocket)),
+ exit(normal)
+ after 10000 ->
+ %% Just in case
+ (catch gen_tcp:close(Sock)),
+ (catch gen_tcp:close(ListenSocket)),
+ exit(timeout)
+ end;
+ Error ->
+ exit(Error)
end.
-
-hard_skip(Reason) ->
- throw(skip(Reason)).
-
-skip(Reason) ->
- {skip, Reason}.
diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl
index 93dbc270c5..80f43ec236 100644
--- a/lib/inets/test/httpc_cookie_SUITE.erl
+++ b/lib/inets/test/httpc_cookie_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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..1efa78a63e 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -67,97 +67,36 @@
]).
-export([
- pssl_mod_alias/1,
essl_mod_alias/1,
-
- pssl_mod_actions/1,
essl_mod_actions/1,
-
- pssl_mod_security/1,
essl_mod_security/1,
-
- pssl_mod_auth/1,
essl_mod_auth/1,
-
- pssl_mod_auth_api/1,
essl_mod_auth_api/1,
-
- pssl_mod_auth_mnesia_api/1,
essl_mod_auth_mnesia_api/1,
-
- pssl_mod_htaccess/1,
essl_mod_htaccess/1,
-
- pssl_mod_cgi/1,
essl_mod_cgi/1,
-
- pssl_mod_esi/1,
essl_mod_esi/1,
-
- pssl_mod_get/1,
essl_mod_get/1,
-
- pssl_mod_head/1,
essl_mod_head/1,
-
- pssl_mod_all/1,
essl_mod_all/1,
-
- pssl_load_light/1,
essl_load_light/1,
-
- pssl_load_medium/1,
essl_load_medium/1,
-
- pssl_load_heavy/1,
essl_load_heavy/1,
-
- pssl_dos_hostname/1,
essl_dos_hostname/1,
-
- pssl_time_test/1,
essl_time_test/1,
-
- pssl_restart_no_block/1,
essl_restart_no_block/1,
-
- pssl_restart_disturbing_block/1,
essl_restart_disturbing_block/1,
-
- pssl_restart_non_disturbing_block/1,
essl_restart_non_disturbing_block/1,
-
- pssl_block_disturbing_idle/1,
essl_block_disturbing_idle/1,
-
- pssl_block_non_disturbing_idle/1,
essl_block_non_disturbing_idle/1,
-
- pssl_block_503/1,
essl_block_503/1,
-
- pssl_block_disturbing_active/1,
essl_block_disturbing_active/1,
-
- pssl_block_non_disturbing_active/1,
essl_block_non_disturbing_active/1,
-
- pssl_block_disturbing_active_timeout_not_released/1,
essl_block_disturbing_active_timeout_not_released/1,
-
- pssl_block_disturbing_active_timeout_released/1,
essl_block_disturbing_active_timeout_released/1,
-
- pssl_block_non_disturbing_active_timeout_not_released/1,
essl_block_non_disturbing_active_timeout_not_released/1,
-
- pssl_block_non_disturbing_active_timeout_released/1,
essl_block_non_disturbing_active_timeout_released/1,
-
- pssl_block_disturbing_blocker_dies/1,
essl_block_disturbing_blocker_dies/1,
-
- pssl_block_non_disturbing_blocker_dies/1,
essl_block_non_disturbing_blocker_dies/1
]).
@@ -242,26 +181,7 @@ groups() ->
ip_block_non_disturbing_active_timeout_released,
ip_block_disturbing_blocker_dies,
ip_block_non_disturbing_blocker_dies]},
- {ssl, [], [{group, pssl}, {group, essl}]},
- {pssl, [],
- [pssl_mod_alias, pssl_mod_actions, pssl_mod_security,
- pssl_mod_auth, pssl_mod_auth_api,
- pssl_mod_auth_mnesia_api, pssl_mod_htaccess,
- pssl_mod_cgi, pssl_mod_esi, pssl_mod_get, pssl_mod_head,
- pssl_mod_all, pssl_load_light, pssl_load_medium,
- pssl_load_heavy, pssl_dos_hostname, pssl_time_test,
- pssl_restart_no_block, pssl_restart_disturbing_block,
- pssl_restart_non_disturbing_block,
- pssl_block_disturbing_idle,
- pssl_block_non_disturbing_idle, pssl_block_503,
- pssl_block_disturbing_active,
- pssl_block_non_disturbing_active,
- pssl_block_disturbing_active_timeout_not_released,
- pssl_block_disturbing_active_timeout_released,
- pssl_block_non_disturbing_active_timeout_not_released,
- pssl_block_non_disturbing_active_timeout_released,
- pssl_block_disturbing_blocker_dies,
- pssl_block_non_disturbing_blocker_dies]},
+ {ssl, [], [{group, essl}]},
{essl, [],
[essl_mod_alias, essl_mod_actions, essl_mod_security,
essl_mod_auth, essl_mod_auth_api,
@@ -375,8 +295,8 @@ init_per_testcase(Case, Config) ->
init_per_testcase2(Case, Config) ->
- tsp("init_per_testcase2 -> entry with"
- "~n Config: ~p", [Config]),
+ %% tsp("init_per_testcase2 -> entry with"
+ %% "~n Config: ~p", [Config]),
IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf",
@@ -386,33 +306,33 @@ init_per_testcase2(Case, Config) ->
DataDir = ?config(data_dir, Config),
SuiteTopDir = ?config(suite_top_dir, Config),
- tsp("init_per_testcase2 -> "
- "~n SuiteDir: ~p"
- "~n DataDir: ~p", [SuiteTopDir, DataDir]),
+ %% tsp("init_per_testcase2 -> "
+ %% "~n SuiteDir: ~p"
+ %% "~n DataDir: ~p", [SuiteTopDir, DataDir]),
TcTopDir = filename:join(SuiteTopDir, Case),
?line ok = file:make_dir(TcTopDir),
- tsp("init_per_testcase2 -> "
- "~n TcTopDir: ~p", [TcTopDir]),
+ %% tsp("init_per_testcase2 -> "
+ %% "~n TcTopDir: ~p", [TcTopDir]),
DataSrc = filename:join([DataDir, "server_root"]),
ServerRoot = filename:join([TcTopDir, "server_root"]),
- tsp("init_per_testcase2 -> "
- "~n DataSrc: ~p"
- "~n ServerRoot: ~p", [DataSrc, ServerRoot]),
+ %% tsp("init_per_testcase2 -> "
+ %% "~n DataSrc: ~p"
+ %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]),
ok = file:make_dir(ServerRoot),
ok = file:make_dir(filename:join([TcTopDir, "logs"])),
NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config],
- tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"),
+ %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"),
inets_test_lib:copy_dirs(DataSrc, ServerRoot),
- tsp("init_per_testcase2 -> fix cgi"),
+ %% tsp("init_per_testcase2 -> fix cgi"),
EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]),
{ok, FileInfo} = file:read_file_info(EnvCGI),
ok = file:write_file_info(EnvCGI,
@@ -432,14 +352,14 @@ init_per_testcase2(Case, Config) ->
FileInfo1#file_info{mode = 8#00755}),
%% To be used by IP test cases
- tsp("init_per_testcase2 -> ip testcase setups"),
+ %% tsp("init_per_testcase2 -> ip testcase setups"),
create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
normal_access, IpNormal),
create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
mod_htaccess, IpHtaccess),
%% To be used by SSL test cases
- tsp("init_per_testcase2 -> ssl testcase setups"),
+ %% tsp("init_per_testcase2 -> ssl testcase setups"),
SocketType =
case atom_to_list(Case) of
[X, $s, $s, $l | _] ->
@@ -504,8 +424,8 @@ init_per_testcase2(Case, Config) ->
NewConfig
end,
- tsp("init_per_testcase2 -> done when"
- "~n NewConfig2: ~p", [NewConfig2]),
+ %% tsp("init_per_testcase2 -> done when"
+ %% "~n NewConfig2: ~p", [NewConfig2]),
NewConfig2.
@@ -530,27 +450,14 @@ 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),
+
CaseRest =
case atom_to_list(Case) of
"ip_mod_htaccess" ->
@@ -762,14 +669,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"];
@@ -1088,13 +990,6 @@ ip_restart_non_disturbing_block(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_alias(doc) ->
- ["Module test: mod_alias - old SSL config"];
-pssl_mod_alias(suite) ->
- [];
-pssl_mod_alias(Config) when is_list(Config) ->
- ssl_mod_alias(ssl, Config).
-
essl_mod_alias(doc) ->
["Module test: mod_alias - using new of configure new SSL"];
essl_mod_alias(suite) ->
@@ -1111,13 +1006,6 @@ ssl_mod_alias(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_actions(doc) ->
- ["Module test: mod_actions - old SSL config"];
-pssl_mod_actions(suite) ->
- [];
-pssl_mod_actions(Config) when is_list(Config) ->
- ssl_mod_actions(ssl, Config).
-
essl_mod_actions(doc) ->
["Module test: mod_actions - using new of configure new SSL"];
essl_mod_actions(suite) ->
@@ -1136,13 +1024,6 @@ ssl_mod_actions(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_security(doc) ->
- ["Module test: mod_security - old SSL config"];
-pssl_mod_security(suite) ->
- [];
-pssl_mod_security(Config) when is_list(Config) ->
- ssl_mod_security(ssl, Config).
-
essl_mod_security(doc) ->
["Module test: mod_security - using new of configure new SSL"];
essl_mod_security(suite) ->
@@ -1162,13 +1043,6 @@ ssl_mod_security(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_auth(doc) ->
- ["Module test: mod_auth - old SSL config"];
-pssl_mod_auth(suite) ->
- [];
-pssl_mod_auth(Config) when is_list(Config) ->
- ssl_mod_auth(ssl, Config).
-
essl_mod_auth(doc) ->
["Module test: mod_auth - using new of configure new SSL"];
essl_mod_auth(suite) ->
@@ -1186,12 +1060,6 @@ ssl_mod_auth(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_auth_api(doc) ->
- ["Module test: mod_auth - old SSL config"];
-pssl_mod_auth_api(suite) ->
- [];
-pssl_mod_auth_api(Config) when is_list(Config) ->
- ssl_mod_auth_api(ssl, Config).
essl_mod_auth_api(doc) ->
["Module test: mod_auth - using new of configure new SSL"];
@@ -1212,12 +1080,6 @@ ssl_mod_auth_api(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_auth_mnesia_api(doc) ->
- ["Module test: mod_auth_mnesia_api - old SSL config"];
-pssl_mod_auth_mnesia_api(suite) ->
- [];
-pssl_mod_auth_mnesia_api(Config) when is_list(Config) ->
- ssl_mod_auth_mnesia_api(ssl, Config).
essl_mod_auth_mnesia_api(doc) ->
["Module test: mod_auth_mnesia_api - using new of configure new SSL"];
@@ -1236,13 +1098,6 @@ ssl_mod_auth_mnesia_api(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_htaccess(doc) ->
- ["Module test: mod_htaccess - old SSL config"];
-pssl_mod_htaccess(suite) ->
- [];
-pssl_mod_htaccess(Config) when is_list(Config) ->
- ssl_mod_htaccess(ssl, Config).
-
essl_mod_htaccess(doc) ->
["Module test: mod_htaccess - using new of configure new SSL"];
essl_mod_htaccess(suite) ->
@@ -1260,13 +1115,6 @@ ssl_mod_htaccess(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_cgi(doc) ->
- ["Module test: mod_cgi - old SSL config"];
-pssl_mod_cgi(suite) ->
- [];
-pssl_mod_cgi(Config) when is_list(Config) ->
- ssl_mod_cgi(ssl, Config).
-
essl_mod_cgi(doc) ->
["Module test: mod_cgi - using new of configure new SSL"];
essl_mod_cgi(suite) ->
@@ -1275,27 +1123,15 @@ 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.
%%-------------------------------------------------------------------------
-pssl_mod_esi(doc) ->
- ["Module test: mod_esi - old SSL config"];
-pssl_mod_esi(suite) ->
- [];
-pssl_mod_esi(Config) when is_list(Config) ->
- ssl_mod_esi(ssl, Config).
-
essl_mod_esi(doc) ->
["Module test: mod_esi - using new of configure new SSL"];
essl_mod_esi(suite) ->
@@ -1313,13 +1149,6 @@ ssl_mod_esi(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_get(doc) ->
- ["Module test: mod_get - old SSL config"];
-pssl_mod_get(suite) ->
- [];
-pssl_mod_get(Config) when is_list(Config) ->
- ssl_mod_get(ssl, Config).
-
essl_mod_get(doc) ->
["Module test: mod_get - using new of configure new SSL"];
essl_mod_get(suite) ->
@@ -1337,13 +1166,6 @@ ssl_mod_get(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_head(doc) ->
- ["Module test: mod_head - old SSL config"];
-pssl_mod_head(suite) ->
- [];
-pssl_mod_head(Config) when is_list(Config) ->
- ssl_mod_head(ssl, Config).
-
essl_mod_head(doc) ->
["Module test: mod_head - using new of configure new SSL"];
essl_mod_head(suite) ->
@@ -1361,13 +1183,6 @@ ssl_mod_head(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_mod_all(doc) ->
- ["All modules test - old SSL config"];
-pssl_mod_all(suite) ->
- [];
-pssl_mod_all(Config) when is_list(Config) ->
- ssl_mod_all(ssl, Config).
-
essl_mod_all(doc) ->
["All modules test - using new of configure new SSL"];
essl_mod_all(suite) ->
@@ -1385,13 +1200,6 @@ ssl_mod_all(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_load_light(doc) ->
- ["Test light load - old SSL config"];
-pssl_load_light(suite) ->
- [];
-pssl_load_light(Config) when is_list(Config) ->
- ssl_load_light(ssl, Config).
-
essl_load_light(doc) ->
["Test light load - using new of configure new SSL"];
essl_load_light(suite) ->
@@ -1410,13 +1218,6 @@ ssl_load_light(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_load_medium(doc) ->
- ["Test medium load - old SSL config"];
-pssl_load_medium(suite) ->
- [];
-pssl_load_medium(Config) when is_list(Config) ->
- ssl_load_medium(ssl, Config).
-
essl_load_medium(doc) ->
["Test medium load - using new of configure new SSL"];
essl_load_medium(suite) ->
@@ -1441,13 +1242,6 @@ ssl_load_medium(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_load_heavy(doc) ->
- ["Test heavy load - old SSL config"];
-pssl_load_heavy(suite) ->
- [];
-pssl_load_heavy(Config) when is_list(Config) ->
- ssl_load_heavy(ssl, Config).
-
essl_load_heavy(doc) ->
["Test heavy load - using new of configure new SSL"];
essl_load_heavy(suite) ->
@@ -1472,12 +1266,6 @@ ssl_load_heavy(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_dos_hostname(doc) ->
- ["Denial Of Service (DOS) attack test case - old SSL config"];
-pssl_dos_hostname(suite) ->
- [];
-pssl_dos_hostname(Config) when is_list(Config) ->
- ssl_dos_hostname(ssl, Config).
essl_dos_hostname(doc) ->
["Denial Of Service (DOS) attack test case - using new of configure new SSL"];
@@ -1497,12 +1285,6 @@ ssl_dos_hostname(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_time_test(doc) ->
- ["old SSL config"];
-pssl_time_test(suite) ->
- [];
-pssl_time_test(Config) when is_list(Config) ->
- ssl_time_test(ssl, Config).
essl_time_test(doc) ->
["using new of configure new SSL"];
@@ -1535,13 +1317,6 @@ ssl_time_test(Tag, Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-pssl_block_503(doc) ->
- ["Check that you will receive status code 503 when the server"
- " is blocked and 200 when its not blocked - old SSL config."];
-pssl_block_503(suite) ->
- [];
-pssl_block_503(Config) when is_list(Config) ->
- ssl_block_503(ssl, Config).
essl_block_503(doc) ->
["Check that you will receive status code 503 when the server"
@@ -1561,15 +1336,6 @@ ssl_block_503(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "distribing does not really make a difference in this case."
- "Old SSL config"];
-pssl_block_disturbing_idle(suite) ->
- [];
-pssl_block_disturbing_idle(Config) when is_list(Config) ->
- ssl_block_disturbing_idle(ssl, Config).
-
essl_block_disturbing_idle(doc) ->
["Check that you can block/unblock an idle server. The strategy "
"distribing does not really make a difference in this case."
@@ -1589,15 +1355,6 @@ ssl_block_disturbing_idle(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_non_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing does not really make a difference in this case."
- "Old SSL config"];
-pssl_block_non_disturbing_idle(suite) ->
- [];
-pssl_block_non_disturbing_idle(Config) when is_list(Config) ->
- ssl_block_non_disturbing_idle(ssl, Config).
-
essl_block_non_disturbing_idle(doc) ->
["Check that you can block/unblock an idle server. The strategy "
"non distribing does not really make a difference in this case."
@@ -1617,15 +1374,6 @@ ssl_block_non_disturbing_idle(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_disturbing_active(doc) ->
- ["Check that you can block/unblock an active server. The strategy "
- "distribing means ongoing requests should be terminated."
- "Old SSL config"];
-pssl_block_disturbing_active(suite) ->
- [];
-pssl_block_disturbing_active(Config) when is_list(Config) ->
- ssl_block_disturbing_active(ssl, Config).
-
essl_block_disturbing_active(doc) ->
["Check that you can block/unblock an active server. The strategy "
"distribing means ongoing requests should be terminated."
@@ -1645,15 +1393,6 @@ ssl_block_disturbing_active(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_non_disturbing_active(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing means the ongoing requests should be compleated."
- "Old SSL config"];
-pssl_block_non_disturbing_active(suite) ->
- [];
-pssl_block_non_disturbing_active(Config) when is_list(Config) ->
- ssl_block_non_disturbing_active(ssl, Config).
-
essl_block_non_disturbing_active(doc) ->
["Check that you can block/unblock an idle server. The strategy "
"non distribing means the ongoing requests should be compleated."
@@ -1673,17 +1412,6 @@ ssl_block_non_disturbing_active(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be compleated"
- "if the timeout does not occur."
- "Old SSL config"];
-pssl_block_disturbing_active_timeout_not_released(suite) ->
- [];
-pssl_block_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- ssl_block_disturbing_active_timeout_not_released(ssl, Config).
-
essl_block_disturbing_active_timeout_not_released(doc) ->
["Check that you can block an active server. The strategy "
"distribing means ongoing requests should be compleated"
@@ -1706,17 +1434,6 @@ ssl_block_disturbing_active_timeout_not_released(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be terminated when"
- "the timeout occurs."
- "Old SSL config"];
-pssl_block_disturbing_active_timeout_released(suite) ->
- [];
-pssl_block_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- ssl_block_disturbing_active_timeout_released(ssl, Config).
-
essl_block_disturbing_active_timeout_released(doc) ->
["Check that you can block an active server. The strategy "
"distribing means ongoing requests should be terminated when"
@@ -1741,16 +1458,6 @@ ssl_block_disturbing_active_timeout_released(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_non_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed."
- "Old SSL config"];
-pssl_block_non_disturbing_active_timeout_not_released(suite) ->
- [];
-pssl_block_non_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- ssl_block_non_disturbing_active_timeout_not_released(ssl, Config).
-
essl_block_non_disturbing_active_timeout_not_released(doc) ->
["Check that you can block an active server. The strategy "
"non non distribing means ongoing requests should be completed."
@@ -1774,16 +1481,6 @@ ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_non_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non distribing means ongoing requests should be completed. "
- "When the timeout occurs the block operation sohould be canceled."
- "Old SSL config"];
-pssl_block_non_disturbing_active_timeout_released(suite) ->
- [];
-pssl_block_non_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- ssl_block_non_disturbing_active_timeout_released(ssl, Config).
essl_block_non_disturbing_active_timeout_released(doc) ->
["Check that you can block an active server. The strategy "
@@ -1811,12 +1508,6 @@ ssl_block_non_disturbing_active_timeout_released(Tag, Config)
%%-------------------------------------------------------------------------
-pssl_block_disturbing_blocker_dies(doc) ->
- ["old SSL config"];
-pssl_block_disturbing_blocker_dies(suite) ->
- [];
-pssl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
- ssl_block_disturbing_blocker_dies(ssl, Config).
essl_block_disturbing_blocker_dies(doc) ->
["using new of configure new SSL"];
@@ -1835,13 +1526,6 @@ ssl_block_disturbing_blocker_dies(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_block_non_disturbing_blocker_dies(doc) ->
- ["old SSL config"];
-pssl_block_non_disturbing_blocker_dies(suite) ->
- [];
-pssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
- ssl_block_non_disturbing_blocker_dies(ssl, Config).
-
essl_block_non_disturbing_blocker_dies(doc) ->
["using new of configure new SSL"];
essl_block_non_disturbing_blocker_dies(suite) ->
@@ -1859,12 +1543,6 @@ ssl_block_non_disturbing_blocker_dies(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_restart_no_block(doc) ->
- ["old SSL config"];
-pssl_restart_no_block(suite) ->
- [];
-pssl_restart_no_block(Config) when is_list(Config) ->
- ssl_restart_no_block(ssl, Config).
essl_restart_no_block(doc) ->
["using new of configure new SSL"];
@@ -1883,12 +1561,6 @@ ssl_restart_no_block(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_restart_disturbing_block(doc) ->
- ["old SSL config"];
-pssl_restart_disturbing_block(suite) ->
- [];
-pssl_restart_disturbing_block(Config) when is_list(Config) ->
- ssl_restart_disturbing_block(ssl, Config).
essl_restart_disturbing_block(doc) ->
["using new of configure new SSL"];
@@ -1940,12 +1612,6 @@ ssl_restart_disturbing_block(Tag, Config) ->
%%-------------------------------------------------------------------------
-pssl_restart_non_disturbing_block(doc) ->
- ["old SSL config"];
-pssl_restart_non_disturbing_block(suite) ->
- [];
-pssl_restart_non_disturbing_block(Config) when is_list(Config) ->
- ssl_restart_non_disturbing_block(ssl, Config).
essl_restart_non_disturbing_block(doc) ->
["using new of configure new SSL"];
@@ -2338,7 +2004,7 @@ create_config(Config, Access, FileName) ->
"~n Type: ~p"
"~n Port: ~p"
"~n Host: ~p"
- "~n", [ServerRoot, TcTopDir, Port, Type, Host]),
+ "~n", [ServerRoot, TcTopDir, Type, Port, Host]),
SSL =
if
@@ -2698,11 +2364,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;
@@ -2781,3 +2442,4 @@ tsp(F, A) ->
tsf(Reason) ->
inets_test_lib:tsf(Reason).
+
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index 387263ce58..df4ed6b179 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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,6 @@
%%-------------------------------------------------------------------------
alias(Type, Port, Host, Node) ->
%% This is very crude, but...
- tsp("alias -> Has IPv6 support: ~p", [inets_test_lib:has_ipv6_support()]),
Opts = [],
ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
"GET /pics/icon.sheet.gif "
@@ -85,16 +84,7 @@ actions(Type, Port, Host, Node) ->
%%-------------------------------------------------------------------------
security(ServerRoot, Type, Port, Host, Node) ->
- 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 -> "
@@ -333,13 +323,7 @@ security(ServerRoot, Type, Port, Host, Node) ->
%%-------------------------------------------------------------------------
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",
@@ -750,11 +734,6 @@ htaccess(Type, Port, Host, Node) ->
{header, "WWW-Authenticate"}]).
%%--------------------------------------------------------------------
cgi(Type, Port, Host, Node) ->
-%% tsp("cgi -> entry with"
-%% "~n Type: ~p"
-%% "~n Port: ~p"
-%% "~n Host: ~p"
-%% "~n Node: ~p", []),
{Script, Script2, Script3} =
case test_server:os_type() of
{win32, _} ->
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 4b33350cf2..13584c50f6 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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,7 +91,7 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) ->
verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
-verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) ->
+verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, TimeOut) ->
tsp("verify_request -> entry with"
"~n SocketType: ~p"
"~n Host: ~p"
@@ -100,7 +100,17 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, Ti
"~n Node: ~p"
"~n Options: ~p"
"~n TimeOut: ~p",
- [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]),
+ [SocketType, Host, Port, TranspOpts0, Node, Options, TimeOut]),
+
+ %% For now, until we modernize the httpd tests
+ TranspOpts =
+ case lists:member(inet6, TranspOpts0) of
+ true ->
+ TranspOpts0;
+ false ->
+ [inet | TranspOpts0]
+ end,
+
try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of
{ok, Socket} ->
tsp("verify_request -> connected - now send message"),
@@ -293,8 +303,7 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
list_to_integer(Headers#http_response_h.'content-length'),
Body).
-
-%%--------------------------------------------------------------------
+%--------------------------------------------------------------------
%% Internal functions
%%------------------------------------------------------------------
check_version(Version, Options) ->
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..6510c70d08 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -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
@@ -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_appup_test.erl b/lib/inets/test/inets_appup_test.erl
index 7ed237243e..d563b52ae7 100644
--- a/lib/inets/test/inets_appup_test.erl
+++ b/lib/inets/test/inets_appup_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -257,6 +257,21 @@ check_instruction(_, Instr, _AllInstr, _Modules) ->
check_version(V) when is_list(V) ->
ok;
+check_version(REBin) when is_binary(REBin) ->
+ try
+ begin
+ RE = binary_to_list(REBin),
+ case re:compile(RE) of
+ {ok, _} ->
+ ok;
+ {error, _} ->
+ error({bad_version, REBin})
+ end
+ end
+ catch
+ _T:_E ->
+ error({bad_version, REBin})
+ end;
check_version(V) ->
error({bad_version, V}).
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 1d262a2739..0ac940fd3e 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-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
@@ -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 0f8671b682..6ccc7b0da1 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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,26 +22,8 @@
-include("inets_test_lib.hrl").
-include_lib("inets/src/http_lib/http_internal.hrl").
-%% Various small utility functions
--export([start_http_server/1, start_http_server/2]).
--export([start_http_server_ssl/1, start_http_server_ssl/2]).
--export([hostname/0]).
--export([connect_bin/3, connect_bin/4,
- connect_byte/3, connect_byte/4,
- 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]).
--export([oscmd/1, has_ipv6_support/0, has_ipv6_support/1, print_system_info/1]).
--export([run_on_os/2, run_on_windows/1]).
--export([ensure_started/1]).
--export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]).
--export([flush/0]).
--export([start_node/1, stop_node/1]).
-
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
%% -- Misc os command and stuff
@@ -474,7 +456,7 @@ connect_bin(ssl, Host, Port, Opts0) ->
Opts = [binary, {packet,0} | Opts0],
connect(ssl, Host, Port, Opts);
connect_bin(essl, Host, Port, Opts0) ->
- Opts = [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true} | Opts0],
+ Opts = [{ssl_imp, new}, binary, {packet,0}| Opts0],
connect(ssl, Host, Port, Opts);
connect_bin(ip_comm, Host, Port, Opts0) ->
Opts = [binary, {packet, 0} | Opts0],
@@ -494,74 +476,10 @@ connect_byte(ip_comm, Host, Port, Opts0) ->
Opts = [{packet,0} | Opts0],
connect(ip_comm, Host, Port, Opts).
-
-%% This always falls back on IPV4, but tries IPV6 first.
-connect(Proto, Host, Port, Opts0) ->
- Opts = Opts0 -- [inet, inet6],
- connect(Proto, Host, Port, Opts ++ [inet6], inet6).
-
-connect(ssl, Host, Port, Opts, Type) ->
- tsp("connect(ssl) -> entry with"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n Opts: ~p"
- "~n Type: ~p", [Host, Port, Opts, Type]),
- ssl:start(),
- %% We ignore this option for ssl...
- %% ...maybe we should really treat this in the same way as ip_comm...
- case ssl:connect(Host, Port, Opts) of
- {ok, Socket} ->
- {ok, Socket};
- {error, Reason} when Type =:= inet6 ->
- tsp("connect(ssl) -> failed connecting with inet6: "
- "~n Reason: ~p"
- "~n trying inet", [Reason]),
- connect(ssl, Host, Port, Opts -- [inet6], inet);
- {error, Reason} ->
- tsp("connect(ssl) -> failed connecting: "
- "~n Reason: ~p", [Reason]),
- {error, Reason};
- Error ->
- Error
- end;
-connect(ip_comm, Host, Port, Opts, Type) ->
- tsp("connect(ip_comm) -> entry with"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n Opts: ~p"
- "~n Type: ~p", [Host, Port, Opts, Type]),
-
- case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of
- {ok, Socket} ->
- tsp("connect success"),
- {ok, Socket};
-
- {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_comm) -> Fatal connect error: "
- "~n Error: ~p"
- "~nwhen"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n Opts: ~p"
- "~n Type: ~p"
- "~n", [Error, Host, Port, Opts, Type]),
- Error
- end.
-
+connect(ip_comm, Host, Port, Opts) ->
+ gen_tcp:connect(Host, Port, Opts);
+connect(ssl, Host, Port, Opts) ->
+ ssl:connect(Host, Port, Opts).
send(ssl, Socket, Data) ->
ssl:send(Socket, Data);
@@ -651,3 +569,13 @@ format_timestamp({_N1, _N2, N3} = Now) ->
[YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
lists:flatten(FormatDate).
+start_apps(Apps) ->
+ lists:foreach(fun(App) ->
+ application:stop(App),
+ application:start(App)
+ end, Apps).
+stop_apps(Apps) ->
+ lists:foreach(fun(App) ->
+ application:stop(App)
+ end, Apps).
+
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/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl
new file mode 100644
index 0000000000..9ba09e1474
--- /dev/null
+++ b/lib/inets/test/uri_SUITE.erl
@@ -0,0 +1,159 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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:run("../inets_test", uri_SUITE).
+%%
+
+-module(uri_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("inets_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(GOOGLE, "www.google.com").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ ipv4,
+ ipv6,
+ host,
+ userinfo,
+ scheme,
+ queries,
+ escaped,
+ hexed_query
+ ].
+
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ Config.
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+
+init_per_testcase(_Case, Config) ->
+ Config.
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+
+ipv4(Config) when is_list(Config) ->
+ {ok, {http,[],"127.0.0.1",80,"/foobar.html",[]}} =
+ http_uri:parse("http://127.0.0.1/foobar.html").
+
+ipv6(Config) when is_list(Config) ->
+ {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
+ http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html"),
+ {ok, {http,[],"[2010:836B:4179::836B:4179]",80,"/foobar.html",[]}} =
+ http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html",
+ [{ipv6_host_with_brackets, true}]),
+ {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
+ http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html",
+ [{ipv6_host_with_brackets, false}]),
+ {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} =
+ http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html",
+ [{foo, false}]),
+ {error,
+ {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} =
+ http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html").
+
+host(Config) when is_list(Config) ->
+ {ok, {http,[],"localhost",8888,"/foobar.html",[]}} =
+ http_uri:parse("http://localhost:8888/foobar.html").
+
+userinfo(Config) when is_list(Config) ->
+ {ok, {http,"nisse:foobar","localhost",8888,"/foobar.html",[]}} =
+ http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html").
+
+scheme(Config) when is_list(Config) ->
+ {error, no_scheme} = http_uri:parse("localhost/foobar.html"),
+ {error, {malformed_url, _, _}} =
+ http_uri:parse("localhost:8888/foobar.html").
+
+queries(Config) when is_list(Config) ->
+ {ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} =
+ http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42").
+
+escaped(Config) when is_list(Config) ->
+ {ok, {http,[],"www.somedomain.com",80,"/%2Eabc",[]}} =
+ http_uri:parse("http://www.somedomain.com/%2Eabc"),
+ {ok, {http,[],"www.somedomain.com",80,"/%252Eabc",[]}} =
+ http_uri:parse("http://www.somedomain.com/%252Eabc"),
+ {ok, {http,[],"www.somedomain.com",80,"/%25abc",[]}} =
+ http_uri:parse("http://www.somedomain.com/%25abc"),
+ {ok, {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"}} =
+ http_uri:parse("http://www.somedomain.com/%25abc?foo=bar").
+
+hexed_query(doc) ->
+ [{doc, "Solves OTP-6191"}];
+hexed_query(Config) when is_list(Config) ->
+ Google = ?GOOGLE,
+ GoogleSearch = "http://" ++ Google ++ "/search",
+ Search1 = "?hl=en&q=a%D1%85%D1%83%D0%B9&btnG=Google+Search",
+ URI1 = GoogleSearch ++ Search1,
+ Search2 = "?hl=en&q=%25%25",
+ URI2 = GoogleSearch ++ Search2,
+ Search3 = "?hl=en&q=%foo",
+ URI3 = GoogleSearch ++ Search3,
+
+ Verify1 =
+ fun({http, [], ?GOOGLE, 80, "/search", _}) -> ok;
+ (_) -> error
+ end,
+ Verify2 = Verify1,
+ Verify3 = Verify1,
+ verify_uri(URI1, Verify1),
+ verify_uri(URI2, Verify2),
+ verify_uri(URI3, Verify3).
+
+
+%%--------------------------------------------------------------------
+%% Internal Functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+
+verify_uri(URI, Verify) ->
+ case http_uri:parse(URI) of
+ {ok, ParsedURI} ->
+ case Verify(ParsedURI) of
+ ok ->
+ ok;
+ error ->
+ Reason = {unexpected_parse_result, URI, ParsedURI},
+ ERROR = {error, Reason},
+ throw(ERROR)
+ end;
+ {error, _} = ERROR ->
+ throw(ERROR)
+ end.
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 0c7cb5e7c2..f5abaf9764 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2012. All Rights Reserved.
+# Copyright Ericsson AB 2001-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 = inets
-INETS_VSN = 5.9.2
+INETS_VSN = 5.9.4
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/inviso/AUTHORS b/lib/inviso/AUTHORS
deleted file mode 100644
index fc5040fe92..0000000000
--- a/lib/inviso/AUTHORS
+++ /dev/null
@@ -1,4 +0,0 @@
-Original Authors and Contributors:
-
-Lennart Ohman
-
diff --git a/lib/inviso/Makefile b/lib/inviso/Makefile
deleted file mode 100644
index be19199151..0000000000
--- a/lib/inviso/Makefile
+++ /dev/null
@@ -1,37 +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$
-#
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-#
-# Macros
-#
-
-SUB_DIRECTORIES = src doc/src
-
-include vsn.mk
-VSN = $(RUNTIME_TOOLS_VSN)
-
-SPECIAL_TARGETS =
-
-#
-# Default Subdir Targets
-#
-include $(ERL_TOP)/make/otp_subdir.mk
-
-
diff --git a/lib/inviso/doc/pdf/.gitignore b/lib/inviso/doc/pdf/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/doc/pdf/.gitignore
+++ /dev/null
diff --git a/lib/inviso/doc/src/Makefile b/lib/inviso/doc/src/Makefile
deleted file mode 100644
index f00b10f29c..0000000000
--- a/lib/inviso/doc/src/Makefile
+++ /dev/null
@@ -1,122 +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$
-#
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../../vsn.mk
-VSN=$(INVISO_VSN)
-APPLICATION=inviso
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
-# Target Specs
-# ----------------------------------------------------
-XML_APPLICATION_FILES = ref_man.xml
-XML_REF3_FILES = \
- inviso.xml \
- inviso_as_lib.xml \
- inviso_lfm.xml \
- inviso_lfm_tpfreader.xml \
- inviso_rt.xml \
- inviso_rt_meta.xml
-
-XML_PART_FILES = part.xml part_notes.xml
-XML_CHAPTER_FILES = inviso_chapter.xml notes.xml
-
-BOOK_FILES = book.xml
-
-XML_FILES = \
- $(BOOK_FILES) $(XML_CHAPTER_FILES) \
- $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
-
-GIF_FILES = \
- inviso_users_guide_pic1.gif
-
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-
-HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
-
-TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
-
-docs: pdf html man
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: gifs $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES)
-
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -f $(MAN3DIR)/*
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f errs core *~
-
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
-
-release_spec:
-
-
-
-
diff --git a/lib/inviso/doc/src/book.xml b/lib/inviso/doc/src/book.xml
deleted file mode 100644
index c258f33ff7..0000000000
--- a/lib/inviso/doc/src/book.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE book SYSTEM "book.dtd">
-
-<book xmlns:xi="http://www.w3.org/2001/XInclude">
- <header titlestyle="normal">
- <copyright>
- <year>2006</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>Inviso</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <insidecover>
- </insidecover>
- <pagetext>Inviso</pagetext>
- <preamble>
- <contents level="2"></contents>
- </preamble>
- <parts lift="no">
- <xi:include href="part.xml"/>
- </parts>
- <applications>
- <xi:include href="ref_man.xml"/>
- </applications>
- <releasenotes>
- <xi:include href="notes.xml"/>
- </releasenotes>
- <listofterms></listofterms>
- <index></index>
-</book>
-
diff --git a/lib/inviso/doc/src/fascicules.xml b/lib/inviso/doc/src/fascicules.xml
deleted file mode 100644
index 0678195e07..0000000000
--- a/lib/inviso/doc/src/fascicules.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
-
-<fascicules>
- <fascicule file="part" href="part_frame.html" entry="no">
- User's Guide
- </fascicule>
- <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
- Reference Manual
- </fascicule>
- <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
- Release Notes
- </fascicule>
- <fascicule file="" href="../../../../doc/print.html" entry="no">
- Off-Print
- </fascicule>
-</fascicules>
-
diff --git a/lib/inviso/doc/src/inviso.xml b/lib/inviso/doc/src/inviso.xml
deleted file mode 100644
index c0e2e8f0de..0000000000
--- a/lib/inviso/doc/src/inviso.xml
+++ /dev/null
@@ -1,693 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year><year>2011</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>inviso</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso</module>
- <modulesummary>Main API Module to the Inviso Tracer</modulesummary>
- <description>
- <warning>
- <p>The <c>inviso</c> application is deprecated and will be
- removed in the R16 release.</p>
- </warning>
- <p>With the <c>inviso</c> API runtime components can be started and tracing managed across a network of distributed Erlang nodes, using a control component also started with <c>inviso</c> API functions.</p>
- <p>Inviso can be used both in a distributed environment and in a non-distributed. API functions not taking a list of nodes as argument works on all started runtime components. If it is the non-distributed case, that is the local runtime component. The API functions taking a list of nodes as argument, or as part of one of the arguments, can not be used in a non-distributed environment. Return values named <c>NodeResult</c> refers to return values from a single Erlang node, and will therefore be the return in the non-distributed environment.</p>
- </description>
- <funcs>
- <func>
- <name>start() -> {ok,pid()} | {error,Reason}</name>
- <name>start(Options) -> {ok,pid()} | {error,Reason}</name>
- <fsummary>Start a control component at the local node</fsummary>
- <type>
- <v>Options = [Option]</v>
- </type>
- <desc>
- <p><c>Options</c> may contain both options which will be default options to a runtime component when started, and options to the control component. See <seealso marker="#add_nodes/3">add_nodes/3</seealso> for details on runtime component options. The control component recognizes the following options:</p>
- <taglist>
- <tag><c>{subscribe,Pid}</c></tag>
- <item>
- <p>Making the process <c>Pid</c> receive Inviso events from the control component.</p>
- <p>Starts a control component process on the local node. A control component must be started before runtime components can be started manually or otherwise accessed through the <c>inviso</c> API.</p>
- </item>
- </taglist>
- </desc>
- </func>
- <func>
- <name>stop() -> shutdown</name>
- <fsummary>Stop the control component</fsummary>
- <desc>
- <p>Stops the control component. Runtime components are left as is. They will behave according to their dependency values.</p>
- </desc>
- </func>
- <func>
- <name>add_node(RTtag) -> NodeResult | {error,Reason}</name>
- <name>add_node(RTtag,Options) -> NodeResult | {error,Reason}</name>
- <fsummary>Starts or adopts a runtime component at the local node</fsummary>
- <type>
- <v>RTtag = PreviousRTtag = term()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option -- see below</v>
- <v>&nbsp;Option = {dependency,Dep}</v>
- <v>&nbsp;&nbsp;Dep = int() | infinity</v>
- <d>The timeout, in milliseconds, before the runtime component will terminate if abandoned by <em>this</em>control component.</d>
- <v>&nbsp;Option = {overload,Overload} | overload</v>
- <d>Controls how and how often overload checks shall be performed. Just <c>overload</c>specifies that no loadcheck shall be performed.</d>
- <v>&nbsp;&nbsp;Overload = Interval | {LoadMF,Interval,InitMFA,RemoveMFA}</v>
- <v>&nbsp;&nbsp;&nbsp;LoadMF = {Mod,Func} | function()/1</v>
- <v>&nbsp;&nbsp;&nbsp;Interval = int() | infinity</v>
- <d>Interval is the time in milliseconds between overload checks.</d>
- <v>&nbsp;&nbsp;&nbsp;InitMFA = RemoveMFA = {Mod,Func,ArgList} | void</v>
- <d>When starting up the runtime component or when changing options (see <c>change_options/2</c>) the overload mechanism is initialized with a call to the <c>InitMFA</c>function. It shall return <c>LoadCheckData</c>. Every time a load check is performed, <c>LoadMF</c>is called with <c>LoadCheckData</c>as its only argument. <c>LoadMF</c>shall return <c>ok</c>or <c>{suspend,Reason}</c>. When the runtime component is stopped or made to change options involving changing overload-check, the <c>RemoveMFA</c>function is called. Its return value is discarded.</d>
- <v>NodeResult = {ok,NAns} | {error,Reason}</v>
- <v>&nbsp;NAns = new | {adopted,State,Status,PreviousRTtag} | already_added</v>
- <v>&nbsp;&nbsp;State = new | tracing | idle</v>
- <v>&nbsp;&nbsp;Status = running | {suspended,SReason}</v>
- </type>
- <desc>
- <p>Starts or tries to connect to an existing runtime component at the local node, regardless if the system is distributed or not. <c>Options</c> will override any default options specified at start-up of the control component.</p>
- <p>The <c>PreviousRTtag</c> can indicate if the incarnation of the runtime component at the node in question was started by "us" and then can be expected to do tracing according to "our" instructions or not.</p>
- </desc>
- </func>
- <func>
- <name>add_node_if_ref(RTtag) -> NodeResult | {error,{wrong_reference,OtherTag}} | {error,Reason}</name>
- <name>add_node_if_ref(RTtag,Options) -> NodeResult | {error,{wrong_reference,OtherRef}} | {error,Reason}</name>
- <fsummary>Start or adopt a runtime component at the local node, provided it has a certain rttag</fsummary>
- <type>
- <v>OtherRef = term()</v>
- <d>rttag of the running incarnation</d>
- </type>
- <desc>
- <p>As <seealso marker="#add_node/1">add_node/1,2</seealso> but will only adopt the runtime component if its rttag is <c>RTtag</c>.</p>
- </desc>
- </func>
- <func>
- <name>add_nodes(Nodes,RTtag) -> {ok,NodeResults} | {error,Reason}</name>
- <name>add_nodes(Nodes,RTtag,Options) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Start or adopt runtime components at some nodes</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- </type>
- <desc>
- <p>As <seealso marker="#add_node/1">add_node/1,2</seealso> but for a distributed environment.</p>
- </desc>
- </func>
- <func>
- <name>add_nodes_if_ref(Nodes,RTtag) -> NodeResult | {error,Reason}</name>
- <name>add_nodes_if_ref(Nodes,RTtag,Options) -> NodeResult | {error,Reason}</name>
- <fsummary>Start or adopt runtime components at some nodes, provided they have a certain rttag</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- </type>
- <desc>
- <p>As <seealso marker="#add_node_if_ref/1">add_node_if_ref/1,2</seealso> but for a distributed environment.</p>
- </desc>
- </func>
- <func>
- <name>stop_nodes() -> {ok,NodeResults} | NodeResult</name>
- <name>stop_nodes(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Stop runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Stops runtime component on <c>Nodes</c>. <c>stop_nodes/0</c> will if the control component is running on a distributed node stop all runtime components. And if running on a non distributed node, stop the local and only runtime component.</p>
- </desc>
- </func>
- <func>
- <name>stop_all() = {ok,NodeResults} | NodeResult</name>
- <fsummary>Stop both control and runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>A combination of <seealso marker="#stop/0">stop/0</seealso> and <seealso marker="#stop_nodes/0">stop_nodes/0</seealso>.</p>
- </desc>
- </func>
- <func>
- <name>change_options(Options) -> NodeResult | {ok,NodeResults} | {error,Reason}</name>
- <name>change_options(Nodes,Options) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Change options for runtime components</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Changes the options for one or several runtime components. If for instance overload is redefined, the previous overload will be stopped and the new started. See <seealso marker="#add_node/1">add_node/1</seealso> for details on <c>Options</c>.</p>
- </desc>
- </func>
- <func>
- <name>init_tracing(TracerData) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>init_tracing(TracerList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>init_tracing(Nodes,TracerData) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Initiate tracing</fsummary>
- <type>
- <v>TracerData = [{trace,LogTD} [,{ti,TiTD}] }] | LogTD</v>
- <v>LogTD = {HandlerFun,Data1} | collector | {relayer,CollectingNode} | {ip,IPPortParameters} | {file,FilePortParameters}</v>
- <v>TiTD = {file,FileName} | {file,FileName,TiSpec} | {relay,Node}</v>
- <v>&nbsp;TiSpec = {InitMFA,RemoveMF,CleanMF}</v>
- <v>&nbsp;&nbsp;InitMFA = {Mi,Fi,Argsi}</v>
- <v>&nbsp;&nbsp;RemoveMF = {Mr,Fr} | void</v>
- <v>&nbsp;&nbsp;CleanMF = {Mc,Fc}</v>
- <v>&nbsp;&nbsp;Mi = Fi = Mr = Fr = Mc = Fd = atom()</v>
- <v>&nbsp;&nbsp;Argsi = [term()]</v>
- <v>TracerList = [{Node,TracerData}]</v>
- <v>IPPortParameters = Portno | {Portno,Qsize}</v>
- <v>&nbsp;Portno = tcp_portno()</v>
- <v>&nbsp;Qsize = int()</v>
- <v>FilePortParameters = {Filename,wrap,Tail,{time,WrapTime},WrapCnt} | {FileName,wrap,Tail,WrapSize,WrapCnt} | {FileName,wrap,Tail,WrapSize} | {FileName,wrap,Tail} | FileName</v>
- <v>&nbsp;FileName = string()</v>
- <v>&nbsp;Tail = string() =/= ""</v>
- <v>&nbsp;WrapTime = WrapCnt = WrapSize = int() >0</v>
- <v>TracerList = [{Node,TracerData}]</v>
- <v>Nodes = [Node]</v>
- <v>HandlerFun = function()/2;</v>
- <v>&nbsp;HandlerFun(TraceMsg,Data1) -> NewData</v>
- <v>CollectingNode = pid() | node()</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,LogResults} | {error,NReason}</v>
- <v>&nbsp;LogResults = [LogResult]</v>
- <v>&nbsp;&nbsp;LogResult = {trace_log,LogRes} | {ti_log,LogRes}</v>
- <v>&nbsp;&nbsp;&nbsp;LogRes = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Starts the tracing at the specified nodes, meaning that the runtime components transits from the state <c>new</c> or <c>idle</c> to <c>tracing</c>. For trace messages to be generated, there must of course also be trace pattern and/or trace flags set. Such can not be set before tracing has been initiated with <c>init_tracing/1,2</c>.</p>
- <p><c>TracerData</c> controls how the runtime component will handle generated trace messages. The <c>trace</c> tag controls how regular trace messages are handled. The <c>ti</c> tag controls if and how trace information will be stored and the meta tracer will be activated. That is if <c>ti</c> is omitted, no meta tracer will be started as part of the runtime component. It is possible to have <c>ti</c> without <c>trace</c>, but most likely not useful.</p>
- <p>The <c>ip</c> and <c>file</c> trace tracerdata instructions results in using the built in trace ip-port and file-port respectively. <c>relayer</c> will result in that all regular trace messages are forwarded to a runtime component at the specified node. Using a <c>HandlerFun</c> will result in that every incoming regular trace message is applied to the <c>HandlerFun</c>. <c>collector</c> can be used to use this runtime component to receive relayed trace messages and print them to the shell.
- </p>
- <p>The trace information can be configured to either write trace information to a plain trace information file or to relay it to another inviso meta tracer on another node. The inviso meta tracer is capable of matching function calls with their function returns (only if <c>return_trace</c> is activated in the meta trace match specification for the function in question). This is necessary since it may not be possible to decide what to do, if anything shall be done at all, until the return value of the function call is examined.
- </p>
- <p>To be able to match calls with returns a state can be saved when detecting a function call in a public loop data structure kept by the inviso meta tracer. The public loop data structure is given as argument to a handler-function called whenever a meta trace message arrives to the inviso meta tracer (both function calls and function returns). The public loop data structure is first initiated by the <c>Mi:Fi</c> function which takes the items in <c>Argsi</c> as arguments. <c>Fi</c> shall return the initial public loop data structure. When meta tracing is stopped, either because tracing is stopped or because tracing is suspended, the <c>Mr:Fr(PublicLoopData)</c> is called to offer a possibility to clean-up. Note that for every function meta-tracing is activated, a public loop data modification function can be specified. That function will prepare the current loop data structure for this particular function.
- </p>
- <p>Further there is a risk that function call states becomes abandoned inside the public loop data structure. This will happen if a function call is entered into the public loop data structure, but no function return occurs. To prevent the public loop data structure from growing infinitely the clean function <c>Fc</c> will periodically be called with the public loop data structure as argument. Elements entered into the public loop data structure as a result of a function call must contain a timestamp for the <c>Fc</c> to be able to conclude if it is abandoned or not. <c>Fc</c> shall return a new public loop data structure.
- </p>
- <p>When initiating tracing involving trace information without a <c>TiSpec</c>, a default public loop data structure will be initiated to handle locally registered process aliases. The default public loop data structure is a two-tuple where the first element is used by the meta tracing on the BIF <c>register/2</c>. The second element is left for user usage.</p>
- <p>The default public loop data structure may be extended with more element positions. The first position must be left to the implementation of registered-name translations. If the public loop data structure is changed no longer meeting this requirement, the <seealso marker="#tpm_localnames/0">tpm_localnames/0,1</seealso> and <seealso marker="#tpm_globalnames/0">tpm_globalnames/0,1</seealso> can no longer be used.</p>
- <p>A wrap files specification is used to limit the disk space consumed by the trace. The trace is written to a limited number of files each with a limited size. The actual filenames are <c>Filename ++ SeqCnt ++ Tail</c>, where <c>SeqCnt</c> counts as a decimal string from 0 to <c>WrapCnt</c> and then around again from 0. When a trace message written to the current file makes it longer than <c>WrapSize</c>, that file is closed, if the number of files in this wrap trace is as many as <c>WrapCnt</c> the oldest file is deleted then a new file is opened to become the current. Thus, when a wrap trace has been stopped, there are at most <c>WrapCnt</c> trace files saved with a size of at least <c>WrapSize</c> (but not much bigger), except for the last file that might even be empty. The default values are <c>WrapSize == 128*1024</c> and <c>WrapCnt == 8</c>.</p>
- <p>The <c>SeqCnt</c> values in the filenames are all in the range 0 through <c>WrapCnt</c> with a gap in the circular sequence. The gap is needed to find the end of the trace.</p>
- <p>If the <c>WrapSize</c> is specified as <c>{time,WrapTime}</c>, the current file is closed when it has been open more than <c>WrapTime</c> milliseconds, regardless of it being empty or not.</p>
- <p>The ip trace driver has a queue of <c>QSize</c> messages waiting to be delivered. If the driver cannot deliver messages as fast as they are produced by the runtime system, they are dropped. The number of dropped messages are indicated in the trace log as separate trace message.</p>
- </desc>
- </func>
- <func>
- <name>stop_tracing(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <name>stop_tracing() -> {ok,NodeResults} | NodeResult</name>
- <fsummary>Stop tracing</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,State} | {error,Reason}</v>
- <v>&nbsp;State = new | idle</v>
- </type>
- <desc>
- <p>Stops tracing on all or specified <c>Nodes</c>. Flushes the trace buffer if a trace-port is used, closes the trace-port and removes all trace flags and meta-patterns. The nodes are called in parallel.</p>
- <p>Stopping tracing means going to state <c><![CDATA[idle<c>. If the runtime component was already in state <c>new]]></c>, it will of course remain in state <c>new</c> (then there was no tracing to stop).</p>
- </desc>
- </func>
- <func>
- <name>clear() -> {ok,NodeResults} | NodeResult</name>
- <name>clear(Nodes,Options) -> {ok,NodeResults} | {error,Reason}</name>
- <name>clear(Options) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Stop tracing and remove meta trace patterns</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = keep_trace_patterns | keep_log_files</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>&nbsp;NodeResult = {ok,{new,Status}} | {error,Reason}</v>
- <v>&nbsp;&nbsp;Status = running | {suspended,SReason}</v>
- </type>
- <desc>
- <p>Stops all tracing including removing meta-trace patterns. Removes all trace patterns. If the node is <c>tracing</c> or <c>idle</c>, trace-logs belonging to the current tracerdata are removed. Hence the node is returned to state <c>new</c>. Note that the node can still be suspended.</p>
- <p>Various options can make the node keep set trace patterns and log-files. The node still enters the <c>new</c> state.</p>
- </desc>
- </func>
- <func>
- <name>tp(Nodes,Mod,Func,Arity,MatchSpec,Opts) -> </name>
- <name>tp(Nodes,Mod,Func,Arity,MatchSpec) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tp(Mod,Func,Arity,MatchSpec,Opts) -> </name>
- <name>tp(Mod,Func,Arity,MatchSpec) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tp(Nodes,PatternList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tp(PatternList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Set global trace patterns</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>Mod = Func = atom() | '_'</v>
- <v>Arity = int() | '_'</v>
- <v>MatchSpec = true | false | [] | matchspec()</v>
- <v>PatternList = [Pattern],</v>
- <v>&nbsp;Pattern = {Mod,Func,Arity,MatchSpec,Opts}</v>
- <v>Opts = [Opt]</v>
- <v>&nbsp;Opt = only_loaded</v>
- <v>NodeResults = [NodeResult]</v>
- <v>&nbsp;NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>&nbsp;&nbsp;Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set trace pattern (global) on specified or all nodes. The integer replied if the call was successfully describes the number of matched functions. The functions without a <c>Nodes</c> argument means all nodes, in a non-distributed environment it means the local node. Using wildcards follows the rules for wildcards of <c>erlang:trace_pattern/3</c>. It is for instance illegal to specify <c>M == '_'</c> while <c>F</c> is not <c>'_'</c>.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- <p>The option <c>only_loaded</c> will prevent modules not loaded (yet) into the runtime system to become loaded just as a result of that a trace pattern is requested to be set on it. Otherwise modules are automatically loaded if not already loaded (since the module must be present for a trace pattern to be set on it). The latter does not apply if the wildcard <c>'_'</c> is used as module specification.</p>
- </desc>
- </func>
- <func>
- <name>tpl(Nodes,Mod,Func,Arity,MatchSpec) -> </name>
- <name>tpl(Nodes,Mod,Func,Arity,MatchSpec,Opts) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpl(Mod,Func,Arity,MatchSpec) -> </name>
- <name>tpl(Mod,Func,Arity,MatchSpec,Opts) -> {ok,NodeResults} | NodeResult| {error,Reason}</name>
- <name>tpl(Nodes,PatternList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpl(PatternList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Set local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/6">tp/N</seealso> function above for details on arguments and return values.</p>
- <p>Set local trace pattern on specified functions. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctp(Nodes,Mod,Func,Arity) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctp(Mod,Func,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear global trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/6">tp/N</seealso> for argument descriptions.</p>
- <p>Clear global trace patterns. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctpl(Nodes,Mod,Func,Arity) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctpl(Mod,Funct,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/6">tp/N</seealso> for argument description.</p>
- <p>Clear local trace patterns. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>tf(Nodes,PidSpec,FlagList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tf(PidSpec,FlagList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tf(Nodes,TraceConfList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tf(NodeTraceConfList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tf(TraceConfList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Set process trace flags</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeTraceConfList = [{Node,TraceConfList}]</v>
- <v>TraceConfList = [{PidSpec,FlagList}]</v>
- <v>FlagList = [Flag]</v>
- <v>PidSpec = all | new| existing | pid() | locally_registered_name()</v>
- <v>Flag -- see erlang:trace/3</v>
- <v>NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set process trace flags on processes on all or specified nodes. The integer returned if the call was successful describes the matched number of processes. The functions without a <c>Nodes</c> argument means all nodes, in a non-distributed environment it means the local node.
- </p>
- <p>There are many combinations which does not make much sense. For instance specifying a certain process identifier at all nodes. Or an empty <c>TraceConfList</c> for all nodes.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctf(Nodes,PidSpec,FlagList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctf(PidSpec,FlagList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctf(Nodes,TraceConfList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctf(TraceConfList) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear process trace flags</fsummary>
- <desc>
- <p>See <seealso marker="#tf/3">tf/N</seealso> for arguments and return value description.</p>
- <p>Clear process trace flags on all or specified nodes. When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctf_all(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <name>ctf_all() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Clear all process trace flags</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Clears all trace flags on all or specified nodes. Just for convenience.</p>
- </desc>
- </func>
- <func>
- <name>init_tpm(Mod,Func,Arity,CallFunc) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>init_tpm(Nodes,Mod,Func,Arity,CallFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <name>init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>init_tpm(Nodes,Mod,Func,Arity, InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Initialize meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- <v>InitFunc,RemoveFunc = {Module,Function} | function()/4 | void</v>
- <v>CallFunc = ReturnFunc = {Module,Function} | function()/3 | void</v>
- </type>
- <desc>
- <p>Initializes <c>Mod:Func/Arity</c> for meta tracing without setting any meta trace patterns. This is necessary if the named match specs will be used (see <seealso marker="#tpm_ms/5">tpm_ms/5,6</seealso>). Otherwise initialization of public loop data can be done at the same time as setting meta trace patterns using <seealso marker="#tpm/8">tpm/8,9</seealso>.</p>
- <p>Note that we can not use wildcards here (even if it is perfectly legal in Erlang). It also sets the <c>CallFunc</c> and <c>ReturnFunc</c> for the meta traced function. That is the functions which will be called when a function call and a return_trace meta trace message respectively arrives to the inviso meta tracer for <c>Mod:Func/Arity</c>.</p>
- <p>This function is also available without <c>InitFunc</c> and <c>RemoveFunc</c>. That means that no initialization of the public loop data structure will be done and that <c>CallFunc</c> and <c>ReturnFunc</c> must either use already existing parts of public loop data structure or not use it at all.</p>
- <p>The <c>InitFunc</c> initializes the already existing public loop data structure for use with <c>Mod:Func/Arity. InitFunc(Mod,Func,Arity,PublLD) -> {ok,NewPublLD,Output}</c> where <c>OutPut</c> can be a binary which will then be written to the trace information file. If it is not a binary, no output will be done. <c>RemoveFunc</c> will be called when the meta tracing is cleared with <seealso marker="#ctpm/3">ctpm/3,4</seealso>. <c>RemoveFunc(Mod,Func,Arity,PublLD) -> {ok,NewPublLD}</c>.</p>
- <p>See <seealso marker="#tpm/4">tpm/N</seealso> for details on <c>CallFunc</c> and <c>ReturnFunc</c>.</p>
- </desc>
- </func>
- <func>
- <name>tpm(Mod,Func,Arity,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm(Nodes,Mod,Func,Arity,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm(Nodes,Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm(Nodes,Mod,Func,Arity,MS, InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Activate meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>MS = [match_spec()]</v>
- <v>Nodes = [Node]</v>
- <v>InitFunc = RemoveFunc = {Module,Function} | function()/4 | void</v>
- <v>CallFunc = ReturnFunc = {Module,Function} | function()/3 | void</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,1} | {ok,0} | {error,Reason}1</v>
- </type>
- <desc>
- <p>Activates meta-tracing in the inviso_rt_meta tracer. Except when using <c>tpm/6</c>, <c>tpm/8</c> and <c>tpm/9</c> the <c>Mod:Func/Arity</c> must first have been initiated using <seealso marker="#init_tpm/4">init_tpm/N</seealso>. When calling several nodes, the nodes are called in parallel.</p>
- <p><c>CallFunc</c> will be called every time a meta trace message arrives to the inviso meta tracer because of a call to <c>Func</c>. <c>CallFunc(CallingPid,ActualArgList,PublLD) -> {ok,NewPrivLD,Output}</c> where <c>Output</c> can be a binary or <c>void</c>. If it is a binary it will be written to the trace information file.</p>
- <p><c>ReturnFunc</c> will be called every time a meta return_trace message arrives to the inviso meta tracer because of a return_trace of a call to <c>Func</c>. <c>ReturnFunc(CallingPid,ReturnValue,PublLD) -> {ok,NewPrivLD,Output}</c>. Further the <c>ReturnFunc</c> must handle the fact that a return_trace message arrives for a call which was never noticed. This because the message queue of the meta tracer may have been emptied.</p>
- </desc>
- </func>
- <func>
- <name>tpm_tracer(Mod,Func,Arity,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_tracer(Nodes,Mod,Func,Arity,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | NodeResults | {error,Reason}</name>
- <name>tpm_tracer(Nodes,Mod,Func,Arity,MS, InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Activate meta tracing and at the same time append a {tracer,Tracer} process trace flag to the enable list in a match specification <c>trace</c>action term</fsummary>
- <desc>
- <p>See tpm/X for details on arguments and return values.</p>
- <p>Same as tpm/X but all match specs in <c>MS</c> containing a <c>trace</c> action term will have a <c>{tracer,Tracer}</c> appended to its enable-list. <c>Tracer</c> will be the current output for regular trace messages as specified when tracing was initiated. This function is useful when setting a meta trace pattern on a function with the intent that its execution shall turn tracing on for the process executing the match-spec in the meta trace pattern. The reason the <c>tracer</c> process trace flag can not be explicitly written in the action term by the user is that it may be difficult to learn its exact value for a remote node. Further more inviso functions are made to work on several nodes at the same time, requiring different match specs to be set for different nodes.</p>
- <p>Simple example: We want any process executing the function <c>mymod:init(1234)</c> (with the argument, exactly the integer 1234) to begin function-call tracing. In the example, if the process is found to be one that shall start call tracing, we also first disable <c>all</c> process trace flags to ensure that we have full control over what the process traces. <c>void</c> in the example specifies that the meta-tracer (inviso_rt_meta) will not call any function when meta trace messages for <c>mymod:init/1</c> arrives. There is no need for a <c>CallFunc</c> since the side-effect (start call-tracing) is achieved immediately with the match-spec.</p>
- <code type="none">
- inviso:tpm_tracer(mymod,init,1,[{[1234],[],[{trace,[all],[call]}]}],void). </code>
- <p>This will internally, by the meta tracer on each Erlang node, be translated to:</p>
- <code type="none">
- erlang:trace_pattern({mymod,init,1},[{[1234],[],[{trace,[all],[call,{{tracer,T}}]}]}],[{meta,P}]).
- </code>
- <p>Where <c>T</c> is the tracer for regular trace messages (most often a trace-port, but can be the runtime component inviso_rt process), and <c>P</c> is the meta tracer (the inviso_rt_meta process).</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms(Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_ms(Nodes,Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Add match specifications</fsummary>
- <type>
- <v>Nodes = [Node]&lt;v> &lt;v>Mod = Func = atom()&lt;v> &lt;v>Arity = int()&lt;v> &lt;v>MSname = term()&lt;v> &lt;v>MS = [match_spec()]&lt;v> &lt;v>NodeResults = [{Node,NodeResult}]&lt;v> &lt;v>NodeResult = {ok,1} | {ok,0} | {error,Reason}&lt;v></v>
- </type>
- <desc>
- <p>This function adds a list of match-specs to the already existing ones. It uses an internal database to keep track of existing match-specs. This set of match specs can hereafter be referred to with the name <c>MSname</c>. If the match-spec does not result in any meta traced functions (for whatever reason), the <c>MS</c> is not saved in the database. The previously known match-specs are not removed. If <c>MSname</c> is already in use as a name referring to a set of match-specs for this particular meta-traced function, the previous set of match-specs are replaced with <c>MS</c>.</p>
- <p><c>Mod:Func/Arity</c> must previously have been initiated in order for this function to add a match-spec.</p>
- <p>When calling several nodes, the nodes are called in parallel. <c>{ok,1}</c> indicates success.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms_tracer(Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_ms_tracer(Nodes,Mod,Func,Arity,MSname,MS) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Add match specifications and at the same time append a {tracer,Tracer} process trace flag to the enable list in a match specification <c>trace</c>action term</fsummary>
- <desc>
- <p>See tpm_ms/X for details on arguments and return values, and tpm_tracer/X for explanations about the appending of <c>{tracer,Tracer}</c> process trace flag.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_ms(Mod,Func,Arity,MSname) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm_ms(Nodes,Mod,Func,Arity,MSname) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Remove a match specification</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Removes a named match-spec from the meta traced function. Note that it never is a fault to remove a match spec. Not even from a function which is non existent.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>ctpm(Mod,Func,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm(Nodes,Mod,Func,Arity) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Remove a meta trace pattern</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Removes the meta trace pattern for the function, means stops generating output for this function. The public loop data structure may be cleared by the previously entered <c>RemoveFunc</c>.</p>
- <p>When calling several nodes, the nodes are called in parallel.</p>
- </desc>
- </func>
- <func>
- <name>tpm_localnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_localnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>Quick version for setting meta-trace patterns on <c>erlang:register/2</c>. It uses a default <c>CallFunc</c> and <c>ReturnFunc</c> in the meta-tracer server. The main purpose of this function is to create ti-log entries for associations between pids and registered name aliases. The implementation uses return_trace to see if the registration was successful or not, before actually making the ti-log alias entry. Further the implementation also meta traces the BIF <c>unregister/1</c>.</p>
- <p>If both <c>N1</c> and <c>N2</c> is 1, function call was successful. <c>N1</c> and <c>N2</c> represent setting meta trace pattern on <c>register/2</c> and <c>unregister/1</c>.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_localnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm_localnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Function for removing previously set patters by <seealso marker="#tpm_localnames/0">tpm_localnames/0</seealso>. The two results <c>R1</c> and <c>R2</c> represents that meta pattern is removed from both <c>register/2</c> and <c>unregister/1</c>.</p>
- </desc>
- </func>
- <func>
- <name>tpm_globalnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>tpm_globalnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>Quick version for setting meta-trace patterns capable of learning the association of a pid with a globally registered name (registered using <c>global:register_name</c>). The implementation meta-traces on <c>global:handle_call({register,'_','_','_'},'_','_')</c> and <c>global:delete_global_name/2</c>. The <c>N1</c> and <c>N2</c> represents the success of the two sub-tmp calls.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_globalnames() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctpm_globalnames(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Function for removing previously set meta patters by <seealso marker="#tpm_globalnames/0">tpm_globalnames/0,1</seealso>. The two results <c>R1</c> and <c>R2</c> represents that meta pattern are removed from both <c>global:handle_call/3</c> and <c>global:delete_global_name/1</c>.</p>
- </desc>
- </func>
- <func>
- <name>ctp_all() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>ctp_all(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Clear all (global and local) trace patterns</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Clears all, both global and local trace patterns. Does not clear meta trace patterns. Equivalent to a call to <seealso marker="#ctp/4">ctp/3,4</seealso> and to <seealso marker="#ctpl/4">ctpl/3,4</seealso> with wildcards <c>'_'</c> for all modules, functions and arities.</p>
- </desc>
- </func>
- <func>
- <name>suspend(SReason) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>suspend(Nodes,SReason) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Suspend runtime components</fsummary>
- <type>
- <v>SReason = term()</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Suspends the runtime components. <c>SReason</c> will become the suspend-reason replied in for instance a <seealso marker="#get_status/0">get_status/0,1</seealso> call. A runtime component that becomes suspended removes all trace flags and all meta trace patterns. In that way trace output is no longer generated. The task of reactivating a suspended runtime component is outside the scoop of inviso. It can for instance be implemented by a higher layer trace-tool "remembering" all trace flags and meta patterns set.</p>
- </desc>
- </func>
- <func>
- <name>cancel_suspension() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>cancel_suspend(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Reactivate suspended runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>Makes the runtime components <c>running</c> again (as opposite to <c>suspended).</c> Since reactivating previous trace flags and meta trace patterns is outside the scoop of inviso, cancelling suspension is simply making it possible to set trace flags and meta trace patterns again.</p>
- </desc>
- </func>
- <func>
- <name>get_status() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>get_status(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Get status of runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,{State,Status}} | {error,Reason}</v>
- <v>State = new | idle | tracing</v>
- <v>Status = running | {suspended,SReason}</v>
- <v>SReason = term()</v>
- </type>
- <desc>
- <p>Finds out the state and status of a runtime component. A runtime component is in state <c>new</c> before it has been initiated to do any tracing the first time. There are clear-functions which can make a runtime component become <c>new</c> again without having to restart. A runtime component becomes <c>idle</c> after tracing is stopped.</p>
- </desc>
- </func>
- <func>
- <name>get_tracerdata() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>get_tracerdata(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <fsummary>Get tracerdata of runtime components</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,NResult} | {error,Reason}</v>
- <v>NResult = TracerData | no_tracerdata</v>
- </type>
- <desc>
- <p>Returns the current tracerdata of a runtime component. A runtime component in state <c>new</c> can not have tracerdata. An <c>idle</c> runtime component does have tracerdata, the last active tracerdata. <c>TracerData</c> will be a term as specified to <c>init_tracing</c> when tracing was initiated for the runtime component.</p>
- </desc>
- </func>
- <func>
- <name>list_logs() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>list_logs(Nodes) -> {ok,NodeResults} | {error,Reason}</name>
- <name>list_logs(NodeTracerData) -> {ok,NodeResults} | {error,Reason}</name>
- <name>list_logs(TracerData) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Get log file names associated with tracerdata</fsummary>
- <type>
- <v>TracerData -- see init_tracing/1,2</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,FileList} | {ok,no_log} | {error,Reason}</v>
- <v>&nbsp;FileList = [FileType]</v>
- <v>&nbsp;&nbsp;FileType = {trace_log,Dir,Files} | {ti_log,Dir,Files}</v>
- <v>&nbsp;&nbsp;Files = [FileNameWithOutPath]</v>
- </type>
- <desc>
- <p>Returns the actually existing log files associated with <c>TracerData</c>. If a tracerdata is not specified, current tracerdata is used for that particular runtime component. <c>Files</c> will be a list of one or more files should it be a wrap-set. Otherwise the it is a list of only one filename.</p>
- <p>This function is useful to learn the name and path of all files belonging to a trace. This information can later be used to move those files for merging. Note that since it is possible to ask on other tracerdata than the current, it is possible to learn filenames of previously done traces, under the circumstances that they have not been removed.</p>
- </desc>
- </func>
- <func>
- <name>fetch_log(LogSpecList,DestDir,Prefix) -> {ok,NodeResults} | {error,not_distributed} | {error,Reason} </name>
- <name>fetch_log(DestDir,Prefix) -> {ok,NodeResults} | {error,not_distributed} | {error,Reason}</name>
- <fsummary>Fetch log files to control component node</fsummary>
- <type>
- <v>DestDir = string()</v>
- <v>Prefix = string()</v>
- <v>LogSpecList = [LogSpec]</v>
- <v>&nbsp;LogSpec = {Node,FileSpecList} | Node | {Node,TracerData}</v>
- <v>TracerData = see init_tracing/1,/2</v>
- <v>FileSpecList = [{trace_log,Dir,FileList},{ti_log,Dir,FileList}] | [{trace_log,Dir,FileList}]</v>
- <v>&nbsp;FileList = [RemoteFileName]</v>
- <v>NodeResult = {Conclusion,ResultFileSpec} | no_log | {error,NReason}</v>
- <v>&nbsp;NReason = own_node | Reason</v>
- <v>&nbsp;Conclusion = complete | incomplete</v>
- <v>&nbsp;ResultFileSpec = [{trace_log,FileResults},{ti_log,FileResults}]</v>
- <v>&nbsp;&nbsp;FileResults = [FileResult]</v>
- <v>&nbsp;&nbsp;&nbsp; FileResult = {ok,FileName} | {error,FReason}</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;FReason = {file_open,{posix(),FileName}} | {file_open,{posix(),RemoteFileName}} | {file_open,{posix(),[DestDir,Prefix,RemoteFileName]}} | {file_write,{posix(),FileName}} | {truncated,FileName} | {truncated,{Reason,FileName}}</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;posix() = atom()</v>
- </type>
- <desc>
- <p>Copies log files over distributed erlang to the control component node. This function can only be used in a distributed system.</p>
- <p>The resulting transferred files will have the prefix <c>Prefix</c> and will be located in <c>DestDir</c>. The source files can either be pointed out using a <c>FileListSpec</c> or tracerdata. If no files are explicitly specified, current tracerdata for that node will be used. Note that if source files have the same name (on several nodes) they will overwrite each other at <c>DestDir</c>.</p>
- </desc>
- </func>
- <func>
- <name>delete_log(Nodes,TracerData) -> {ok,NodeResults} | {error,Reason}</name>
- <name>delete_log(NodeSpecList) -> {ok,NodeResults} | {error,Reason}</name>
- <name>delete_log(Spec) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>delete_log(TracerData) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <name>delete_log() -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Delete log files associated with tracerdata</fsummary>
- <type>
- <v>Nodes = [Node]</v>
- <v>NodeSpecList = [{Node,Spec}]</v>
- <v>&nbsp;Spec = [AbsPathFileName] | LogSpecs</v>
- <v>&nbsp;&nbsp;LogSpecs = [LogSpec]</v>
- <v>&nbsp;&nbsp;&nbsp;LogSpec = {trace_log,Dir,[FileNameWithoutPath]} | {ti_log,Dir,[FileNameWithoutPath]}</v>
- <v>TracerData -- see init_tracing/1,/2</v>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = {ok,no_log} | {ok,LogInfos} | {ok,FileInfos}</v>
- <v>&nbsp;LogInfos = [LogInfo]</v>
- <v>&nbsp;&nbsp;LogInfo = {trace_log,FileInfos} | {ti_log,FileInfos}</v>
- <v>&nbsp;FileInfos = [FileInfo]</v>
- <v>&nbsp;&nbsp;FileInfo = {ok,FileName} | {error,Reason} </v>
- </type>
- <desc>
- <p>Deletes listed files or files corresponding to tracerdata. If no tracerdata or list of files are specified in the call, current tracerdata at the runtime components will be used to identify files to delete. All filenames shall be strings.</p>
- <p><c>FileName</c> can either be an absolute path or just a filename depending on if <c>AbsPathFileName</c> or a <c>LogSpec</c> was used to identify the file.</p>
- </desc>
- </func>
- <func>
- <name>subscribe() -> ok | {error,Reason}</name>
- <name>subscribe(Pid) -> ok | {error,Reason}</name>
- <fsummary>Subscribe to Inviso events</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
- <desc>
- <p>Adds <c>Pid</c> or <c>self()</c> if using <c>subscribe/0</c> to the inviso-event sending list. Note that it is possible to add a pid several times and that the <c>Pid</c> then will receive multiple copies of inviso-event messages.</p>
- <p>All events will be sent to all subscribers in the event sending list.</p>
- <code type="none">
-Event = {inviso_event,ControllerPid,erlang:localtime(),Msg}
- Msg = {connected, Node, {RTtag, {State,Status}}}
- | {disconnected, Node, NA}
- | {state_change,Node,{State,Status}}
- | {port_down,Node,Reason}
- Node = node() | local_runtime
- </code>
- <p>Subscribing to inviso-event may be necessary for a higher layer trace-tool using inviso to follow the runtime components. <c>local_runtime</c> will be used for a runtime component running in a non-distributed environment.</p>
- </desc>
- </func>
- <func>
- <name>unsubscribe() -> ok</name>
- <name>unsubscribe(Pid) -> ok</name>
- <fsummary>Unsubscribe to Inviso events</fsummary>
- <desc>
- <p>Removes <c>Pid</c> (once) from the subscription list.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_as_lib.xml b/lib/inviso/doc/src/inviso_as_lib.xml
deleted file mode 100644
index 1f4961166c..0000000000
--- a/lib/inviso/doc/src/inviso_as_lib.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_as_lib</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_as_lib</module>
- <modulesummary>The Inviso Autostart Utility Library</modulesummary>
- <description>
- <p>The purpose of the Inviso autostart utility library is to facilitate the creation and modification of autostart configuration files used by the standard autostart.</p>
- </description>
- <funcs>
- <func>
- <name>setup_autostart(Node, R, Opts, TracerData, CmdFiles, Bindings, Transl, RTtag) -> ok | {error, Reason}</name>
- <fsummary>Create an autostart configuration file</fsummary>
- <type>
- <v>Node = atom()</v>
- <v>R = int()</v>
- <v>Opts -- see inviso:add_nodes/2,3</v>
- <v>TracerData -- see inviso:init_tracing/1,2</v>
- <v>CmdFiles = [CmdFile]</v>
- <v>&nbsp;CmdFile = string()</v>
- <v>Bindings = [{Var,Val}]</v>
- <v>&nbsp;Var = atom()</v>
- <v>&nbsp;Val = term()</v>
- <v>Transl = [{{M1,F1,Arity}, {M2,F2,{Mt,Ft}}}]</v>
- <v>&nbsp;M1 = F1 = M2 = F2 = Mt = Ft = atom()</v>
- <v>&nbsp;Arity = int()</v>
- <v>RTtag = term()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Creates an autostart configuration file on <c>Node</c>. The name of the file is automatically deducted from consulting the Runtime_Tools configuration parameters at <c>Node</c>.</p>
- <p><c>R</c> is the number of allowed autostarts remaining.</p>
- <p><c>Opts</c> is the options which shall be given to the runtime component. See <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2,3</seealso>.</p>
- <p><c>TracerData</c> is used when initiating tracing on this node. See <seealso marker="inviso#init_tracing/1">inviso:init_tracing/1,2</seealso>.</p>
- <p><c>CmdFiles</c> points out files containing instructions understood by the <c>inviso_autostart_server</c> implementation of an autostart initiator.</p>
- <p><c>Bindings</c> is a list of <c>{Var, Val}</c> tuples, where <c>Var</c> is the name of a variable and <c>Val</c> the actual value of the variable.</p>
- <p><c>Transl</c> means that <c>M1:F1/Arity</c> shall be translated into <c>M2:F2</c>.</p>
- <p><c>RTtag</c> is the incarnation tag of the runtime component. See See <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2,3</seealso>.</p>
- </desc>
- </func>
- <func>
- <name>set_repeat(Node, R) -> ok | {error, Reason}</name>
- <fsummary>Set the repeat parameter in the autostart file</fsummary>
- <type>
- <v>Node = atom()</v>
- <v>R = int()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Sets the repeat parameter in the autostart file at <c>Node</c> without changing any of its other contents. The autostart configuration file must exist.</p>
- <p><c>R</c> is the number of allowed autostarts remaining.</p>
- </desc>
- </func>
- <func>
- <name>inhibit_autostart(Node) -> ok | {error, Reason}</name>
- <fsummary>Set the repeat parameter in the autostart file to 0</fsummary>
- <type>
- <v>Node = atom()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Sets the repeat parameter in the autostart file at <c>Node</c> to 0. Equivalent to <c>set_repeat(Node, 0)</c>.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_chapter.xml b/lib/inviso/doc/src/inviso_chapter.xml
deleted file mode 100644
index b69fb97586..0000000000
--- a/lib/inviso/doc/src/inviso_chapter.xml
+++ /dev/null
@@ -1,362 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2006</year><year>2011</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>inviso_chapter.xml</file>
- </header>
-
- <section>
- <p><em>inviso</em>: (Latin) to go to see, visit, inspect, look at.</p>
- <warning>
- <p>The <c>inviso</c> application is deprecated and will be
- removed in the R16 release.</p>
- </warning>
- <title>Introduction</title>
- <p>The Inviso trace system consists of one or several runtime components supposed to run on each Erlang node doing tracing and one control component which can run on any node with available processor power. Inviso may also be part of a higher layer trace tool. See the inviso-tool as an example. The implementation is spread out over the Runtime_tools and the Inviso Erlang/OTP applications. Erlang modules necessary to run the runtime component are located in Runtime_tools and therefore assumed to be available on any node. Even though Inviso is introduced with Erlang/OTP R11B the runtime component implementation is done with backward compatibility in mind. Meaning that it is possible to compile and run it on older Erlang/OTP releases.</p>
- <image file="inviso_users_guide_pic1.gif">
- <icaption>Inviso Trace System Architecture Overview.</icaption>
- </image>
- <p>This document describes the control and runtime components of the Inviso trace system.</p>
-
- <section>
- <title>Underlying Mechanisms</title>
- <p>Inviso is built on Erlang trace BIFs and standard linked in trace-port drivers for efficient trace message logging. This means that Inviso can not co-exist in runtime with any other trace tool using the trace BIFs.</p>
- </section>
-
- <section>
- <title>Trace Recepie</title>
- <p>This is a short step-by-step description of how tracing using Inviso can be done.</p>
- <list type="ordered">
- <item>Start the Inviso control component at any node. Preferably a node that is not participating in the "work" done by your "system". The control component runs independently and normally not linked to any other process. (Prompt 2 in the example below.)</item>
- <item>Add all Erlang nodes to the inviso control component where you want to trace. This is starting runtime components on all involved Erlang nodes. Can include the node where the control component runs as well. Note that the Runtime_Tools application must be running on the nodes where runtime components shall be started. (Prompt 1 and 3 in the example below.)</item>
- <item>Initiate tracing on the added nodes. Initiating tracing means "opening" the output to where trace-messages will be written. Most commonly this is a file. Note that it is not actually necessary to initiate the same tracing on all nodes. It might for instance be wise to use different filenames on different nodes. In the example below tracing is initiated on two nodes. The same node as where the shell is running (<c>node()</c>) and at <c>node2@hurin</c>. Further both "regular" tracing (<c>trace</c>) as well as trace information (<c>ti</c>) are specified for both nodes. (Prompt 4 in the example below).</item>
- <item>If needing pid-to-alias translations, activate meta tracing on the necessary functions. This requires that trace information was specified when initiating tracing. (Prompt 5 in the example below illustrates using pid to locally registered name translations).</item>
- <item>Set trace-patterns on the functions that shall be traced. (Prompt 6 in the example below).</item>
- <item>Set process trace flags on necessary processes. Do not forget to use the <c>timestamp</c> flag in order to be able to merge log files together in chronological order. (Prompt 7 in the example below).</item>
- <item>Run your code. (Prompt 8 in the example below).</item>
- <item>Stop tracing (opposite of initiate tracing) and clear trace-patterns on the nodes. It is actually not necessary to stop tracing on all nodes at once. Nodes no longer of interest can be made to stop tracing before others. (Prompt 9 in the example below stops tracing. Prompt 13 removes all trace flags and trace patterns. Removing trace flags are really not necessary since those will be removed when the runtime components are stopped. Removing trace patterns may many times be necessary to "return" the node to a "clean" state from a trace perspective. Trace patterns are never automatically cleared by the runtime system unless the Erlang module in question is reloaded.)</item>
- <item>If necessary fetch the log files from the various nodes. (Prompt 10 in the example blow).</item>
- <item>Merge and format the log files. (Prompt 12 in the example below).</item>
- <item>Stop the runtime components. This is important if the Erlang nodes are real "live" systems, and will not necessarily be stopped just because the tracing is completed. (Prompt 14 in the example below).</item>
- </list>
- <p>This "recipe" is valid also when tracing in a non-distributed environment. The only difference is that function calls not taking a node-name as argument are used. The runtime component will then of course run on the same node as the control component.</p>
- <p>Simple example illustrating the above listed recipe. It traces on two nodes, node1 where the control component also runs. And node2 which is a remote node from the control components perspective. The example uses a mixture of API-calls specifying what nodes to trace on and API functions working on all added nodes. This is in this example interchangeable since all to the control component known nodes are participating in the same way.</p>
- <pre>
-Eshell V5.5 (abort with ^G)
-(node1@hurin)1><input>application:start(runtime_tools).</input>
-ok
-(node1@hurin)2> <input>inviso:start().</input>
-{ok,&lt;0.56.0>}
-(node1@hurin)3> <input>inviso:add_nodes([node(),node2@hurin],mytag).</input>
-{ok,[{'node1@hurin',{ok,new}},
- {'node2@hurin',{ok,new}}]}
-(node1@hurin)4> <input>inviso:init_tracing( [{node(),[{trace,{file,"tracefile_node1.log"}},{ti,{file,"trace_node1.ti"}}]}, {node2@hurin,[{trace,{file,"tracefile_node2.log"}},{ti,{file,"trace_node2.ti"}}]}]).</input>
-{ok,[{'node1@hurin',{ok,[{trace_log,ok},{ti_log,ok}]}},
- {'node2@hurin',{ok,[{trace_log,ok},{ti_log,ok}]}}]}
-(node1@hurin)5> <input>inviso:tpm_localnames([node(),node2@hurin]).</input>
-{ok,[{'node1@hurin',{{ok,1},{ok,1}}},
- {'node2@hurin',{{ok,1},{ok,1}}}]}
-(node1@hurin)6> <input>inviso:tpl([node(),node2@hurin],code,which,'_',[]).</input>
-{ok,[{'node1@hurin',{ok,[2]}},
- {'node2@hurin',{ok,[2]}}]}
-(node1@hurin)7> <input>inviso:tf(all,[call,timestamp]).</input>
-{ok,[{'node1@hurin',{ok,"/"}},
- {'node2@hurin',{ok,"-"}}]}
-(node1@hurin)8> <input>code:which(ordset).</input>
-non_existing
-(node1@hurin)9> <input>inviso:stop_tracing().</input>
-{ok,[{'node1@hurin',{ok,idle}},
- {'node2@hurin',{ok,idle}}]}
-(node1@hurin)10> <input>inviso:fetch_log([node2@hurin],".","aprefix_").</input>
-{ok,[{'node2@hurin',
- {complete,[{trace_log,[{ok,"aprefix_tracefile_node2.log"}]},
- {ti_log,[{ok,"aprefix_trace_node2.ti"}]}]}}]}
-(node1@hurin)11> <input>inviso:list_logs([node()]).</input>
-{ok,[{'node1@hurin',
- {ok,[{trace_log,".",["tracefile_node1.log"]},
- {ti_log,".",["trace_node1.ti"]}]}}]}
-(node1@hurin)12> <input>inviso_lfm:merge( [{node(),[{trace_log,["tracefile_node1.log"]}, {ti_log,["trace_node1.ti"]}]}, {node2@hurin,[{trace_log,["aprefix_tracefile_node2.log"]}, {ti_log,["aprefix_trace_node2.ti"]}]}],"theoutfile.txt").</input>
-{ok,15}
-(node1@hurin)13> <input>inviso:clear().</input>
-{ok,[{'node1@hurin',{ok,{new,running}}},
- {'node2@hurin',{ok,{new,running}}}]}
-(node1@hurin)14> <input>inviso:stop_nodes().</input>
-{ok,[{'node2@hurin',ok},
- {'node1@hurin',ok}]}
-(node1@hurin)15> </pre>
- </section>
- </section>
-
- <section>
- <title>Incarnation runtime tags</title>
- <p>Incarnation runtime tags are used to identify an incarnation of a runtime component. An incarnation is one "start-up" of a runtime component on a specific Erlang node. The reason why it can sometimes be necessary to examine the incarnation runtime tag is that a user wants to connect, adopt, an already running runtime component. This may be the case if the runtime component has autostarted or because the control component terminated without killing the runtime component. While the user has been out of control of the runtime component it may very well have terminated and been restarted. If it was restarted without the user's knowledge, its incarnation runtime tag has most likely changed. The user can therefore, if the current incarnation runtime tag is not what it is supposed to be, conclude that the runtime component is not "doing" what is expected.</p>
- <p>The runtime tag is set at runtime component start-up. This is either done when it is started manually by a call to <c>inviso:add_nodes/X</c>, or according to a specification in one of the autostart configuration files.</p>
- </section>
-
- <section>
- <title>Runtime component state and status</title>
- <p>A runtime component has a state and a status. The possible states are: <c>new</c>, <c>tracing</c> and <c>idle</c>. A runtime component that is <c>tracing</c> has (possibly) open log files. A <c>new</c> runtime component has no current tracer-data. That is it lacks any history of what it has done just recently. An <c>idle</c> runtime component is no longer tracing. It does therefore have current tracer-data that describes what it did do when it was tracing.</p>
- <p>The status describes if the runtime component is <c>running</c> or suspended. A suspended runtime component may very well be in state <c>tracing</c>. However the point is that it shall not generate any processor load. It will therefore refrain from generating any trace messages.</p>
- </section>
-
- <section>
- <title>The Runtime Meta Tracer</title>
- <p>Meta tracing is a trace mechanism separate from the regular tracing. It is normally used by a trace-tool to learn about function calls made anywhere in an Erlang node. A typical example is that there is a possibility in Inviso to get pids translated to registered name in the final formatted trace-log (for processes having registered names). This is done by meta-tracing on the BIF <c>register/2</c> to learn about all name/pid associations made.</p>
- <p>Meta tracing in Inviso is done by the <c>inviso_rt_meta</c> process, which is part of the runtime component if trace-information, ti, is initiated. See <seealso marker="inviso#init_tracing/1">inviso:init_tracing/1</seealso> for details. The runtime meta tracer opens and controls the so called trace information file. Translations can then be done off-line using the associations logged in the trace information file. Currently the only type of trace information file available is a straight binary file. A wrap-file makes no sense since pid-to-name associations made in the beginning will most likely be lost.</p>
- <p>The runtime meta tracer can also be used to translate pids to own identifiers. The only thing needed is one or several association points in the form of function calls which will only be made if an association is done in the system. The pid and own-identifier must be arguments and/or return values from the same function call.</p>
- <p>The runtime meta tracer can further more be used to achieve side-effects during tracing, like turning tracing on or off.</p>
-
- <section>
- <title>Matching function calls with return values</title>
- <p>It may sometimes be necessary to wait for a meta traced function to return before it can be decided what to do. This may be due to that one piece of information to make the decision is in the arguments to the function, the other in the return value. This kind of logic can be programmed to be executed by the inviso meta tracer. In order for the inviso meta tracer to "remember" function-call arguments until the function return trace message arrives, a <c>public loop data structure</c> is implemented. The public loop data structure is first created when tracing is initiated (of course only when trace information is specified in the <em>init_tracing</em> call). The public loop data can then later be further initiated each time meta tracing (<c>tpm</c> and <c>tpm_ms</c>) is activated for a certain function.</p>
- <p>The default public loop data structure is a tuple of size two. The first element in that tuple is used by the predefined meta tracing for capturing locally registered names. The second element is free to use for any other purpose. The elements of the tuple must in the default implementation be lists of tuples. Where each sub-tuple shall represent one waiting call. The last element of that tuple must be a now-stamp (as returned by the BIF <c>now/0</c>). See below for an explanation of the now-stamp. The size of the outer most tuple may be increased as long as the term residing in the first element is left unchanged, and all other elements follow the above described rules.</p>
- <p>The inviso meta tracer "cleans" the public loop data structure approximately once every minute. The reason for this is that entries in the public loop data structure may become abandoned. If for instance a process crashes while executing the body of a meta traced function, no return value will be generated. Or in other words, receiving the call meta trace-message can have caused information to have been written into the public loop data structure. That entry will be used and removed when the return_trace meta trace-message arrives. But if the meta traced function causes an exception, no return_trace message will come. The function which normally removes the entry is then therefore never called.</p>
- <p>The default clean-function assumes that every item in the public loop data tuple is a list. Where each list contains tuples where the last element of those tuples are "now-stamps". The default clean-function considers an entry older than 30 seconds to be abandoned.</p>
- </section>
-
- <section>
- <title>Making pid/alias entries in the ti-file</title>
- <p>When activating meta tracing for a function for the purpose of writing pid-alias associations in the trace information file, a call-func and possibly also a return-func is specified. These functions will be called when a meta trace message arrives to the inviso meta tracer as a result of function calls or returns for this meta traced function. What exactly to write in the trace information file is dictated by the merge mechanism. This since pid-alias translations are done off line when merging log-files. See the chapter on merging and formatting log files for more details.</p>
- <p>Simple example where the call to the function <c>connection:assoc_id(Pid,Ref)</c> will associated <c>Pid</c> with the id <c>Ref</c>. We will then in a merged log-file see a translation between <c>Pid</c> and <c>Ref</c>. Actually for all future since there is no unalias function meta traced in this example. The inviso meta tracer will receive a meta trace message every time <c>connection:assoc_id/2</c> is called. When that message arrives the meta tracer will call <c>mytrace:call_assoc_id/3</c> which must return <c>{ok,NewPublicLoopData,OutPutBinary}</c>.</p>
- <code type="none">
- -module(mytrace).
-
- call_assoc_id(_CallingPid,[Pid,Ref],PublLoopData) ->
- {ok,PublLoopData,term_to_binary({Pid,Ref,alias,now()})}.
- </code>
- <pre>
-
-(node1@hurin)21> <input>inviso:tpm(connection,assoc_id,2,[], {mytrace,call_assoc_id}).</input>
-{ok,[{'node1@hurin',{ok,1}},
- {'node2@hurin',{ok,1}}]}
-(node1@hurin)22> </pre>
- </section>
-
- <section>
- <title>Extending the public loop data structure.</title>
- <p>It is of course very likely that the public loop data structure must be extended to host all functions where the meta tracer must delay its action until the function in question returns. What is necessary is to create your own public loop data structure at trace initialization. This is done by using the <c>TiSpec</c>. <c>TiSpec={InitMFA,RemoveMF,CleanMF}</c>, where <c>InitMFA</c> creates the structure, <c>RemoveMF</c> removes it (must often not necessary unless a database, file or similar is used as storage instead of a tuple). <c>CleanMF</c> is the function which will be called each every 60 seconds to go over the public loop data structure. Following the below rules, not much programming will be needed, apart from the <c>InitMFA</c>:</p>
- <list type="bulleted">
- <item>Make the public loop data structure a tuple of lists, where each list is a list of tuples where the tuples represents one entry.</item>
- <item>Make the <c>CallFunc</c> (the function called each time a call meta trace message arrives for the function in question) add a tuple to the correct list where the last element of that tuple is a now-stamp.</item>
- <item>Make sure that the first element in the loop data structure tuple is left alone for the default implementation of the handling of registered names.</item>
- <item>Use <c>inviso_rt_meta:clean_std_publld/1</c> (which is exported for this purpose) as <c>CleanMF</c>. This function is normally the default clean function, if not using the possibility to in detail initiate the inner workings of the inviso meta tracer.</item>
- </list>
- <p>Simple example where tracing is initiated with a public loop data structure having 10 places for nine (the locally registered names is mandatory) different functions to be meta traced. Note that the BIF <c>list_to_tuple/1</c> is used as initialization function. And that the Stdlib function <c>lists:duplicate/2</c> is used to create something for the initialization function to work on.</p>
- <pre>
-(node1@hurin)4> <input>inviso:init_tracing( [{node2@hurin,[{trace,{file,"tracefile_node2.log"}}, {ti,{file,"trace_node2.ti",{{erlang,list_to_tuple,[lists:duplicate(10,[])]}, void,{inviso_rt_meta,clean_std_publld}}}}]}]).</input>
-{ok,[{'node2@hurin',{ok,[{trace_log,ok},{ti_log,ok}]}}]}
-(node1@hurin)5> </pre>
- </section>
-
- <section>
- <title>Using the inviso meta tracer to achieve side effects</title>
- <p>Since meta tracing is independent of regular tracing and catches any function call to a particular function made in any process, it is well suited to be used to turn things on or off during execution. That trick is done by letting the <c>CallFunc</c> and (if used) <c>ReturnFunc</c> do these sideeffects. One must of course remember that the inviso meta tracer is a process amongst all other processes in the system. Meaning that the side effect is not necessarily done exactly when the meta traced function is called. Unless the side effect can be achieved using a match specification action.</p>
- </section>
- </section>
-
- <section>
- <title>Runtime Component Autostart</title>
- <p>In order to trace before any user interaction is possible, an autostart mechanism is implemented. The runtime component is started by the top supervisor of the Runtime_Tools application top supervisor. Hence the Runtime_Tools application must be part of the boot script for autostart tracing to work. The Runtime_Tools applications must of course be started before any application that is to be traced. Do note that application startup is not entirely synchronous. Meaning that just because the application controller has begun starting the next application, Runtime_Tools is not necessarily fully up and running.</p>
- <p>The autostart mechanism is configurable. The runtime component comes with a standard autostart configuration, only missing two text-files to be completely operational.</p>
-
- <section>
- <title>Autostart Configuration</title>
- <p>The autostart is controlled by the Runtime_Tools application configuration parameter <c>inviso_autostart_mod</c>. It must be the name of a module exporting an <c>autostart/1</c> function. The default value is <c>inviso_autostart</c>, a module which is provided with Runtime_Tools. See <seealso marker="#inviso_autostart">below</seealso> for details.</p>
- <p>An <c>autostart/1</c> function must offer the following:</p>
- <code type="none">
-autostart(RuntimeToolsArg) = {MFA,Options,Tag} | any()
- </code>
- <p><c>RuntimeToolsArgs</c> is the argument <c>Arg</c> provided to the Runtime_Tools application through the application resource file <c>{mod,{Module,Arg}}</c> parameter.</p>
- <p><c>MFA = {AutoMod,AutoFunc,AutoArgs} | any()</c> controls how tracing will be initiated. Note that initiating tracing is not necessarily the same as starting a runtime component. It is possible to have a runtime component without doing any tracing. The runtime component is started as long as <c>autostart/1</c> returns the proper tuple, and <c>Options</c> does not for instance require a certain non-existing control component. If it is not a proper tuple or there are other faults in the tuple items, the autostart will terminate. Typically will this happen if there is no <c>autostart/1</c> function.</p>
- <p>If MFA does not properly point out a function possible to call with <c>spawn(AutoMod,AutoFunc,AutoArg)</c>, there will simply be no initialization. (Initialization is done by a separate process spawned by the runtime component during autostart.) It may be worth reminding that <c>AutoMod</c> must be present at the node where the runtime component is supposed to run. Not necessarily the node where the control component usually runs.</p>
- <p><c>Options</c> is the list of options given to the runtime component. See <c>Options</c> in <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2</seealso>.</p>
- <p><c>Tag</c> is the runtime component incarnation tag. See <c>Tag</c> in <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2</seealso>.</p>
- </section>
-
- <section>
- <title>The Standard Autostart Implementation</title>
- <p>As mentioned above, Inviso comes with a complete implementation of autostart sufficient for most situations.</p>
-
- <section>
- <marker id="inviso_autostart"></marker>
- <title>inviso_autostart</title>
- <p>The default autostart module is <c>inviso_autostart</c>, provided as part of the Runtime_Tools application. Since that name is the default module name, it is not really necessary to set the <c>inviso_autostart_mod</c> configuration parameter for the Runtime_Tools application.</p>
- <p>Its <c>autostart/1</c> function reads a configuration file pointed out by the Runtime_Tools application configuration parameter <c>inviso_autostart_conf</c>. If the parameter is not present, a default file, <c>inviso_autostart.config</c> in the current working directory, will be consulted.</p>
- <p>The config file must be an ascii text file with one or more tuples ended with a dot. The following parameters are recognized:</p>
- <taglist>
- <tag><c>{repeat,N}</c></tag>
- <item>
- <p>Optional parameter where <c>N</c> specifies the maximum remaining autostarts. The autostart functionality will rewrite the configuration decreasing <c>N</c> if present. If <c>N==0</c> the autostart will be terminated.</p>
- </item>
- <tag><c>{mfa,{M,F,Args}}</c></tag>
- <item>
- <p>Optional parameter controlling how initialization shall be done. The control component will spawn a separate process to do the initializations by doing <c>spawn(M,F,Args)</c>.</p>
- </item>
- <tag><c>{options,Options}</c></tag>
- <item>
- <p>Optional parameter specifying the options for the runtime component itself. See <c>Options</c> in <seealso marker="inviso#add_nodes/2">inviso:add_nodes/2</seealso>.</p>
- </item>
- <tag><c>{tag,Tag}</c></tag>
- <item>
- <p>Optional parameter specifying the runtime component tag. If missing the default tag will be <c>default_tag</c>.</p>
- </item>
- </taglist>
- <p>Example:</p>
- <code type="none">
-{repeat,1}.
-{mfa,{inviso_autostart_server,
- init,
- [[{tracerdata,{file,"mylogfile"}},
- {cmdfiles,["a_trace_case.txt"]},
- {bindings,[{'M',mymod},{'F','_'},{'Arity','_'}]},
- {translations,[]}]]}}.
- </code>
- <p>The example file results in the start of a runtime component given no specific options. There will only be one autostart since the repeat parameter is set to 1. Tracing will be initiated by the standard initiator (<seealso marker="#autostart_server">inviso_autostart_server</seealso>). The initiator will initiate tracing opening a plain trace-port logfile (<c>"mylogfile"</c>). It will further read the <c>"a_trace_case.txt"</c> file to get instructions on what patterns and flags to set. If there are variables mentioned in the trace-case file <c>"a_trace_case.txt"</c>, it is parameterized, the variables <c>M</c>, <c>F</c> and <c>Arity</c> will get the values according to <c>bindings</c>. There will be no translations done, hence the trace-case file must be written using <c>inviso_rt</c> function calls directly.</p>
- </section>
-
- <section>
- <marker id="autostart_server"></marker>
- <title>inviso_autostart_server</title>
- <p>To further facilitate the standard autostart implementation a default initiator is implemented. To use it, simply specify it as mfa in the config file read by the standard autostart module.</p>
- <p>Its <c>init/1</c> function takes one argument on the form of a list of tuples. The following tuple-parameters are recognized:</p>
- <taglist>
- <tag><c>{tracerdata,TracerData}</c></tag>
- <item>
- <p>Specifies how tracing is initiated. See <seealso marker="inviso#init_tracing/1">inviso:init_tracing/1</seealso> for details on <c>TracerData</c>.</p>
- </item>
- <tag><c>{cmdfiles,ListOfFileNames}</c></tag>
- <item>
- <p>Specifies trace-case files which shall be executed to set the patterns and flags of the trace. See the <seealso marker="#trace_cases">Trace Cases</seealso> chapter for more details. The files will be executed in the order specified.</p>
- </item>
- <tag><c>{translations,Translations}</c></tag>
- <item>
- <p>Optional parameter specifying how functions in trace-case files shall be translated. This is useful since trace-cases can be written for higher-layer Inviso tools, but must during an autostart execute using <c>inviso_rt</c> function calls only.</p>
- <code type="none">
-Translations=
- [{{Mod1,Func1,Arity},
- {Mod2,Func2,{TranslMod,TranslFunc}}},...]
-TranslMod:TranslFunc(ListOfOrigArgs)->
- ListOfTransformedArgs
- </code>
- <p><c>Mod1:Func1/Arity</c> specifies the function that shall be translated into <c>Mod2:Func2/Arity</c>. The actual arguments will be translated with <c>TranslMod:TranslFunc/1</c>. The translation function shall take a list of the actual arguments, and return a list of new arguments. The return-value list may for instance have certain arguments removed, if such are not relevant to the <c>Func2</c> function. (Such arguments must actually be removed since the return-value list from the translation function must have the correct amount of elements corresponding to the arity of <c>Func2</c>.)</p>
- </item>
- <tag><c>{bindings,Bindings}</c></tag>
- <item>
- <p><c>Bindings=[{Var,Val}]</c> <br></br>
-<c>Var=atom()</c>, the name of the variable</p>
- <p>Optional parameter specifying the actual values of variables used in the trace-cases. <c>Bindings</c> is a bindings structure as used by functions in the <c>erl_eval</c> module.</p>
- </item>
- </taglist>
- </section>
-
- <section>
- <title>The Standard Autostart Utility Library</title>
- <p>To facilitate creating the configuration file described above, there are functions in a module named <c>inviso_as_lib</c> which can both create new files according to supplied arguments and update existing configuration files.</p>
- <p>The node(s) in question must be running since the functionality in the utility library uses distributed Erlang to access the file system.</p>
- </section>
- </section>
- </section>
-
- <section>
- <title>The Dependency Property</title>
- <p>In order to protect real "live" systems from getting a runtime component lingering around without a control component, a dependency property can be specified at runtime component start-up. The property specifies a dependency in milliseconds. Meaning that if the property is set to 0 (zero), the runtime component will terminate immediately if its current control component terminates.</p>
- <p>If a control component tries to start a runtime component at an Erlang node where there already is a runtime component, the control component will adopt the already existing runtime component if it has no current control component. Otherwise the control component will experience an error, not being able to start a runtime component at that node.</p>
- <p>It must also be noted that an autostart runtime component is running without control component, at least before any control component adopts it.</p>
- </section>
-
- <section>
- <title>Overload Protection</title>
- <p>Since Inviso is intended to be used on real "live" systems, it is possible to protect the system against overload, having Inviso suspend tracing should an overload situation occur.</p>
- <p>What indicates an overload situation must be programmed and configured outside of Inviso. Inviso can initiate an overload protection, call an overload function periodically and clean-up an overload mechanism should it decide to terminate.</p>
- <p>Internally inside the runtime component, suspending tracing means removing all process trace flags and meta patterns. Reactivating tracing is outside the scoop of Inviso, but can be implemented in a tool using Inviso.</p>
- <p>Simple example adding a runtime component and making it protect its Erlang node from overload.</p>
- <pre>
-inviso:add_node(my_rt_tag,
- [{overload,{{my_ovl,check},
- 15000,
- {my_ovl,start,[my_port_pgm]},
- {my_ovl,stop,[my_port_pgm]}}}]).
- </pre>
- <p>Immediately when the runtime component is started, it will initiate overload protection by calling <c>my_ovl:start(my_port_pgm)</c>. When tracing (not when in state idle or new), the runtime component will every 15000 milliseconds call <c>my_ovl:check/1</c>. Depending on its return value, the runtime component will either do nothing or suspend tracing. When the runtime component is stopped, <c>my_ovl:stop(my_port_pgm)</c> will be called.</p>
- </section>
-
- <section>
- <title>Merging and Formatting Logfiles</title>
- <p>If logging trace messages to a logfile has been used (decided when tracing is initiated) the various log files will be located on the different Erlang nodes participating in the trace. The log files must be merged and formatted for the following reasons:</p>
- <list type="bulleted">
- <item>The various log files from the different nodes must be merged into one logfile in chronological order (where trace messages from different nodes will be mixed). If only one Erlang node participated in the trace, this step is obviously not necessary.</item>
- <item>Trace-port log files are on binary format and must in most cases be transformed in some way. This can for instance be to a text-file format or inserted into a database for analysis.</item>
- <item>Use trace information data to translate process identifiers to aliases, both standard Erlang ones (as registered names) as well as own invented.</item>
- </list>
- <p>The first step before any merging can take place is of course to get all log files, including any trace information files to a location where the logfile merger can access them. This can either be done by simply copying the files. However if the file systems on the Erlang nodes are not that easily accessed, there is a <c>fetch_log</c> function implemented in the runtime component. It will transfer log files using distributed Erlang.</p>
- <p>Inviso comes with two Erlang modules, <c>inviso_lfm</c> and <c>inviso_lfm_tpfreader</c>, implementing a standard log file merger and formatter. The log file merger (<c>inviso_lfm</c>) uses a file reader process (implemented in <c>inviso_lfm_tpfreader</c>) to access log entries in parallel. It is possible to write your own logfile reader. This is necessary since you may have your own trace-log format and/or own trace information log format. The logfile merger can further more be configured to use your own formatter, customizing what to do with a trace message.</p>
- <p>Trace messages in the log files must of course be time-stamped for the logfile merger to be capable of correctly merging them. This means using the <c>timestamp</c> process trace flag.</p>
- <p>The standard inviso log-file reader understands the following trace information file entries:</p>
- <list type="bulleted">
- <item>
- <code type="none">
-{Pid,Alias,alias,NowStamp} </code>
- </item>
- <item>
- <code type="none">
-{Pid,Alias,unalias,NowStamp} </code>
- </item>
- </list>
- <p>The <c>Pid</c> in an <c>alias</c> entry must always be a proper pid. In an <c>unalias</c> entry it may also be the atom <c>undefined</c>. The latter means that all associations involving <c>Alias</c> shall stop to be valid. The standard inviso log file reader uses the now-stamp to make sure that associations are only used during time periods in the log-file when such are valid.</p>
- </section>
-
- <section>
- <marker id="trace_cases"></marker>
- <title>Trace Cases</title>
- <p>The idea behind trace cases is that someone knowledgeable of a certain system component can write a file specifying the trace-patterns and process trace flags necessary to trace on certain items once and for all. Hence a trace case will most likely be a series of calls to functions setting trace patterns and process trace flags.</p>
- <p>However, the actual Erlang nodes and values of arguments given in the trace function calls can not be static in order for the trace cases to become useful and reusable. A trace case file must therefore be possible to parameterize. Introducing variables that will get their values at the time of trace case execution. It may also be the case that Inviso is used as a component in a higher layer trace tool. Trace cases may therefore be written calling more complex functions than the low level <c>inviso_rt</c> functions which are available to autostart mechanisms. In a matter of fact, the <c>inviso</c> API itself can be considered a higher layer. It addresses multiple nodes at once where the <c>inviso_rt</c> API can only address the local node.</p>
- <p>This results in that for trace cases to be useful there must be a function call translation mechanism and an execution environment capable of handling variable bindings.</p>
- <p>A trace-case is a text ascii file consisting of function calls written as they could have been done in the Erlang shell:</p>
- <code type="none">
-modulename:functionname(arg1,arg3,...).
- </code>
- <p>A trace-case may contain any valid function call, including binding new variables which are used later in the trace-case, but:</p>
- <list type="bulleted">
- <item>No spawn, send or receive.</item>
- <item>No apply or similar (including <c>mod:F(Arg1,Arg2)</c>). This because the variable environment is not available during the translation. Only during execution.</item>
- </list>
- <p>Example: Trace cases are expected to be written to be executed directly in an Erlang shell (by some utility reading a text file on trace case format) calling <c>inviso</c> functions. The translations must then translate <c>inviso</c> function calls to <c>inviso_rt</c> function calls, since <c>inviso</c> is not available in the Runtime_Tools applications. It can not be assumed that any trace tools outside the Runtime_Tools application is available on the nodes. Luckily (!) the <c>inviso_rt</c> API resembles the <c>inviso</c> API very much, apart from that the <c>inviso_rt</c> API does not take a list of nodes as an argument. Therefore in most situations the only transformation necessary is to change from <c>inviso</c> to <c>inviso_rt</c> and remove the first argument to the function call.</p>
- <p>Assume that we have the following trace-case file:</p>
- <code type="none">
-inviso:tpl(Nodes,mymod,'_','_',MS).
-inviso:tf(Nodes,all,[call,timestamp]).
- </code>
- <p>For this to work in an autostart the following translation is needed:</p>
- <code type="none">
-[{{inviso,tpl,5},{inviso_rt,tpl,{erlang,tl}}},
- {{inviso,tf,3},{inviso_rt,tf,{erlang,tl}}}]
- </code>
- <p>Since transforming the arguments from <c>inviso</c> calls to <c>inviso_rt</c> calls is simply removing the first argument, there is no need to program any function to do this. The BIF <c>tl/1</c> can be used directly.</p>
- <p>Further there must be a variable binding for <c>MS</c> when executing the trace-case. It is not necessary to have one for <c>Nodes</c> since that argument is removed from all function calls by the translation.</p>
- </section>
-</chapter>
-
diff --git a/lib/inviso/doc/src/inviso_lfm.xml b/lib/inviso/doc/src/inviso_lfm.xml
deleted file mode 100644
index 70207d0b58..0000000000
--- a/lib/inviso/doc/src/inviso_lfm.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_lfm</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_lfm</module>
- <modulesummary>An Inviso Off-Line Logfile Merger</modulesummary>
- <description>
- <p>Implements an off-line logfile merger, merging binary trace-log files from several nodes together in chronological order. The logfile merger can also do pid-to-alias translations.</p>
- <p>The logfile merger is supposed to be called from the Erlang shell or a higher layer trace tool. For it to work, all logfiles and trace information files (containing the pid-alias associations) must be located in the file system accessible from this node and organized according to the API description.</p>
- <p>The logfile merger starts a process, the output process, which in its turn starts one reader process for every node it shall merge logfiles from. Note that the reason for a process for each node is not remote communication, the logfile merger is an off-line utility, it is to sort the logfile entries in chronological order.</p>
- <p>The logfile merger can be customized both when it comes to the implementation of the reader processes and the output the output process shall generate for every logfile entry.</p>
- </description>
- <funcs>
- <func>
- <name>merge(Files, OutFile) -></name>
- <name>merge(Files, WorkHFun, InitHandlerData) -></name>
- <name>merge(Files, BeginHFun, WorkHFun, EndHFun, InitHandlerData) -> {ok, Count} | {error, Reason}</name>
- <fsummary>Merge logfiles into one file in chronological order</fsummary>
- <type>
- <v>Files = [FileDescription]</v>
- <v>&nbsp;FileDescription = FileSet | {reader,RMod,RFunc,FileSet}</v>
- <v>&nbsp;&nbsp;FileSet = {Node,LogFiles} | {Node,[LogFiles]}</v>
- <v>&nbsp;&nbsp;&nbsp;Node = atom()</v>
- <v>&nbsp;&nbsp;&nbsp;LogFiles = [{trace_log,[FileName]}] | [{trace_log,[FileName]},{ti_log,TiFileSpec}]</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;TiFileSpec = [string()] - a list of one string.</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;FileName = string()</v>
- <v>&nbsp;&nbsp;RMod = RFunc = atom()</v>
- <v>OutFile = string()</v>
- <v>BeginHFun = fun(InitHandlerData) -> {ok, NewHandlerData} | {error, Reason}</v>
- <v>WorkHFun = fun(Node, LogEntry, PidMappings, HandlerData) -> {ok, NewHandlerData}</v>
- <v>&nbsp;LogEntry = tuple()</v>
- <v>&nbsp;PidMappings = term()</v>
- <v>EndHFun = fun(HandlerData) -> ok | {error, Reason}</v>
- <v>Count = int()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Merges the logfiles in <c>Files</c> together into one file in chronological order. The logfile merger consists of an output process and one or several reader processes.</p>
- <p>Returns <c>{ok, Count}</c> where <c>Count</c> is the total number of log entries processed, if successful.</p>
- <p>When specifying <c>LogFiles</c>, currently the standard reader-process only supports:</p>
- <list type="bulleted">
- <item>one single file</item>
- <item>a list of wraplog files, following the naming convention <c><![CDATA[<Prefix><Nr><Suffix>]]></c>.</item>
- </list>
- <p>Note that (when using the standard reader process) it is possible to give a list of <c>LogFiles</c>. The list must be sorted starting with the oldest. This will cause several trace-logs (from the same node) to be merged together in the same <c>OutFile</c>. The reader process will simply start reading the next file (or wrapset) when the previous is done.</p>
- <p><c>FileDescription == {reader,RMod,RFunc,FileSet}</c> indicates that <c>spawn(RMod, RFunc, [OutputPid,LogFiles])</c> shall create a reader process.</p>
- <p>The output process is customized with <c>BeginHFun</c>, <c>WorkHFun</c> and <c>EndHFun</c>. If using <c>merge/2</c> a default output process configuration is used, basically creating a text file and writing the output line by line. <c>BeginHFun</c> is called once before requesting log entries from the reader processes. <c>WorkHFun</c> is called for every log entry (trace message) <c>LogEntry</c>. Here the log entry typically gets written to the output. <c>PidMappings</c> is the translations produced by the reader process. <c>EndHFun</c> is called when all reader processes have terminated.</p>
- <p>Currently the standard reader can only handle one ti-file (per <c>LogFiles</c>). The current inviso meta tracer is further not capable of wrapping ti-files. (This also because a wrapped ti-log will most likely be worthless since alias associations done in the beginning are erased but still used in the trace-log).</p>
- <p>The standard reader process is implemented in the module <c>inviso_lfm_tpreader</c> (trace port reader). It understands Erlang linked in trace-port driver generated trace-logs and <c>inviso_rt_meta</c> generated trace information files.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>Writing Your Own Reader Process</title>
- <p>Writing a reader process is not that difficult. It must:</p>
- <list type="bulleted">
- <item>Export an init-like function accepting two arguments, pid of the output process and the <c>LogFiles</c> component. <c>LogFiles</c> is actually only used by the reader processes, making it possible to redefine <c>LogFiles</c> if implementing an own reader process.</item>
- <item>Respond to <c>{get_next_entry, OutputPid}</c> messages with <c>{next_entry, self(), PidMappings, NowTimeStamp, Term}</c> or <c>{next_entry, self(), {error,Reason}}</c>.</item>
- <item>Terminate normally when no more log entries are available.</item>
- <item>Terminate on an incoming EXIT-signal from <c>OutputPid</c>.</item>
- </list>
- <p>The reader process must of course understand the format of a logfile written by the runtime component.</p>
- </section>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_lfm_tpfreader.xml b/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
deleted file mode 100644
index bae40522a3..0000000000
--- a/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_lfm_tpfreader</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_lfm_tpfreader</module>
- <modulesummary>Inviso Standard Reader Process to Standard Logfile Merger</modulesummary>
- <description>
- <p>Implements the standard reader process to the standard logfile merger <c>inviso_lfm</c>.</p>
- <p>The reader process reads logfiles belonging to the same set (normally one node) in chronological order and delivers logged trace messages one by one to the output process. Before any trace messages are delivered, the <c>inviso_lfm_tpreader</c> implementation reads the entire trace information file (if in use) and builds a database over pid-to-alias associations.</p>
- <p>The <c>inviso_lfm_tpreader</c> implementation is capable of considering that an alias may have been used for several processes during different times. An alias may also be in use for several pids at the same time, on purpose. If a process has generated a trace message, all associations between that pid and aliases will be presented as the list <c>PidMappings</c> in the message sent to the output process.</p>
- </description>
- <funcs>
- <func>
- <name>handle_logfile_sort_wrapset(LogFiles) -> FileList2</name>
- <fsummary>Sort logfiles in chronological order</fsummary>
- <type>
- <v>LogFiles = [{trace_log, FileList}]</v>
- <v>FileList = FileList2 = [FileName]</v>
- <v>&nbsp;FileName = string()</v>
- </type>
- <desc>
- <p>Only one <c>{trace_log, FileList}</c> tuple is expected in <c>LogFiles</c>, all other tuples are ignored. <c>FileList</c> must:</p>
- <list type="bulleted">
- <item>contain one single file name, or</item>
- <item>a list of wraplog files, following the naming convention <c><![CDATA[<Prefix><Nr><Suffix>]]></c>.</item>
- </list>
- <p>Sorts the files in <c>FileList</c> in chronological order beginning with the oldest. Sorting is only relevant if <c>FileList</c> is a list of wraplogs. The sorting is done on finding the modulo-counter in the filename and not on filesystem timestamps.</p>
- <p>This function is exported for convenience should an own reader process be implemented.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>The Trace Information File Protocol</title>
- <p>The format of a trace information file is dictated by the meta tracer process. The <c>inviso_lfm_tpfreader</c> implementation of a reader process understands the following trace information entries. Note that the <c>inviso_rt_meta</c> trace information file is on binary format prefixing every entry with a 4 byte length indicator.</p>
- <taglist>
- <tag><c>{Pid, Alias, alias, NowStamp}</c></tag>
- <item>
- <p><c>Pid = pid()</c> <br></br>
-<c>Alias = term()</c> <br></br>
-<c>NowStamp = term()</c>, but in current implementation as
- returned from <c>erlang:now/0</c></p>
- <p>This message indicates that from now on shall <c>Pid</c> be associated with <c>Alias</c>.</p>
- </item>
- <tag><c>{MaybePid, Alias, unalias, NowStamp}</c></tag>
- <item>
- <p><c>MaybePid = pid() | undefined</c> <br></br>
-<c>Alias = term()</c> <br></br>
-<c>NowStamp = term()</c>, see above</p>
- <p>This message indicates that, if <c>MaybePid</c> is a pid, this pid shall no longer be associated with <c>Alias</c>. If it is <c>undefined</c>, all associations with <c>Alias</c> from now shall be considered invalid.</p>
- <p>Also note that there are many situations where <c>unalias</c> entries will be missing. For instance if a process terminates without making explicit function calls removing its associations first. This is seldom a problem unless the pid is reused.</p>
- </item>
- </taglist>
- </section>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_rt.xml b/lib/inviso/doc/src/inviso_rt.xml
deleted file mode 100644
index 3a8e77f65c..0000000000
--- a/lib/inviso/doc/src/inviso_rt.xml
+++ /dev/null
@@ -1,246 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</year>
- <year>2011</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>inviso_rt</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_rt</module>
- <modulesummary>Direct API to the Inviso Runtime Component</modulesummary>
- <description>
- <p>The <c>inviso_rt</c> API is normally only used when programming autostart scripts or similar mechanisms. The reason is that the runtime component is part of the Runtime_tools application and will therefore always be available. But the regular inviso API is part of the Inviso application not necessarily available on the node doing an autostart. It is of course possible to runt a "lean" tracer only using the runtime component manually (i.e not through autostart). The runtime component shall otherwise be controlled through the control component, which is accessed with the <c>inviso</c> API.</p>
- </description>
- <funcs>
- <func>
- <name>init_tracing(TracerData) -> NodeResult | {error,Reason}</name>
- <fsummary>Initiate tracing</fsummary>
- <desc>
- <p>See <seealso marker="inviso#init_tracing/2">inviso:init_tracing/2</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tp(Mod,Func,Arity,MatchSpec,Opts) -></name>
- <name>tp(Mod,Func,Arity,MatchSpec) -> NodeResult | {error,Reason}</name>
- <name>tp(PatternList) -> NodeResult | {error,Reason}</name>
- <fsummary>Set global trace patterns</fsummary>
- <type>
- <v>Mod,Func = atom() | '_' | ModRegExp | {DirRegExp,ModRegExp}</v>
- <v>&nbsp;ModRegExp = regexp_string()</v>
- <v>&nbsp;DirRegExp = regexp_string()</v>
- <v>Arity = int() | '_'</v>
- <v>MatchSpec = true | false | [] | matchspec()</v>
- <v>PatternList = [Pattern],</v>
- <v>&nbsp;Pattern = {Mod,Func,Arity,MatchSpec,Opts}</v>
- <v>Opts = [Opt]</v>
- <v>&nbsp;Opt = only_loaded</v>
- <v>NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>&nbsp;&nbsp;Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set global trace patterns. The integer replied if the call was successfull describes the number of matched functions. Using wildcards follows the rules for wildcards of <c>erlang:trace_pattern</c>. It is for instance illegal to specify <c>M=='_'</c> while <c>F</c> is not <c>'_'</c>.</p>
- <p>Modules can also be specified using Erlang regular expressions as described in the <c>regexp</c> module. If <c>{DirRegExp,ModRegExp}</c> is used, module selection will further be restricted by that the module must be loaded from a location containing <c>DirRegExp</c> somewhere in the path. This can be used to for instance trace on all modules belonging to a certain application.</p>
- </desc>
- </func>
- <func>
- <name>tpl(Mod,Func,Arity,MatchSpec) -></name>
- <name>tpl(Mod,Func,Arity,MatchSpec,Opts) -> NodeResult | {error,Reason}</name>
- <name>tpl(PatternList) -> NodeResult | {error,Reason}</name>
- <fsummary>Set local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/5">tp/N</seealso> function above for details on arguments and return values.</p>
- <p>Set local trace pattern on specified functions.</p>
- </desc>
- </func>
- <func>
- <name>ctp(Mod,Func,Arity) -> NodeResult | {error,Reason}</name>
- <fsummary>Clear global trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/5">tp/N</seealso> for argument descriptions.</p>
- <p>Clear global trace patterns.</p>
- </desc>
- </func>
- <func>
- <name>ctpl(Mod,Func,Arity) -> NodeResult | {error,Reason}</name>
- <fsummary>Clear local trace patterns</fsummary>
- <desc>
- <p>See <seealso marker="#tp/5">tp/N</seealso> for argument description.</p>
- <p>Clear local trace patterns.</p>
- </desc>
- </func>
- <func>
- <name>tf(PidSpec,FlagList) -> NodeResult | {error,Reason}</name>
- <name>tf(TraceConfList) -> NodeResult | {error,Reason}</name>
- <fsummary>Set process trace flags</fsummary>
- <type>
- <v>TraceConfList = [{PidSpec,FlagList}]</v>
- <v>FlagList = [Flag]</v>
- <v>PidSpec = all | new| existing | pid() | locally_registered_name()</v>
- <v>Flag = all process trace flags allowed.</v>
- <v>NodeResult = {ok,[Ans]} | {error,Reason}</v>
- <v>Ans = int() | {error,Reason}</v>
- </type>
- <desc>
- <p>Set process trace flags. The integer returned if the call was successful describes the matched number of processes.</p>
- </desc>
- </func>
- <func>
- <name>ctf(PidSpec,FlagList) -> NodeResult | {error,Reason}</name>
- <name>ctf(TraceConfList) -> NodeResult | {error,Reason}</name>
- <fsummary>Clear process trace flags</fsummary>
- <desc>
- <p>See <seealso marker="#tf/2">tf/1,2</seealso> for arguments and return value description.</p>
- <p>Clear process trace flags.</p>
- </desc>
- </func>
- <func>
- <name>init_tpm(Mod,Func,Arity,CallFunc) -> NodeResult | {error,Reason}</name>
- <name>init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> NodeResult | {error,Reason}</name>
- <fsummary>Initialize meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>NodeResult = ok | {error,Reason}</v>
- <v>InitFunc = RemoveFunc = {Module,Function} | function()/4 | void</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#init_tpm/4">inviso:init_tpm/5,7</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm(Mod,Func,Arity,MS) -> NodeResult | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,CallFunc) -> NodeResults | {error,Reason}</name>
- <name>tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> NodeResults | {error,Reason}</name>
- <fsummary>Activate meta tracing</fsummary>
- <type>
- <v>Mod = Func = atom() =/= '_'</v>
- <v>Arity = int()</v>
- <v>MS = match_spec()</v>
- <v>InitFunc = CallFunc = ReturnFunc = RemoveFunc = {Module,Function} | function()</v>
- <v>NodeResult = {ok,1} | {ok,0} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm/4">inviso:tpm/4,5,8</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm_tracer(Mod,Func,Arity,MS) -> NodeResult | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,CallFunc) -> NodeResults | {error,Reason}</name>
- <name>tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> NodeResults | {error,Reason}</name>
- <fsummary>Activate meta tracing</fsummary>
- <desc>
- <p>See inviso:tpm_tracer/4,5,8 for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms(Mod,Func,Arity,MSname,MS) ->d NodeResult | {error,Reason}</name>
- <fsummary>Add match specifications</fsummary>
- <type>
- <v>Mod = Func = atom()</v>
- <v>Arity = int()</v>
- <v>MSname = term()</v>
- <v>MatchSpec = [match_spec()]</v>
- <v>NodeResult = {ok,1} | {ok,0} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm_ms/5">inviso:tpm_ms/5</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->d NodeResult | {error,Reason}</name>
- <fsummary>Add match specifications</fsummary>
- <desc>
- <p>See inviso:tpm_ms_tracer/5 for details.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_ms(Mod,Func,Arity,MSname) -> NodeResult | {error,Reason}</name>
- <fsummary>Remove a match specification</fsummary>
- <type>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm_ms/4">inviso:ctpm_ms/4</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>ctpm(Mod,Func,Arity) -> {ok,NodeResults} | NodeResult | {error,Reason}</name>
- <fsummary>Remove a meta trace pattern</fsummary>
- <type>
- <v>NodeResults = [{Node,NodeResult}]</v>
- <v>NodeResult = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm/3">inviso:ctpm/3</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>local_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm_localnames/0">inviso:tpm_localnames/0</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>remove_local_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>register/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm_localnames/0">inviso:ctpm_localnames/0</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>global_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Set meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = {ok,0} | {ok,1} | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#tpm_globalnames/0">inviso:tpm_globalnames/0</seealso> for details.</p>
- </desc>
- </func>
- <func>
- <name>remove_global_register() ->NodeResult | {error,Reason}</name>
- <fsummary>Clear meta trace pattern on <c>global:register_name/2</c></fsummary>
- <type>
- <v>NodeResult = {R1,R2} | {error,Reason}</v>
- <v>R1 = R2 = ok | {error,Reason}</v>
- </type>
- <desc>
- <p>See <seealso marker="inviso#ctpm_globalnames/0">inviso:ctpm_globalnames/0</seealso> for details.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_rt_meta.xml b/lib/inviso/doc/src/inviso_rt_meta.xml
deleted file mode 100644
index a1e5400ce0..0000000000
--- a/lib/inviso/doc/src/inviso_rt_meta.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2006</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>inviso_rt_meta</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <module>inviso_rt_meta</module>
- <modulesummary>Direct API to the Inviso Runtime Component's meta tracer</modulesummary>
- <description>
- <p>This module provides a direct API to the inviso meta tracer. These functions are only meant to be used in meta tracing <c>CallFunc</c> and <c>RemoveFunc</c>.</p>
- <p>It can sometimes be necessary to manipulate meta match-patterns from <c>CallFunc</c>s and <c>RemoveFunc</c>s. The problem then is that call-funcs and remove-funcs are meta trace call-backs executed inside the inviso meta tracer's context. Hence making calls to the regular API's manipulating meta trace-patterns will hang the inviso meta tracer!.</p>
- <p>To remedy this problem, a number of useful tpm-functions are available in this API. It must be understood that their actions are local to the Erlang node where they are called.</p>
- </description>
- <funcs>
- <func>
- <name>tpm_ms(Mod,Func,Arity,MSname,MS) -> {ok,0} | {ok,1} | {error,not_initiated}</name>
- <fsummary>Adds a list of match-specs, associated with the name <c>MSname</c>, to <c>Mod:Func/Arity</c>.</fsummary>
- <desc>
- <p>See inviso:tpm_ms/6 for details. Note that this function only effects meta trace-patterns on the Erlang node where the function is called. This also implies that only the local inviso meta tracer's name-database is updated with <c>MSname</c>.</p>
- </desc>
- </func>
- <func>
- <name>tpm_ms_tracer(Mod,Func,Arity,MSname,MS) -> {ok,0} | {ok,1} | {error,not_initiated}</name>
- <fsummary>As tpm_ms_tracer/5 but also adds a <c>{tracer,Tracer}</c>trace flag to the enable-list of every <c>trace</c>in <c>MS</c>.</fsummary>
- <desc>
- <p>See inviso:tpm_ms_ms/6 for details. Note that this function only effects meta trace-patterns on the Erlang node where the function is called. This also implies that only the local inviso meta tracer's name-database is updated with <c>MSname</c>.</p>
- </desc>
- </func>
- <func>
- <name>list_tpm_ms(Mod,Func,Arity) -> [MSname]</name>
- <fsummary>Returns a list of <c>MSname</c>.</fsummary>
- <desc>
- <p>Returns a list of all <c>MSname</c> in use for <c>Mod:Func/Arity</c>. This can be useful instead of having to have an own-implemented database over currently in use meta match-functions for a particular function.</p>
- </desc>
- </func>
- <func>
- <name>ctpm_ms(Mod,Func,Arity,MSname) -> ok</name>
- <fsummary>Removes the list of match-specs associated with the <c>MSname</c>from the meta trace-pattern of <c>Mod:Func/Arity</c>.</fsummary>
- <desc>
- <p>See inviso:ctpm_ms/5 for details. Note that this function only effects meta trace-patterns on the Erlang node where the function is called. This also implies that only the local inviso meta tracer's name-database is updated with <c>MSname</c>.</p>
- </desc>
- </func>
- <func>
- <name>get_tracer() -> Tracer</name>
- <fsummary>Returns the pid or port acting as regular tracer.</fsummary>
- <type>
- <v>Tracer = pid() | port()</v>
- </type>
- <desc>
- <p>Returns the pid or port acting as the receiver of regular trace messages. This is useful if it is necessary to manipulate meta trace-patterns by hand (using <c>erlang:trace_pattern/3</c>) and the <c>{tracer,Tracer}</c> must be used in one of the match-function bodies.</p>
- </desc>
- </func>
- </funcs>
-</erlref>
-
diff --git a/lib/inviso/doc/src/inviso_users_guide_pic1.gif b/lib/inviso/doc/src/inviso_users_guide_pic1.gif
deleted file mode 100644
index a6da9d37de..0000000000
--- a/lib/inviso/doc/src/inviso_users_guide_pic1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/inviso/doc/src/inviso_users_guide_pic1.ps b/lib/inviso/doc/src/inviso_users_guide_pic1.ps
deleted file mode 100644
index fc859cd129..0000000000
--- a/lib/inviso/doc/src/inviso_users_guide_pic1.ps
+++ /dev/null
@@ -1,24489 +0,0 @@
-%!PS-Adobe-2.0 EPSF-2.0
-%%Title: /clearcase/otp/tools/inviso/doc/src/inviso_users_guide_pic1b.ps
-%%Creator: XV Version 3.10a Rev: 12/29/94 - by John Bradley
-%%BoundingBox: -1 161 615 630
-%%Pages: 1
-%%DocumentFonts:
-%%EndComments
-%%EndProlog
-
-%%Page: 1 1
-
-% remember original state
-/origstate save def
-
-% build a temporary dictionary
-20 dict begin
-
-% define string to hold a scanline's worth of data
-/pix 1848 string def
-
-% define space for color conversions
-/grays 616 string def % space for gray scale line
-/npixls 0 def
-/rgbindx 0 def
-
-% lower left corner
--1 161 translate
-
-% size of image (on paper, in 1/72inch coords)
-616.03200 469.00800 scale
-
-% define 'colorimage' if it isn't defined
-% ('colortogray' and 'mergeprocs' come from xwd2ps
-% via xgrab)
-/colorimage where % do we know about 'colorimage'?
- { pop } % yes: pop off the 'dict' returned
- { % no: define one
- /colortogray { % define an RGB->I function
- /rgbdata exch store % call input 'rgbdata'
- rgbdata length 3 idiv
- /npixls exch store
- /rgbindx 0 store
- 0 1 npixls 1 sub {
- grays exch
- rgbdata rgbindx get 20 mul % Red
- rgbdata rgbindx 1 add get 32 mul % Green
- rgbdata rgbindx 2 add get 12 mul % Blue
- add add 64 idiv % I = .5G + .31R + .18B
- put
- /rgbindx rgbindx 3 add store
- } for
- grays 0 npixls getinterval
- } bind def
-
- % Utility procedure for colorimage operator.
- % This procedure takes two procedures off the
- % stack and merges them into a single procedure.
-
- /mergeprocs { % def
- dup length
- 3 -1 roll
- dup
- length
- dup
- 5 1 roll
- 3 -1 roll
- add
- array cvx
- dup
- 3 -1 roll
- 0 exch
- putinterval
- dup
- 4 2 roll
- putinterval
- } bind def
-
- /colorimage { % def
- pop pop % remove 'false 3' operands
- {colortogray} mergeprocs
- image
- } bind def
- } ifelse % end of 'false' case
-
-
-
-616 469 8 % dimensions of data
-[616 0 0 -469 0 469] % mapping matrix
-{currentfile pix readhexstring pop}
-false 3 colorimage
-
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff000000000000
-000000ffffffccccccffffffffffffffffff000000000000ffffffffffffccccccffffff
-000000ffffff000000000000ffffffffffffcccccc000000000000000000000000ffffff
-000000ffffff000000000000ffffffffffffffffff000000000000ffffffccccccffffff
-ffffffffffff000000ffffffffffffffffffccccccffffffffffffffffff000000000000
-000000ffffffccccccffffffffffffffffff000000000000ffffffffffffccccccffffff
-ffffff000000ffffff000000000000ffffffcccccc000000000000ffffffffffffffffff
-ffffff000000cccccc000000000000ffffffffffffffffffffffffffffffcccccc000000
-000000ffffffffffffffffffffffffffffff000000ffffff000000000000ffffffffffff
-ffffffffffff000000000000000000ffffffffffffffffffffffff000000cccccc000000
-000000ffffffffffff000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000
-000000000000ffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-000000000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000000000000000ffffffffffff000000000000ffffffffffff000000ffffffffffff
-000000000000000000ffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000000000000000ffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000000000000000ffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000000000000000000000000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffff000000ffffffccccccffffffffffff000000ffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffff000000ffffffccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffff000000ffffffccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffff000000000000000000000000ffffffffffff000000000000ccccccffffff
-ffffff000000ffffff000000000000ffffffccccccffffff000000000000000000000000
-ffffffffffff000000ffffffffffffffffff000000ffffff000000000000ccccccffffff
-000000000000ffffffffffffffffffffffffcccccc000000000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffff000000000000000000ffffffccccccffffff
-ffffffffffff000000000000ffffffffffffccccccffffffffffff000000ffffff000000
-000000ffffffcccccc000000000000ffffffffffffffffff000000ffffff000000000000
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-ffffff000000cccccc000000000000ffffffffffffffffffffffff000000000000000000
-ffffffffffffffffffffffff000000ffffff000000000000ffffffffffffffffff000000
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff000000ffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000000000000000ffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000000000ffffffffffff000000000000
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffff000000000000000000ffffff
-ffffff000000000000ffffffffffff000000ffffff000000000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000000000000000ffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffff000000000000000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000ffffffffffffffffffffffffffffff000000000000ffffffffffffffffff000000
-000000000000ffffff000000000000000000ffffffffffff000000000000ffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000ffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffffffffffffffff000000000000
-000000ffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-000000000000000000ffffff000000000000000000ffffff000000000000000000ffffff
-ffffff000000ffffff000000000000ffffffffffffffffffffffffffffffffffff000000
-000000ffffffffffffffffffffffff000000000000000000ffffff000000000000000000
-ffffffffffff000000000000000000ffffffffffffffffff000000000000000000ffffff
-000000000000000000ffffff000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000000000000000000000000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000000000000000000000
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffff000000ccccccffffff000000ffffffffffffffffff
-ffffff000000ccccccffffff000000ffffffffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffff000000ccccccffffff000000ffffffffffffffffff
-ffffffffffffccccccffffff000000ffffffffffffffffff000000ffffffccccccffffff
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000000000000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffff000000000000ffffff000000ffffffffffff
-000000000000000000ffffff000000000000000000ffffffffffff000000000000ffffff
-ffffff000000000000000000ffffff000000000000000000ffffff000000000000000000
-ffffff000000000000000000ffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffff
-ffffffffffff000000000000ffffffffffffffffffffffff000000000000000000ffffff
-000000000000000000ffffff000000000000000000ffffff000000ffffff000000000000
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-000000000000000000ffffff000000000000000000ffffffffffff000000000000000000
-ffffffffffffffffff000000000000000000ffffff000000000000000000ffffffffffff
-000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000ffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc666666999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc666666999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffff999999666666ffffffffffffffffffcccccc666666ccccccffffffffffff
-ffffff666666999999ffffffffffffffffff999999666666ffffffffffffffffffcccccc
-666666ccccccffffffffffffffffff666666999999ffffffffffffffffff999999666666
-ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666999999ffffff
-ffffffffffff999999666666ffffffffffffffffffcccccc666666ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999ccccccffffffffffffffffff666666999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffff666666999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999ccccccffffffffffff
-ffffff666666999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-999999999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc999999999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc999999999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffcccccc666666999999ffffffffffffffffff999999999999ffffff
-ffffffffffffcccccc666666ffffffffffffffffffcccccc666666999999ffffffffffff
-ffffff999999999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc
-666666999999ffffffffffffffffff999999999999ffffffffffffffffffcccccc666666
-ffffffffffffffffffffffff
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000333333ffffffffffff999999000000000000ffffff
-ffffffffffff000000000000999999ffffffffffff333333000000333333ffffffffffff
-999999000000000000ffffffffffffffffff000000000000666666ffffffffffff333333
-000000333333ffffffffffff999999000000000000ffffffffffffffffff000000000000
-666666ffffffffffff333333000000333333ffffffffffff999999000000000000ffffff
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffff666666000000333333ffffffffffff999999000000000000cccccc
-ffffffffffff000000000000666666ffffffffffff666666000000333333ffffffffffff
-999999000000000000ccccccffffffffffff000000000000666666ffffffffffff666666
-000000333333ffffffffffff999999000000000000ccccccffffffffffff000000000000
-666666ffffffffffffffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff666666333333ffffffffffffffffff999999333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff666666333333ffffffffffffffffff999999
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff666666333333
-ffffffffffffffffff999999333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff666666333333ffffffffffffffffff999999333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ffffffffffffffffff999999
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ffffffffffffffffff999999333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ffffffffffffffffff999999333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ffffffffffffffffff999999
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333999999ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333999999ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333999999ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffcccccc333333666666ffffffffffffffffff333333666666ffffff
-ffffffffffff999999333333ccccccffffffffffffcccccc333333666666ffffffffffff
-ffffff333333666666ffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333666666ffffffffffffffffff333333666666ffffffffffffffffff999999333333
-ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999cccccc666666999999666666999999666666333333
-333333333333333333333333333333333333333333333333333333999999666666999999
-666666999999999999ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff000000ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666
-666666000000000000333333666666333333666666666666999999666666999999cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccc666666999999666666
-999999666666666666333333333333000000333333333333999999999999cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000
-000000000000000000ffffffffffff000000000000ffffffffffffffffff000000ffffff
-000000000000ffffffffffffffffff000000000000000000000000ffffffffffff000000
-ffffffffffffffffff000000ffffff000000000000ffffffffffff000000000000ffffff
-ffffffffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffff
-000000000000ffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-ffffffffffffffffff000000ffffffffffffffffffffffff000000000000000000ffffff
-ffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffff
-000000ffffff000000000000ffffffffffffffffffffffff000000ffffff000000000000
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffff000000000000000000ffffffffffffffffffffffff000000000000000000
-ffffffffffffffffff000000000000000000000000ffffffffffff000000ffffffffffff
-ffffffffffff000000000000ffffffffffffffffffffffffffffff000000ffffff000000
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666666666333333000000000000333333666666999999
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666666666333333333333
-000000333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffff000000000000000000
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000000000
-ffffffffffff000000000000000000ffffffffffff000000000000ffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff000000
-000000000000ffffffffffff000000ffffffffffff000000000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffff000000000000000000ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666333333333333999999999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccccccccc666666333333000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffffffffff000000000000000000000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000ffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000333333
-666666999999ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000333333666666999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffffcccccc000000ffffffffffffffffff000000
-ffffffffffffcccccc000000ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffcccccc000000ffffffffffff000000ffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffffccccccffffff000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffff000000000000ffffff000000ccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffff000000ffffffffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffff000000ccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffff000000000000ffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffff000000ccccccffffff
-000000ffffffffffffffffffffffff000000ccccccffffffffffff000000ffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999666666000000666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffff000000000000000000ffffffffffff000000ffffff
-ffffffffffff000000ffffffffffffffffff000000ffffff000000ffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ffffff000000ffffffffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000ffffff
-ffffffffffffffffffffffffffffff000000ffffffffffff000000000000ffffffffffff
-000000000000ffffffffffff000000ffffffffffffffffff000000000000ffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000
-000000ffffffffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffff
-ffffffffffffffffff000000000000ffffff000000ffffffffffff000000000000000000
-ffffff000000000000000000ffffffffffff000000000000ffffffffffff000000000000
-000000ffffff000000000000000000ffffff000000000000000000ffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-000000000000ffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-ffffffffffff000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffff000000000000000000ffffff000000000000ffffff
-000000ffffff000000000000ffffffffffffffffffffffff000000ffffff000000000000
-ffffffffffffffffffffffff000000000000000000ffffff000000000000000000ffffff
-ffffffffffff000000000000000000ffffffffffffffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffff000000000000000000ffffff
-ffffffffffff000000000000ffffffffffffffffffffffff000000000000000000ffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-666666ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffffffffffffffffffffffffffff000000ffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000
-000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffcccccc
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccccccccccccccc
-ffffffffffffffffffcccccc999999ccccccffffffffffffffffffcccccccccccccccccc
-ccccccccccccccccccccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffff999999333333333333333333000000000000000000000000000000000000333333
-ffffffffffffffffffffffff000000ccccccffffffffffffffffff333333000000000000
-000000000000000000000000000000000000000000333333ffffffffffffffffffffffff
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc999999999999333333ffffffffffffffffffcccccc000000ccccccffffffffffff
-ffffff666666666666666666999999666666cccccc999999ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccccccccccccccccccccc999999ffffffffffffffffffcccccc
-000000ccccccffffffffffffffffff666666666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666666666333333333333000000000000
-000000000000333333666666ffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333333333000000000000000000333333
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999999999ffffffffffffffffff666666333333333333666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666
-666666000000000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999666666333333ccccccffffffffffffffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999333333ffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000000000000000ffffffffffffffffff000000000000ffffff
-000000000000ffffff000000000000000000ffffff000000000000000000ffffff000000
-000000000000ffffffffffffffffffffffff000000000000000000000000ffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666333333000000000000000000333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffffffffff000000000000
-ffffffffffff000000ffffff000000ffffffccccccffffff000000ffffffffffffffffff
-ffffff000000ccccccffffffffffff000000ffffffffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-000000333333000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffff000000000000000000ffffffffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333ffffffffffffffffffcccccccccccccccccc
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333000000333333666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffff000000ffffff000000
-ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffcccccc999999666666333333000000
-666666ffffffffffffffffff999999ccccccffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666333333000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000000000000000000000000000ffffff000000000000000000
-ffffff000000000000000000ffffff000000000000ffffffffffffffffffffffff000000
-000000000000000000000000ffffff000000000000000000000000ffffffffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333999999333333333333000000000000333333666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffcccccc333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000666666666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000000000000000000000000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333000000333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999ffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333666666ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333666666666666000000000000000000666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333000000666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666333333
-ccccccffffffcccccc000000000000000000000000000000666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333
-999999ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999333333333333999999ffffff
-ffffffffffffffffff000000000000000000000000000000000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333666666ccccccffffffffffffffffff
-ffffffffffffffffff666666000000000000000000000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666333333333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000000000000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-666666000000666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000000000999999333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666333333000000666666
-999999ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffcccccc999999666666333333000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ccccccffffffffffff333333333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000333333666666ccccccccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999333333333333666666999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333000000333333333333999999999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc666666666666
-333333000000000000666666666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999cccccc666666666666333333
-000000333333666666666666999999666666ccccccccccccccccccccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccccccccc
-cccccc999999999999666666999999333333333333000000666666333333999999999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999666666999999333333333333333333333333333333000000
-000000000000000000000000000000000000000000000000000000333333333333333333
-333333666666666666666666666666ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666
-cccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666333333ffffffffffff
-999999000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666333333999999ffffffffffffffffff
-333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000999999ffffffffffffffffffffffffcccccc
-333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff999999
-666666000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666ccccccffffffffffffffffffffffffffffffffffffffffff333333
-cccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333cccccc
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666000000666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffffffff666666999999666666333333
-333333333333333333333333999999ffffffcccccc333333666666999999666666999999
-666666999999666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666
-666666333333000000000000666666ffffffffffffffffff999999666666999999cccccc
-ccccccccccccccccccccccccffffffffffffffffffcccccc999999666666999999666666
-999999666666999999666666ffffffffffffffffff333333666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffff333333666666999999
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666999999333333333333
-000000333333666666ffffffffffffffffff333333ccccccffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc666666333333000000666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffff999999000000999999ffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-333333999999ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff999999333333666666000000333333333333cccccc
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffcccccc999999333333
-333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-666666ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffff
-cccccc000000ffffffffffff999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffff666666000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff333333ccccccccccccffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffff666666333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffff999999000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333999999ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000000000000000000000
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffcccccc000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000ffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffcccccc666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-000000000000ffffff000000ffffffffffff000000000000000000ffffffffffffffffff
-000000000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000000000000000ffffffffffff000000000000000000000000
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ccccccffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000ffffffffffff000000ffffffcccccc000000
-ffffff000000ffffffffffff000000ffffff000000ccccccccccccffffffffffffffffff
-000000999999000000333333333333000000333333000000ffffffffffff333333333333
-666666666666333333999999666666999999666666ccccccffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffffffffffffffff000000ffffff000000ffffff
-ffffff999999999999000000333333000000000000000000666666ffffffffffffffffff
-999999666666999999ccccccccccccccccccccccccccccccffffffffffffffffffcccccc
-999999666666999999666666999999666666999999666666ffffffffffffffffff333333
-666666000000ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffff999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffff000000999999666666666666ffffff
-ffffff333333666666999999999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000333333333333000000333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff
-000000ffffffffffffcccccc000000000000333333000000666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffccccccccccccffffffffffffcccccc666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333333333333333999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffff999999666666
-000000333333333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffff333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999333333333333999999ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffcccccc333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccccccccffffffffffff999999666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff666666333333
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffff333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff999999333333999999ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000000000000000000000000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff000000000000ffffffffffffffffffffffffccccccffffff
-ffffff333333ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffcccccc000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffff000000000000ffffff000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000ffffffffffffffffff000000000000ffffff
-ffffffffffffffffffffffff000000ffffffffffff000000000000000000ffffffffffff
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffccccccffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffff000000000000ffffffffffff000000ffffffffffff
-000000ffffffcccccc000000ffffff000000ffffffffffff000000ffffff000000cccccc
-ccccccffffffffffffffffff000000999999000000333333333333000000333333000000
-ffffffffffff333333333333666666999999666666999999666666999999666666cccccc
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffff999999999999000000333333000000000000000000
-666666ffffffffffffffffff999999666666999999cccccccccccccccccccccccccccccc
-ffffffffffffffffffcccccc999999666666999999666666999999666666999999666666
-ffffffffffffffffff333333666666666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-999999666666666666ffffffffffff333333666666999999999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666999999333333333333000000333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffff000000ffffffffffffcccccc000000000000333333000000
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffff
-ffffffcccccc666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333ccccccffffffffffffffffffccccccffffff
-ffffffffffff333333999999ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff333333333333333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff999999666666000000333333333333ccccccffffffffffffffffffffffff
-999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333999999ffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999333333333333999999ffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333333333ccccccffffffffffff
-ffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffff
-999999666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666cccccc
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666cccccc
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff666666333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333999999ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-999999333333999999ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff000000000000000000000000000000ffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffcccccc000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffff000000ffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffff000000000000ffffff000000
-ffffffffffff000000000000000000ffffffffffffffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffff000000ffffffffffff000000
-000000000000ffffffffffff000000000000000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333333333ccccccffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffccccccffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffffffffff000000000000ffffff
-ffffff000000ffffffffffff000000ffffffcccccc000000ffffff000000ffffffffffff
-000000ffffff000000ffffffffffffffffffffffffffffff000000ffffff000000ffffff
-ffffff000000ffffff000000ffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffff000000ffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffffffffff000000000000ffffffffffff
-ffffff000000ffffff000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000000000000000ffffffffffff000000ffffffffffffffffff
-000000000000000000000000000000ffffff000000000000000000ffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000000000000000000000
-ffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333ccccccffffff
-ffffffffffffccccccffffffffffffffffff333333999999ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999
-ffffffffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-333333ccccccffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000666666ccccccffffff999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333000000333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666ccccccffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666ffffffffffffffffffcccccc999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ccccccffffffcccccc333333333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000ffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000000000000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff333333999999ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000000000000000000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffffffffffffffff000000000000000000
-ffffffffffff000000ffffffffffff000000ffffffffffffffffff000000000000ffffff
-ffffff000000000000ffffff000000000000000000ffffffffffff000000000000000000
-ffffff000000000000ffffff000000ffffff000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000000000
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333000000000000000000000000000000
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000ffffff
-000000ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffff000000000000ffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffffffff999999666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-000000000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000000000
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffff000000000000000000ffffff000000
-ffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffff000000
-ffffffffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333999999999999666666000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000333333ffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333
-000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333333333ccccccffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffffcccccc000000ffffffffffff000000000000
-ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000cccccc000000ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000000000000000000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffcccccc999999ffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999cccccc999999999999666666999999666666999999333333333333
-333333000000333333333333333333333333333333333333666666999999666666999999
-666666cccccc999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffff000000000000ffffff000000000000ffffff000000000000000000ffffff000000
-000000000000ffffffffffff000000000000ffffff000000000000000000ffffffffffff
-ffffff000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333333333000000000000000000000000000000000000666666666666
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccccccccc999999999999333333
-333333000000666666333333666666666666999999666666999999666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccc999999666666999999666666
-666666333333666666333333000000000000666666666666999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333ccccccffffff666666000000000000000000000000333333333333333333
-000000000000000000333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333000000333333333333666666666666cccccc
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999999999666666333333000000000000
-333333666666666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333ffffffffffffffffffffffffcccccc333333000000000000666666ffffffffffff
-ffffffcccccc999999666666999999ffffffffffffffffffcccccc333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000333333666666ccccccccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999333333333333666666999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ffffff
-666666333333ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffcccccc666666000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffff
-ffffffffffff000000333333333333999999999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccccccccc999999333333000000000000333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999666666333333000000666666
-999999ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffcccccc999999333333333333000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000333333ffffff
-ffffff999999333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666666666333333000000000000000000000000666666666666
-ccccccccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666333333000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333333333666666999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999666666999999666666666666999999ffffffffffffcccccc333333
-666666333333999999666666999999666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000666666ffffff
-ffffffffffffcccccc333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333
-666666000000000000cccccc999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffffffffffffff333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999666666000000666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc999999ffffffffffff999999
-000000666666666666999999666666999999999999ffffffffffffffffffcccccccccccc
-999999cccccc666666999999666666999999333333333333666666ffffffffffffcccccc
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666333333666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc999999666666ffffffffffffffffffcccccc666666666666
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666ffffffffffffffffffffffff999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000333333333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666333333333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffff666666ccccccffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc666666000000666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffcccccc333333333333
-000000000000000000333333666666999999999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000
-333333999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000666666
-ccccccffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff333333333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000666666999999ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccccccccc666666666666000000000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000000000000000333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-000000666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccccccccffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccccccccccccccffffffffffffffffff
-000000999999ffffffffffffffffffccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666333333000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666000000000000999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffff666666000000000000666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000666666666666999999ffffffffffffccccccffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-333333333333ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc999999666666333333000000666666ccccccffffffffffffffffff
-ffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999999999666666333333000000000000
-000000333333666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ccccccffffffffffffffffff666666333333333333999999999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666333333ccccccffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffcccccc000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffcccccc333333000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000000000999999ffffffffffffffffffffffffffffff999999000000333333666666
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffffffff333333999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666666666333333ffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666666666000000000000000000333333666666
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333333333000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccccccccc999999999999333333333333000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000ffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999666666333333
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999ffffffffffffffffff333333000000333333666666666666
-999999ccccccccccccffffffffffffcccccc666666000000000000000000999999cccccc
-ffffffffffffffffffffffffffffffffffffcccccccccccccccccccccccccccccc666666
-999999ffffffffffffffffff333333333333666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000000000333333333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffcccccc000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999999999
-666666333333333333999999333333000000000000000000000000000000000000000000
-000000000000ccccccffffffffffff333333000000333333333333333333333333999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccccccccc666666666666000000000000000000333333333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffffffffff
-000000000000333333666666999999ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666
-ccccccffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff666666666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666666666999999
-ffffffffffffffffffccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999333333333333000000000000333333666666666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333000000000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ffffffffffffffffffccccccccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999999999333333333333666666ffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000000000
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffcccccc666666666666999999999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000000000666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-333333000000000000000000333333999999999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333333333000000000000
-000000000000333333666666666666ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000ccccccffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333ccccccffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffff000000ffffff000000000000ffffff
-ffffff000000ffffff000000000000000000cccccc000000000000000000ffffffffffff
-000000000000cccccc000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffff000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc999999666666333333333333000000ccccccffffffffffffffffffcccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000000000000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000000000ffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000666666ffffffffffffffffffffffffffffff
-ffffffffffff999999000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333000000000000333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffcccccc000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff333333999999
-ffffffffffffffffffcccccc666666999999999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000000000ffffffffffffffffff000000ffffffffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000ccccccffffffffffffffffffffffffffffff
-ffffffcccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ffffffffffffffffffffffff666666666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000000000000000333333333333666666999999
-ccccccccccccffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff000000ffffffffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000
-ffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ccccccffffffffffffffffffffffffffffff
-999999000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333000000000000000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333000000000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666666666333333000000
-000000000000cccccccccccc333333ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff000000000000000000ffffff000000000000000000000000000000000000
-000000000000cccccc000000000000000000000000000000ffffffffffffcccccc000000
-000000ffffffffffff000000000000ffffff000000000000000000000000000000ffffff
-000000ffffffccccccffffff000000000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000333333ffffffffffffffffffffffffffffffcccccc
-000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000
-333333000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000666666ffffffffffffffffffffffff999999000000
-000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000000000333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333000000000000ccccccffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000999999ffffffffffffffffff999999000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000000000000000999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffff666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333333333666666000000000000999999ffffffffffff999999000000000000666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333000000000000000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccccccccffffffffffffffffff999999
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000000000000000000000666666999999000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000000000333333999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000000000000000000000000000000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffff999999333333ccccccffffffffffffcccccc
-333333000000000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000000000666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000000000000000000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000999999cccccc666666000000
-000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999333333000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000ffffffffffffffffff000000000000ffffff000000000000ffffff000000000000
-000000ffffff000000000000000000ffffff000000000000000000ffffffffffffffffff
-ffffff000000000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000ffffff000000000000
-ffffff000000000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000000000000000000000000000000000ccccccffffffffffff
-ffffffffffffffffffffffffcccccc333333000000000000000000000000000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffff000000000000ffffffffffff000000ffffff000000
-ffffffffffffcccccc000000ffffffffffffffffffffffff000000ffffffccccccffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff000000000000ccccccffffff
-ffffffffffff000000ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000ffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000000000000000000000000000000000000000000000ccccccffffff
-ffffffffffffffffffffffff666666000000000000000000000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666000000333333999999ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffff000000000000000000ffffffffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000000000000000000000000000333333666666ccccccffffffffffff
-ffffffffffffffffff999999000000000000000000000000000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000000000999999ffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999000000333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-000000ffffff000000ffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffff000000ffffff000000ffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffff000000000000ffffff000000000000
-ffffff000000000000ffffff000000000000000000000000000000ffffffffffffffffff
-000000000000000000000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc000000000000333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc000000000000000000000000000000000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000000000000000ffffff000000000000000000ffffff000000000000000000ffffff
-000000000000ffffffffffffffffffffffff000000000000000000000000000000ffffff
-000000000000000000000000ffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000000000000000000000ffffff
-ffffffffffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffff000000000000ffffff000000
-ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccccccccc999999999999666666999999666666333333333333333333333333333333
-333333333333000000333333333333333333333333333333333333333333333333666666
-666666999999333333666666666666666666666666666666666666999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333999999ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffff000000ffffffffffff000000ffffffcccccc000000
-ffffffffffff000000ffffffffffff000000ccccccffffff000000ffffff000000ffffff
-ffffffffffff000000ffffff000000ffffff000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffcccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333000000
-333333333333666666666666999999666666999999cccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc666666
-999999666666666666333333666666000000000000000000666666666666999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666333333000000333333666666666666ccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666333333
-000000333333666666ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffff999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000ffffff000000ffffffffffff000000000000
-ffffff000000000000ffffff000000000000ffffff000000000000ffffff000000000000
-000000000000000000000000000000ffffff000000000000000000ffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000666666999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999666666333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-999999ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff999999333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffcccccc333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff999999333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333000000
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffff999999000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffff666666666666000000666666
-ccccccffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffcccccc666666333333666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333999999ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffcccccc000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff999999
-333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000999999ffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffcccccc666666333333000000666666666666ccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999999999
-333333000000333333666666999999ffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999ffffffffffffffffffccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffff999999999999333333333333000000333333333333
-666666666666999999999999ccccccccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccccccccccccccc666666999999333333666666333333000000333333666666666666
-ccccccffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-999999999999666666666666333333333333333333000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000333333
-333333333333333333999999666666cccccc999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000000000
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333666666ccccccffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666ffffffffffffffffff999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ccccccffffffffffffcccccc666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000999999
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333000000000000333333
-666666999999999999ffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffcccccc999999999999333333333333000000333333
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999666666666666ffffffffffffffffff000000000000000000000000000000000000
-000000000000ffffffffffffffffff333333666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999333333333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333
-333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333000000000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000333333333333999999
-ccccccffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000333333666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999000000000000333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666
-333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccccccccc666666333333000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000000000000000000000000000333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666666666000000000000333333666666666666ccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333
-000000333333333333999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000000000000000000000000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333000000
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999999999333333333333000000
-333333333333999999666666999999999999cccccc999999ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccccccccc999999
-cccccc666666999999666666666666333333000000333333666666666666cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000000000000000000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-000000000000666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccccccccc666666999999666666666666333333333333333333333333000000000000
-000000000000000000000000000000000000000000000000333333333333333333333333
-333333999999666666999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000000000000000000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333000000666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-333333000000000000ffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999999999000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333666666cccccc
-ffffff000000000000ffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-000000000000666666ffffffffffffcccccc999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000999999ccccccffffffffffff
-ffffff000000000000ffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000000000666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333333333999999ffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333000000333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333000000000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffccccccffffff666666999999ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333000000333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666333333666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000000000ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666000000999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666000000000000ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999333333333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666000000000000999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999000000000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000
-666666ccccccffffffffffffffffffffffffcccccc333333333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333666666ffffffffffff666666333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffff666666333333999999ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff999999000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999000000000000333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333333333999999ffffffffffff333333333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffff666666333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999ccccccffffffffffffffffffffffffffffff999999666666333333666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666ffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffcccccc999999
-000000666666999999ccccccffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc999999333333333333666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666333333333333333333999999999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666
-666666000000333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666000000000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999666666666666333333000000333333333333
-333333999999666666cccccc999999cccccc999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999cccccc999999999999666666666666333333333333000000333333333333999999
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-cccccc666666999999333333666666333333666666000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000333333
-666666333333666666666666999999999999ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffcccccccccccc333333000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc000000000000666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffcccccc999999
-666666ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc000000000000666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccccccccffffffffffffffffffcccccc999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333666666ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666000000
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000333333999999ffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-333333000000000000333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666000000000000999999ccccccffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333000000333333666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000333333666666
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666666666ffffffffffffffffffffffff
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ccccccffffffffffffffffffcccccc666666666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666333333000000333333666666999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffff666666333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666666666000000000000
-000000666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999cccccc999999999999666666999999666666999999666666333333333333333333
-333333333333333333333333333333333333000000333333333333999999666666999999
-999999cccccc999999ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffccccccffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000666666
-ccccccffffffffffffffffffffffffffffffcccccc000000000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999333333000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc999999666666666666333333000000000000
-333333333333333333333333666666666666666666666666999999999999999999999999
-999999999999999999333333333333333333999999666666666666666666666666666666
-666666333333333333333333333333000000333333333333999999999999cccccccccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffff666666333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333666666ffffffffffffffffffcccccc333333000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffcccccc000000999999ffffffcccccc999999
-666666666666333333000000000000333333333333666666666666999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999000000666666999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc666666999999666666666666333333000000
-000000333333333333999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffcccccc666666
-000000333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999cccccc
-999999666666000000666666999999333333000000333333333333333333333333333333
-333333ccccccffffffffffff999999999999666666999999666666999999999999cccccc
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666333333333333333333
-666666666666999999cccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc999999999999666666666666000000333333666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666000000333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccccccccffffffffffff999999000000000000333333666666333333
-666666666666cccccc999999333333666666cccccccccccccccccccccccccccccccccccc
-ccccccccccccffffffffffffcccccc666666999999666666999999666666666666333333
-666666000000333333333333999999ffffffffffffccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666333333000000333333666666999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999666666333333000000000000666666
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666000000333333999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666333333
-000000000000333333999999ffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccccccccccccccffffffffffff999999000000000000333333666666
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999333333333333333333999999999999cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999666666
-666666000000333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffff999999666666666666cccccc999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-999999333333000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999000000333333
-666666999999999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999666666000000333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999666666000000000000666666ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff333333333333666666999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333333333666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccc999999666666333333333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333000000
-666666666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333333333333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000333333999999999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999666666000000666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffcccccc666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000999999999999cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc666666
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffccccccccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333333333666666cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999000000666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333000000333333ccccccffffffffffffffffffffffff
-cccccc666666000000333333999999ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffcccccc333333000000666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc666666333333666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999666666000000666666ccccccffffff666666
-333333666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999000000666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc666666666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333666666cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999666666000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000333333666666
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-cccccc000000000000999999ffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999999999ffffffffffff
-ffffff999999666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999999999ffffffffffffffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999999999ffffffffffff
-ffffff999999666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999999999ffffffffffffffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999999999ffffffffffff
-ffffff999999666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999999999ffffffffffffffffff999999666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999999999ffffffffffffffffff999999666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999666666ffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333666666999999cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666ffffffcccccc666666000000
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-cccccc666666333333ffffffcccccc000000000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000ccccccffffffcccccc000000000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000ccccccffffff
-cccccc000000000000666666ffffffffffff333333000000000000ffffffffffffcccccc
-000000000000999999ffffffcccccc000000000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000999999ffffffcccccc000000000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000999999ffffff
-cccccc000000000000666666ffffffffffff333333000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000666666ffffffffffff333333000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000666666ffffffffffff333333000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff333333000000000000ffffffffffffcccccc000000000000999999ffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000999999999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999000000666666ccccccffffffcccccccccccc
-666666000000ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff999999333333666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff333333333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff333333333333ffffffffffff
-ffffff666666333333ccccccffffffffffffcccccc333333999999ffffffffffffffffff
-333333333333ffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff333333333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff333333333333ffffffffffff
-ffffff666666333333ccccccffffffffffffcccccc333333999999ffffffffffffffffff
-666666333333ffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff666666333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff666666333333ffffffffffff
-ffffff666666333333ccccccffffffffffffcccccc333333999999ffffffffffffffffff
-666666333333ffffffffffffffffff666666333333ccccccffffffffffffcccccc333333
-999999ffffffffffffffffff666666333333ffffffffffffffffff666666333333cccccc
-ffffffffffffcccccc333333999999ffffffffffffffffff666666333333ffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666333333cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccc999999666666666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc666666333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666666666cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999666666
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333999999cccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-999999333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff666666999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccc999999333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-666666cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999666666333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc666666999999ffffffffffffffffff666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999333333ccccccffffffcccccc333333999999ffffff
-ffffffffffff666666666666666666ccccccccccccffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffff666666000000999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-666666333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccc666666999999ffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333333333000000000000000000333333333333999999999999
-cccccc000000000000ffffffffffffffffff999999000000000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccccccccc666666666666333333000000
-000000000000000000ffffffcccccc999999333333000000000000000000333333cccccc
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffcccccc000000000000000000000000000000000000000000
-999999ffffffffffffffffffffffffffffff666666666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffff000000000000000000000000000000000000
-000000999999ffffffffffffffffffffffff666666ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffcccccc000000000000000000000000666666999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000ffffffffffff000000ffffffffffffffffffffffffffffffffffff000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffff999999000000666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccc000000cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc000000cccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffff000000
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666666666cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999000000
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999666666666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000000000000000000000000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff000000000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffff000000000000
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000000000000000ffffff000000ffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffff000000000000ffffff000000000000ffffff000000000000ffffff000000
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc000000000000000000cccccccccccccccccc
-000000000000cccccc000000000000cccccc000000000000000000cccccc000000000000
-000000cccccc000000000000000000cccccccccccccccccccccccc000000000000000000
-000000cccccccccccc000000000000000000cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc000000000000cccccc000000000000cccccc000000000000000000
-000000000000cccccccccccccccccccccccccccccccccccccccccc000000000000000000
-cccccc000000cccccccccccccccccccccccc000000000000000000cccccc000000000000
-000000000000000000cccccccccccccccccccccccc000000000000000000cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffff000000ffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000ffffffffffff000000000000ffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffff000000000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000000000ffffff000000ffffff000000
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc999999cccccc
-999999000000000000cccccc999999000000999999000000999999cccccc999999000000
-999999cccccc999999cccccc000000cccccc999999cccccc000000cccccc999999cccccc
-999999cccccc000000cccccc999999cccccc000000cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc000000000000999999cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc
-000000cccccc000000cccccc999999000000999999cccccc999999000000999999000000
-999999cccccc999999cccccc999999cccccc000000cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999333333999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffffffffff000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccc000000cccccccccccccccccc
-cccccc000000cccccccccccccccccc000000cccccc000000cccccccccccc000000cccccc
-cccccccccccccccccccccccc000000cccccccccccccccccccccccc000000000000000000
-cccccccccccc000000cccccccccccccccccc000000cccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc000000cccccccccccccccccccccccccccccc000000cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc000000cccccc
-000000cccccc000000cccccccccccc000000000000000000000000000000cccccc000000
-cccccccccccccccccccccccccccccccccccccccccc000000000000000000000000cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffff000000000000ffffff000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000ffffffffffffffffff000000000000ffffff
-ffffffffffffffffffffffff000000ffffffffffff000000000000000000ffffffffffff
-000000000000000000000000ffffffffffffffffff000000000000000000ffffffffffff
-000000000000ffffff000000000000000000ffffff000000000000ffffffffffff000000
-ffffff000000000000ffffffffffff000000000000ffffff000000000000ffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000cccccc000000ffffffffffffffffff000000ffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-000000ffffffcccccc000000ffffffffffff000000ffffffffffff000000cccccc000000
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc000000cccccc999999cccccc
-999999000000999999cccccc999999000000999999cccccc000000cccccc000000cccccc
-999999cccccc999999cccccc000000cccccc999999cccccc999999cccccc999999cccccc
-000000cccccc000000cccccc999999cccccc000000cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc000000cccccc999999cccccc999999cccccc000000cccccc
-999999cccccc000000cccccc999999cccccc999999cccccc999999cccccc000000cccccc
-000000cccccc000000cccccc999999000000999999cccccc999999cccccc999999000000
-999999cccccc999999000000999999cccccc000000cccccc999999cccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffff000000000000ffffffffffff000000ffffffffffff
-000000ffffffcccccc000000ffffff000000ffffffffffff000000ffffff000000ffffff
-ffffffffffffffffffffffff000000ffffff000000ffffffffffff000000ffffff000000
-ffffffffffff000000ffffffffffffffffffffffffffffff000000ffffffcccccc000000
-ffffff000000ffffffffffff000000ffffff000000ffffff000000ffffffffffff000000
-000000ffffff000000ffffff000000ffffff000000ffffffffffff000000000000ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffff666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff000000
-000000000000000000000000000000000000000000000000ffffff000000000000000000
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666666666cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc000000000000000000000000000000cccccc
-000000000000000000cccccc000000000000000000cccccc000000000000cccccccccccc
-cccccccccccc000000000000000000000000000000cccccc000000000000000000000000
-cccccccccccccccccc000000000000000000cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc000000000000000000000000cccccccccccccccccccccccc000000
-000000000000cccccccccccccccccccccccccccccccccccccccccc000000000000000000
-000000000000000000000000cccccccccccc000000000000000000000000cccccccccccc
-000000000000000000cccccccccccccccccc000000000000000000000000000000000000
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc999999666666ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffffffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffffffffff000000000000ffffffffffffffffff000000ffffff000000
-000000000000ffffffffffffffffffffffffffffffffffff000000ffffff000000000000
-000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff000000
-ffffffffffff000000000000000000000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-ffffffffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffff000000ffffff000000ffffffffffff000000ffffff000000ffffff
-ffffff000000ffffff000000ffffffffffff000000ffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999cccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccc999999666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffff000000ffffffffffffffffff000000000000000000000000
-000000ffffff000000000000000000ffffffffffff000000000000000000ffffffffffff
-ffffffffffffffffff000000000000000000000000000000ffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000ffffffffffff000000000000
-000000ffffffffffff000000000000000000000000000000ffffffffffff000000ffffff
-ffffff000000000000000000000000000000ffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff333333000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-000000333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc000000000000000000000000
-000000000000000000cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999000000000000000000000000000000000000000000999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffff000000ffffffffffff000000cccccc000000ffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccc666666666666ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-000000000000ffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999999999333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-999999333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc333333999999cccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc333333999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc333333666666cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc333333999999cccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc666666333333999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff333333333333999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999999999333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff666666ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc999999666666333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999333333999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999000000999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000666666999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999999999333333333333999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666000000ccccccffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffff999999333333333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999333333333333999999cccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999
-666666000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666333333999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999000000333333666666cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999666666333333333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666000000666666999999cccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccc999999333333333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000999999ffffffcccccc
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000666666
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999999999333333333333333333999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000666666ffffffffffffffffff
-666666333333000000999999ccccccffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffff999999333333000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000000000000000ffffff000000ffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffff000000
-000000ffffff000000000000ffffff000000000000ffffff000000000000000000ffffff
-000000000000ffffff000000000000ffffff000000000000000000000000000000ffffff
-ffffff000000000000000000ffffff000000000000ffffff000000000000000000000000
-000000ffffff000000000000ffffffffffff000000000000000000000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-333333000000666666666666cccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999
-999999333333333333333333999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000000000666666ffffffffffffffffffffffff
-ffffffffffffcccccc333333333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000ffffffffffff000000000000ffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffff000000000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-000000000000ffffff000000000000ffffff000000000000ffffffffffffffffff000000
-ffffff000000000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffff000000000000ffffffffffff000000ffffff
-000000000000ffffff000000ffffff000000ffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000333333666666999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999999999666666333333000000333333
-666666999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000000000333333ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000ffffff000000ffffffffffffffffff000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000000000ffffffffffffffffff000000
-ffffff000000ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffffffffff000000ffffff000000ffffffffffffffffff000000ffffff
-000000ffffffffffff000000ffffffffffff000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666333333000000666666666666
-999999999999cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccc999999999999333333333333333333999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff333333000000333333ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff
-ffffff000000cccccc000000ffffffffffffffffff000000ffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-000000ffffffcccccc000000ffffffffffff000000000000ffffffffffffcccccc000000
-ffffff000000ffffffffffff000000ffffffcccccc000000ffffff000000ffffffffffff
-000000ffffffccccccffffff000000ffffff000000ffffffffffffffffff000000ffffff
-000000ffffffffffff000000ffffff000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-333333000000000000333333333333666666666666999999999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc666666666666333333333333
-333333000000000000333333666666ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff333333000000333333ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-666666333333000000666666999999ccccccccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffcccccc999999333333000000000000666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-000000ffffffffffffffffff000000000000000000ffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff000000
-000000000000ffffff000000000000ffffff000000000000000000000000000000ffffff
-000000000000000000ffffff000000000000000000000000000000000000000000ffffff
-ffffff000000000000000000ffffff000000000000000000ffffff000000000000000000
-000000000000ffffff000000000000000000000000000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccccccccc666666666666333333333333000000333333333333
-666666666666666666666666999999999999999999999999cccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccc999999999999999999999999999999
-999999666666666666666666666666333333000000000000666666666666999999999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333000000333333ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff999999666666666666ffffffffffffffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666333333666666ccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-666666999999666666666666333333333333333333333333000000000000000000000000
-000000000000000000000000000000000000000000333333333333333333333333333333
-333333999999666666999999999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333000000333333
-333333666666666666999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccccccccc999999999999333333333333000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff000000000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666
-000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999999999333333999999ffffffffffff999999666666666666999999999999
-ccccccccccccccccccccccccffffff999999999999ccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccccccccc999999cccccccccccc999999666666
-999999ccccccffffffffffff999999333333666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffff999999000000000000000000000000000000000000000000000000000000000000
-999999ffffffffffffffffff333333999999ffffffffffffffffff999999000000000000
-000000000000000000000000000000000000000000000000999999ffffffffffffffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000000000333333999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000
-000000ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999999999666666999999
-333333333333333333999999ffffff999999000000000000000000000000000000000000
-000000000000333333ffffffffffffcccccc333333333333333333333333666666999999
-666666ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-ffffffffffffffffffffffffccccccccccccffffffffffffffffffcccccccccccccccccc
-ccccccccccccccccccccccccccccccccccccccccccccccccffffffffffffffffffffffff
-ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000000000000000000000000000
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000000000666666
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999000000000000999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000000000333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000
-000000000000333333666666ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc333333000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000000000000000000000000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999
-666666333333000000000000000000000000333333999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999999999000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333000000000000000000000000333333999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc333333000000666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000000000
-000000333333999999ccccccffffffffffffffffff333333333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000000000000000000000
-000000666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc333333000000333333ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffff999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc999999333333
-000000000000000000000000333333666666ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff666666000000333333ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc999999333333333333000000000000000000000000333333999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffff666666000000333333ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc666666333333000000000000000000000000333333
-999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff666666000000333333ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999666666000000000000
-000000000000000000666666999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999000000000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999333333000000000000000000000000333333666666ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999999999666666999999333333333333333333333333333333333333333333666666
-666666999999666666ccccccccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999333333000000000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffffff999999
-000000000000999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000
-333333666666666666666666999999999999999999999999999999999999999999666666
-666666666666666666333333333333000000666666999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666333333000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999000000333333333333999999999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999666666000000333333999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffffcccccc000000000000
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffcccccc333333333333999999cccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccc999999666666333333
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc999999333333000000000000000000000000333333666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ffffffffffffffffffffffffffffffffffff000000000000333333
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffff999999333333999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999999999
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffff000000ffffffffffffffffffffffffffffffccccccffffff
-ffffff000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffcccccc999999333333000000000000000000000000000000
-333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999
-666666000000000000666666666666999999ffffffffffff000000000000333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffcccccc333333cccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666333333000000
-000000000000000000333333666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000
-000000000000000000000000000000666666ffffff333333000000333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffff999999333333cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffff000000ffffff000000000000000000000000ffffff
-000000000000000000ffffff000000000000ffffff000000000000000000ffffffffffff
-000000000000ffffff000000000000ffffff000000000000ffffffffffffffffff000000
-ffffffffffffffffff000000000000000000000000ffffffffffffffffff000000000000
-ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff000000
-000000ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff
-ffffff000000000000ffffff000000000000ffffff000000000000000000000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999666666000000000000000000000000000000666666666666ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000000000000000000000000000ffffff333333000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffcccccc333333cccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-ffffffffffffffffffffffffffffff000000ffffffffffff000000ffffffffffff000000
-ffffff000000ffffffffffffffffff000000000000ffffffffffffffffff000000ffffff
-ffffff000000000000ffffff000000ffffffffffffffffff000000ffffffffffff000000
-ffffffffffffffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff
-000000ffffffffffffffffffffffff000000ffffff000000000000ffffff000000ffffff
-000000ffffffffffff000000ffffff000000000000ffffff000000ffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999333333000000000000000000000000333333666666
-ccccccffffffffffffffffffffffffffffffffffff333333999999ffffffffffff666666
-000000000000000000000000999999666666000000000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffff666666666666999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-ffffffffffffffffffffffffffffff000000ccccccffffff000000ffffffffffff000000
-ffffff000000ccccccffffffffffff000000000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffff000000ffffff000000000000000000ffffffffffff000000
-ffffffffffffccccccffffff000000ffffffffffff000000ffffff000000000000000000
-000000ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffffcccccc000000000000000000ffffff000000ffffffffffff000000ffffff
-000000000000000000000000ffffff000000ccccccffffff000000ffffff000000ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333000000000000000000
-000000000000333333999999ccccccffffffcccccc000000000000999999ffffffffffff
-000000000000000000000000666666000000000000ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-000000000000000000000000000000999999ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffffffffff999999333333666666cccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc999999333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffff000000ffffffffffff000000
-ffffff000000ffffffffffffffffff000000000000ffffffffffffffffff000000ffffff
-ffffff000000ffffffffffff000000000000ffffffffffff000000ffffffffffff000000
-ffffffffffffffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff
-ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffffffffff
-000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-000000ffffffffffffffffffffffff000000ffffffffffff000000ffffff000000ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-333333000000000000000000000000000000666666000000000000000000666666ffffff
-999999000000000000333333000000000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc666666000000000000000000
-000000333333999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffcccccc666666000000666666666666cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc999999cccccc
-999999cccccc999999cccccc999999cccccc999999999999333333000000333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffff000000000000000000ffffff
-ffffff000000000000ffffff000000000000000000000000000000000000ffffffffffff
-000000000000000000ffffff000000000000000000000000000000000000000000000000
-000000ffffffffffffffffff000000000000000000ffffffffffffffffff000000000000
-000000ffffff000000000000000000000000000000000000ffffff000000000000ffffff
-000000000000ffffff000000000000000000000000000000000000ffffff000000000000
-ffffff000000000000000000000000000000000000ffffff000000000000000000000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999666666000000000000000000000000000000000000000000666666
-cccccc000000000000000000000000999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff999999666666000000000000000000000000333333999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffff999999666666000000333333333333
-666666999999999999999999cccccccccccccccccccccccccccccccccccccccccc999999
-999999999999999999666666666666333333000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc000000000000000000000000000000000000
-666666333333000000000000999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffcccccc666666000000000000000000000000333333999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999999999
-666666333333333333333333000000000000000000000000000000000000000000333333
-333333333333333333999999999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff000000000000000000ccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffff000000000000000000ccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff999999cccccc666666666666000000000000000000000000000000000000
-000000333333000000333333333333333333333333333333333333333333333333333333
-666666666666000000000000000000666666999999ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc999999666666666666000000
-000000000000666666333333999999333333000000000000666666666666999999999999
-cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-999999666666666666666666666666333333333333000000333333333333999999999999
-ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccccccccc666666333333000000333333666666999999999999ffffff
-ffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc999999666666
-333333000000333333666666999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333666666999999ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc999999666666000000666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-333333999999ccccccffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffff999999333333666666ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000ffffffffffff000000000000ffffff000000000000000000000000
-ffffffffffffffffff000000000000000000ffffffffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000000000000000ffffffffffffffffff
-000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff000000999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffcccccc000000ffffffffffff000000ffffff000000ffffffccccccffffff
-000000ffffff000000ffffffffffffffffffcccccc000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333333333
-666666ffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffcccccc666666333333666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffffffffff
-000000ffffff000000ffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-999999000000666666999999ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffcccccc999999333333333333999999ffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000ffffffffffff000000000000000000ffffff000000000000000000
-ffffffffffffffffff000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffff999999666666333333000000333333999999999999ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-666666666666000000333333666666ccccccffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffcccccc666666666666333333000000333333
-666666333333999999666666ccccccccccccccccccccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccccccccccccccc999999999999666666666666333333333333000000333333333333
-999999999999ffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-ffffffffffffffffff666666333333333333333333ffffffffffffffffff666666999999
-666666999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffcccccc000000
-000000000000ffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-999999cccccc666666999999333333333333333333333333000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-333333333333333333666666666666999999999999ccccccccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff666666000000333333333333
-ffffffffffffffffff999999ccccccccccccccccccffffffffffffffffff999999666666
-999999666666666666000000000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffff999999ccccccffffffffffff999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffcccccc000000999999ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffff
-ffffffffffffcccccc000000666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffcccccc000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffff000000
-ffffffffffff000000ffffffffffff000000ffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffff000000000000ffffffffffff000000
-000000ffffff000000000000ffffff000000000000000000000000000000000000ffffff
-ffffff000000000000000000ffffffffffff000000000000ffffff000000ffffff000000
-000000ffffff000000000000ffffff000000000000ffffff000000000000000000ffffff
-ffffff000000000000000000000000ffffffffffff000000000000ffffff000000000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-ffffffffffffffffff000000000000ffffffffffff000000000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000cccccc
-ffffff999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff
-000000000000ffffff000000000000ffffff000000ffffff000000ffffffffffff000000
-000000ffffffffffffffffff000000ffffffffffff000000000000ffffffffffffffffff
-ffffff000000ffffff000000000000ffffff000000ffffffffffff000000ffffffffffff
-ffffffffffff000000ffffffffffff000000ffffffffffff000000000000000000ffffff
-ffffffffffff000000ffffff000000ffffffffffff000000ffffff000000ffffffffffff
-000000ffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffff666666333333ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffff000000ffffffffffffffffff000000000000000000000000ffffffffffff
-000000ffffffcccccc000000ffffffffffff000000ffffff000000ffffffcccccc000000
-000000ffffffffffffffffff000000ffffffcccccc000000ffffffffffffffffff000000
-000000000000cccccc000000ffffffffffff000000ffffff000000ffffffccccccffffff
-ffffffffffff000000ffffffffffff000000ccccccffffff000000ffffff000000ffffff
-ffffffffffff000000ffffff000000ffffffffffffffffffffffff000000000000000000
-000000ffffffffffff000000000000ffffffcccccc000000000000ffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffff999999000000ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffffffffffffff
-000000ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffff000000
-000000ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff
-ffffff000000ffffff000000ffffffffffffffffff000000000000ffffffffffffffffff
-ffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000ffffff
-ffffffffffff000000ffffff000000ffffffffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffccccccccccccffffffffffffccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffff000000000000000000ffffff000000
-000000000000ffffff000000000000ffffff000000000000000000000000000000ffffff
-ffffff000000000000000000ffffffffffff000000000000000000ffffffffffff000000
-000000000000000000000000000000ffffffffffff000000ffffffffffffffffffffffff
-ffffffffffff000000000000000000ffffffffffff000000000000000000ffffff000000
-000000000000ffffffffffffffffff000000000000000000ffffffffffff000000000000
-000000ffffff000000000000ffffffffffff000000000000ffffffffffffffffff000000
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffff333333333333333333666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffff999999
-ccccccffffffffffffcccccc666666000000333333999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc666666ffffff
-ffffffffffff333333000000000000000000ffffffffffffffffff000000000000333333
-333333ffffffffffffffffff999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffcccccc000000000000000000ffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffff000000000000000000ffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff999999666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff999999666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc
-666666999999666666666666333333333333333333333333333333333333333333999999
-666666999999999999ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffff999999666666ffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffcccccc999999333333000000333333333333
-999999666666999999999999cccccccccccccccccccccccccccccccccccccccccc666666
-999999666666666666333333000000333333999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffff999999333333333333999999999999ffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffcccccc666666333333000000999999ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffcccccc333333666666ccccccffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffcccccc333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333
-999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999000000999999
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffff999999333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffff333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333333333ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666999999ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff666666000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccc333333666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-cccccc333333ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-333333ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999666666333333
-999999ccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffff666666333333ccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc666666
-666666ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-000000ffffffffffffffffff000000ffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffff000000ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-666666333333333333999999ccccccffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffcccccc999999666666000000999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc666666333333ffffff
-ffffffffffff999999333333ccccccffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffff999999333333999999
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffff000000ffffffffffff000000000000ffffff000000000000
-ffffff000000000000000000ffffff000000000000ffffff000000000000ffffff000000
-000000ffffffffffff000000000000ffffff000000000000ffffff000000000000ffffff
-ffffff000000000000ffffff000000000000000000000000000000ffffffffffffffffff
-000000000000000000000000ffffff000000000000ffffff000000ffffff000000000000
-000000ffffffffffff000000000000000000ffffffffffff000000000000ffffffffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffcccccc666666333333000000666666666666ccccccccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff999999
-999999333333333333333333666666999999ffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffcccccc333333333333666666ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999666666000000666666ccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000000000ffffffffffff000000ffffffffffff000000ffffff000000
-000000ffffffffffff000000000000ffffff000000000000ffffff000000ffffffffffff
-ffffff000000ffffffffffff000000000000ffffff000000000000ffffffffffff000000
-ffffffffffff000000000000ffffff000000ffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffff000000ffffff000000000000ffffff000000ffffffffffff
-ffffff000000000000ffffffffffff000000ffffff000000ffffffffffff000000ffffff
-000000ffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffff999999999999333333333333000000333333
-333333666666666666999999999999cccccc999999ccccccffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999cccccc999999999999666666999999333333333333333333000000333333666666
-666666ccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff666666666666ffffff
-ffffffffffffffffffffffffffffffcccccc999999333333333333333333666666666666
-ccccccccccccccccccccccccffffffffffffffffffffffffffffffffffffffffff999999
-cccccccccccc999999666666666666000000666666999999ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000ffffffffffffffffffffffffffffff000000000000000000000000ffffff000000
-ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000ffffff000000
-000000000000ffffffffffff000000ffffffffffff000000000000000000000000000000
-ffffffffffff000000ffffffffffff000000ffffff000000ffffffffffffffffffffffff
-ffffff000000ffffffffffff000000ffffff000000ffffffffffff000000ffffffffffff
-ffffff000000000000ffffffffffffffffffffffff000000000000000000000000ffffff
-ffffff000000000000ffffffffffff000000000000ffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc
-cccccc999999999999666666666666333333333333333333000000000000000000000000
-000000000000000000000000000000000000000000000000000000000000000000000000
-666666333333666666333333999999666666cccccc999999ccccccffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffcccccccccccc999999999999
-333333333333333333333333000000000000000000000000000000000000000000333333
-333333333333666666999999999999ffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-000000ffffffccccccffffffffffffffffff000000ffffffffffffffffffcccccc000000
-ffffffffffffffffff000000ffffffffffff000000ffffffffffff000000000000ffffff
-ffffff000000ccccccffffff000000ffffffffffff000000000000ffffffccccccffffff
-ffffffffffff000000ffffffffffff000000cccccc000000ffffffffffffffffffffffff
-ffffff000000ccccccffffff000000ffffff000000ffffffffffff000000ccccccffffff
-ffffff000000000000ffffffffffffffffffcccccc000000ffffffffffffffffffffffff
-ffffffffffff000000ffffffffffffffffff000000ffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000
-000000000000ffffffffffffffffffffffffffffff000000000000000000000000000000
-000000ffffff000000000000000000ffffff000000000000ffffff000000000000000000
-000000000000000000000000000000000000ffffff000000000000000000000000000000
-ffffff000000000000000000ffffff000000000000000000000000ffffffffffffffffff
-ffffff000000000000000000ffffff000000000000000000ffffffffffff000000000000
-000000ffffffffffff000000000000000000ffffffffffff000000000000000000ffffff
-000000000000ffffffffffff000000000000ffffffffffffffffff000000ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff333333333333ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-999999000000000000ccccccffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999999999ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000cccccc
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffccccccffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffff999999666666ffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc000000000000ccccccffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffff999999666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffff999999666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-999999666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-ccccccffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666ccccccffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666ccccccffffffffffffffffff
-cccccc666666ffffffffffffffffffcccccc666666ccccccffffffffffffffffff666666
-999999ffffffffffffffffffcccccc666666ffffffffffffffffffcccccc666666cccccc
-ffffffffffffffffff666666999999ffffffffffffffffffcccccc666666ffffffffffff
-ffffffcccccc666666ccccccffffffffffffffffff666666999999ffffffffffffffffff
-cccccc666666ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-cccccc333333000000ffffffffffff333333000000666666ffffffffffff666666000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff666666000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000666666ffffffffffff666666000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000666666ffffffffffff666666000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000666666
-ffffffffffff666666000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000333333ffffffffffff666666000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000333333ffffffffffff666666000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000333333
-ffffffffffff666666000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000333333
-ffffffffffff999999000000000000ffffffffffffcccccc000000000000999999ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffcccccc
-000000000000999999ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffcccccc000000000000999999ffffffffffff333333000000333333
-ffffffffffff999999000000000000ffffffffffffffffff000000000000999999ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffffffff
-000000000000999999ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffffffff000000000000666666ffffffffffff333333000000333333
-ffffffffffff999999000000000000ffffffffffffffffff000000000000666666ffffff
-ffffff333333000000333333ffffffffffff999999000000000000ffffffffffffffffff
-000000000000666666ffffffffffff333333000000333333ffffffffffff999999000000
-000000ffffffffffffffffff000000000000666666ffffffffffff666666000000333333
-ffffffffffff999999000000000000ffffffffffffffffff000000000000666666ffffff
-ffffff666666000000333333ffffffffffff999999000000000000ffffffffffffffffff
-000000000000666666ffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffff999999333333999999ffffffffffffffffff333333
-999999ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333999999ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333999999ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff666666333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff666666333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-666666333333ffffffffffffffffff999999333333999999ffffffffffffffffff333333
-666666ffffffffffffffffff999999333333ffffffffffffffffff999999333333999999
-ffffffffffffffffff333333666666ffffffffffffffffff999999333333ffffffffffff
-ffffff999999333333999999ffffffffffffffffff333333666666ffffffffffffffffff
-999999333333ffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffccccccffffffffffffffffffffffffffffffffffffffffffccccccffffff
-ffffffffffffffffffffffffffffffffffffccccccffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-ffffffffffffffffffffffff
-
-showpage
-
-% stop using temporary dictionary
-end
-
-% restore original state
-origstate restore
-
-%%Trailer
diff --git a/lib/inviso/doc/src/notes.xml b/lib/inviso/doc/src/notes.xml
deleted file mode 100644
index 661284e786..0000000000
--- a/lib/inviso/doc/src/notes.xml
+++ /dev/null
@@ -1,278 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2006</year><year>2011</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Inviso Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>notes.xml</file>
- </header>
- <p>This document describes the changes made to the Inviso application.</p>
-
-
- <section><title>Inviso 0.6.3</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>The <c>inviso</c> application has been deprecated and
- will be removed in the R16 release.</p>
- <p>
- Own Id: OTP-9798</p>
- </item>
- <item>
- <p>
- Eliminate use of deprecated regexp module</p>
- <p>
- Own Id: OTP-9810</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.6.2</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- The obsolete guards has now been changed to the new guard
- interface.</p>
- <p>
- Own Id: OTP-8747</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.6.1</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <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-8201</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.6</title>
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- In this release the following has been fixed and
- enhanced: Autostart: It is now possible to configure
- modules that shall be loaded by the autostart mechanism.
- This because it is not certain that all application
- systems make use of the OTP boot script to set up paths
- to all Erlang modules. Runtime_tools/Inviso: A bug in the
- fetch_log functionality has been fixed. Further a bug
- that was (supposedly) fixed in a previous patch
- concerning meta-tracer write_ti has been fixed (again) in
- this patch. A bug in inviso_as_lib making remote
- autostart config file updates fail has been fixed.
- Inviso: inviso_tool has been given a flush API.</p>
- <p>
- Own Id: OTP-6918</p>
- </item>
- </list>
- </section>
-
-</section>
-<section><title>Inviso 0.5</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- The following bugs/improvements have been done: Internal
- interworking between inviso_rt and inviso_rt_meta. The
- call function used by inviso_rt to call inviso_rt_meta is
- now protected with a monitor. Inviso_rt_meta now includes
- the timestamp of the incoming meta trace message when
- calling the call-callback. (Makes it possible to add a
- "better" timestamp to the ti-file.) Bug in inviso_tool
- making it not remove trace patterns when terminating. Bug
- in internal function h_start_session making inviso_tool
- crash if there were no active nodes to start the session
- on. The user-inviso_tool and inviso API-inviso control
- component request/response gen_server protocols had
- default time-out. Since many trace operations can be time
- consuming, a longer time-out is necessary. Improved
- overload protection. It is now possible to let the
- overload protection renew itself (e.g after an exit from
- an external overload protector). Inviso_rt_meta now fully
- uses the exception_trace match spec action term. Run
- Trace Case API (as in contrast to activate and deactivate
- trace case APIs) in inviso_tool. Flush trace-port API
- added to inviso. Get_session_data API added to
- inviso_tool. Improved inviso_tool:stop making it possible
- to name nodes which shall not have their trace patterns
- removed when inviso_tool terminates. Bug in handling of
- writing multiple ti-entries if returned from a
- call/return_from call-back in inviso_rt_meta Process
- trace flags are no longer explicitly removed by the
- inviso_tool when it terminates. Not necessary.
- Inviso_tool get_autostart_data adopted to standard
- autostarter.</p>
- <p>
- *** INCOMPATIBILITY with Meta trace call-backs are called
- with different arguments now. ***</p>
- <p>
- Own Id: OTP-6881</p>
- </item>
- </list>
- </section>
-
-</section>
-<section><title>Inviso 0.4</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- A bug in inviso_rt_meta caused an ETS table containing
- information on initiated (init_tpm) functions to be lost
- when suspending tracing. Further an enhancement to
- inviso_rt has been introduced making it possible to
- activate process trace flags based on globally registered
- names. It is then not an error to activate a global name
- on a node where the name does not reside. The process
- count in the return value will simply be set to zero
- (hence exactly one node in the NodeResult part of the
- return value will indicate one matching process found). A
- bug was found in fetch_log API. At the same time the
- fetch_log functionality was enhanced to also offer flow
- control making fetcher processes send chunks of
- transferred file data at a slower pace.</p>
- <p>
- Own Id: OTP-6703</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- Minor Makefile changes.</p>
- <p>
- Own Id: OTP-6689 Aux Id: OTP-6742 </p>
- </item>
- </list>
- </section>
-
-</section>
-
-
-<section><title>Inviso 0.3</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- In this release the first working version of the
- inviso_tool is included. Updates and small bugfixes in
- the inviso application.</p>
- <p>
- Own Id: OTP-6677</p>
- </item>
- </list>
- </section>
-
-</section>
-<section><title>Inviso 0.2.1</title>
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- Misc improvements.</p>
- <p>
- Own Id: OTP-6576</p>
- </item>
- </list>
- </section>
-
-</section>
-
-<section><title>Inviso 0.2</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p>
- This ticket includes several improvements and bugfixes to
- both runtime_tools and inviso. The overload mechanism can
- now also react to incoming messages. This is useful if
- an external overload watch-dog is used. Some improvements
- of variable bindings has been done to the default
- autostart mechanism - inviso_autostart_server. Autostart
- "jobs" can now be done in parallel, allowing for some
- jobs to hang waiting for some parts of the traced system
- to become ready before proceeding. Previously when using
- named meta-match-specs (tpm_ms) ending up with zero
- match-specs still kept the meta trace pattern active.
- This caused zero match-specs to be equal to unlimited
- meta tracing on that particular function. If the internal
- database becomes empty of meta match specs, meta trace
- pattern is removed for that function. Standard public
- loop data in the inviso runtime meta tracer process is
- now extended to a 2-tuple. The functions ctp/1 and ctpl/1
- are added making it possible to remove trace patterns for
- a list of functions rather than one by one.
- Inviso_rt_meta will now accept a list of binaries to be
- output into the trace information file, in additions to a
- single binary. Further it is also possible to make own
- output to the trace information file using the write_ti/1
- function. An error was discovered in inviso_rt making the
- inviso_rt_meta remain rather than terminate if the
- inviso_rt terminated due to "running alone" (not allowed
- to run without a control component). A new tool,
- inviso_tool, has been added to the inviso application.</p>
- <p>
- Own Id: OTP-6426</p>
- </item>
- </list>
- </section>
-
-</section>
-
- <section>
- <title>Inviso 0.1</title>
- <p>First version.</p>
- </section>
-
-</chapter>
-
diff --git a/lib/inviso/doc/src/ref_man.xml b/lib/inviso/doc/src/ref_man.xml
deleted file mode 100644
index c9e75e4029..0000000000
--- a/lib/inviso/doc/src/ref_man.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE application SYSTEM "application.dtd">
-
-<application xmlns:xi="http://www.w3.org/2001/XInclude">
- <header>
- <copyright>
- <year>2006</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>Inviso Reference Manual</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- </header>
- <description>
- <p><em>Inviso</em>, an Erlang trace tool.</p>
- </description>
- <xi:include href="inviso.xml"/>
- <xi:include href="inviso_as_lib.xml"/>
- <xi:include href="inviso_lfm.xml"/>
- <xi:include href="inviso_lfm_tpfreader.xml"/>
- <xi:include href="inviso_rt.xml"/>
- <xi:include href="inviso_rt_meta.xml"/>
-</application>
-
diff --git a/lib/inviso/ebin/.gitignore b/lib/inviso/ebin/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/ebin/.gitignore
+++ /dev/null
diff --git a/lib/inviso/include/.gitignore b/lib/inviso/include/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/include/.gitignore
+++ /dev/null
diff --git a/lib/inviso/info b/lib/inviso/info
deleted file mode 100644
index 3190d6d1cc..0000000000
--- a/lib/inviso/info
+++ /dev/null
@@ -1,2 +0,0 @@
-group: tools
-short: A trace tool for both development and delivered systems.
diff --git a/lib/inviso/priv b/lib/inviso/priv
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/inviso/priv
+++ /dev/null
diff --git a/lib/inviso/src/Makefile b/lib/inviso/src/Makefile
deleted file mode 100644
index 292a2bec99..0000000000
--- a/lib/inviso/src/Makefile
+++ /dev/null
@@ -1,104 +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$
-#
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../vsn.mk
-VSN=$(INVISO_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/inviso-$(VSN)
-
-# ----------------------------------------------------
-# Common Macros
-# ----------------------------------------------------
-
-MODULES= \
- inviso \
- inviso_c \
- inviso_lfm \
- inviso_lfm_tpfreader \
- inviso_tool \
- inviso_tool_lib
-
-
-#HRL_FILES= ../include/
-
-ERL_FILES= $(MODULES:%=%.erl)
-
-TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
-
-APP_FILE= inviso.app
-
-APP_SRC= $(APP_FILE).src
-APP_TARGET= $(EBIN)/$(APP_FILE)
-
-APPUP_FILE= inviso.appup
-
-APPUP_SRC= $(APPUP_FILE).src
-APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_unused_vars -I../include
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug opt: $(TARGET_FILES)
-
-clean:
- rm -f $(TARGET_FILES)
- rm -f errs core *~
-
-$(APP_TARGET): $(APP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) "$(RELSYSDIR)/src"
- $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src"
-# $(INSTALL_DIR) "$(RELSYSDIR)/include"
-# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include"
- $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
- $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
-
-release_docs_spec:
-
-
-
-
-
-
-
diff --git a/lib/inviso/src/inviso.app.src b/lib/inviso/src/inviso.app.src
deleted file mode 100644
index 91eaa1b9b2..0000000000
--- a/lib/inviso/src/inviso.app.src
+++ /dev/null
@@ -1,13 +0,0 @@
-{application,inviso,
- [{description, "INVISO trace tool"},
- {vsn, "%VSN%"},
- {modules, [inviso_c,inviso,
- inviso_lfm,inviso_lfm_tpfreader
- ]},
- {registered, [inviso_c]},
- {applications, [kernel, stdlib, runtime_tools]},
- {env, []}
- ]}.
-
-
-
diff --git a/lib/inviso/src/inviso.appup.src b/lib/inviso/src/inviso.appup.src
deleted file mode 100644
index 54a63833e6..0000000000
--- a/lib/inviso/src/inviso.appup.src
+++ /dev/null
@@ -1 +0,0 @@
-{"%VSN%",[],[]}.
diff --git a/lib/inviso/src/inviso.erl b/lib/inviso/src/inviso.erl
deleted file mode 100644
index 07bdf3e649..0000000000
--- a/lib/inviso/src/inviso.erl
+++ /dev/null
@@ -1,1056 +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$
-%%
-%% Author: Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%%
-%% Description: API module for the inviso system.
-%% Inviso consists of a control component and possibly one or more runtime
-%% components. This module is simply the API to the inviso system. All normal
-%% calls goes through the control component.
-%% ------------------------------------------------------------------------------
-
--module(inviso).
--deprecated(module).
-
-%% ------------------------------------------------------------------------------
-%% Exported API functions.
-%% ------------------------------------------------------------------------------
-
--export([start/0, start/1,
- add_node/1, add_node/2, add_node_if_ref/1, add_node_if_ref/2,
- add_nodes/2, add_nodes/3, add_nodes_if_ref/2, add_nodes_if_ref/3,
- change_options/1, change_options/2,
- init_tracing/1, init_tracing/2,
- stop_tracing/0, stop_tracing/1,
- clear/0, clear/1, clear/2,
- flush/0,flush/1,
- stop/0, stop_nodes/0, stop_nodes/1, stop_all/0,
- tp/1,tp/2,tp/4,tp/5,tp/6,
- tpl/1,tpl/2,tpl/4,tpl/5,tpl/6,
- tpm_localnames/0,tpm_localnames/1,tpm_globalnames/0,tpm_globalnames/1,
- init_tpm/4,init_tpm/5,init_tpm/7,init_tpm/8,
- tpm/4,tpm/5,tpm/6,tpm/8,tpm/9,
- tpm_tracer/4,tpm_tracer/5,tpm_tracer/6,tpm_tracer/8,tpm_tracer/9,
- tpm_ms/5,tpm_ms/6,
- tpm_ms_tracer/5,tpm_ms_tracer/6,
- ctpm_ms/4,ctpm_ms/5,ctpm/3,ctpm/4,
- ctpm_localnames/0,ctpm_localnames/1,ctpm_globalnames/0,ctpm_globalnames/1,
- ctp/1,ctp/2,ctp/3,ctp/4,
- ctpl/1,ctpl/2,ctpl/3,ctpl/4,
- tf/1, tf/2, tf/3,
- ctf/1, ctf/2, ctf/3,
- ctp_all/0, ctp_all/1, ctf_all/0, ctf_all/1,
- suspend/1, suspend/2,
- cancel_suspension/0, cancel_suspension/1,
- get_status/0, get_status/1,
- get_tracerdata/0, get_tracerdata/1,
- list_logs/0, list_logs/1,
- fetch_log/2, fetch_log/3, fetch_log/4,
- delete_log/0, delete_log/1, delete_log/2,
- subscribe/0, subscribe/1,
- unsubscribe/0, unsubscribe/1]).
-
-%% debuging inviso
--export([state/0, state/1]).
-
-%% ------------------------------------------------------------------------------
-%% Macros used in this module.
-%% ------------------------------------------------------------------------------
-
--define(CONTROLLER,inviso_c).
-
-%% Some function calls to runtime components may take long time, we must wait
-%% longer than the standard timeout for a reply from the control component.
--define(CALL_TIMEOUT,60000).
-%% ------------------------------------------------------------------------------
-
-
-
-%% =============================================================================
-%% CONTROL COMPONENT API FUNCTIONS.
-%% =============================================================================
-
-%% start()={ok,pid()}|{error,Reason}
-%% start(Options)={ok,pid()}|{error,Reason}
-%% Options=[Option,...], the options will be default options to runtime components
-%% later started. See add_node about available options.
-%% There are also options consumed by the control component:
-%% Option={subscribe,pid()}
-%%
-%% Starts a control component process on the local node. A control component must
-%% be started before runtime components can be started manually.
-start() ->
- gen_server:start({local,?CONTROLLER},?CONTROLLER,{self(),[]},[]).
-
-start(Options) ->
- gen_server:start({local,?CONTROLLER},?CONTROLLER,{self(),Options},[]).
-%% -----------------------------------------------------------------------------
-
-%% add_node(Reference)=NodeResult|{error, Reason}
-%% add_node(Reference,Options)=NodeResult|{error, Reason}
-%% Reference=PreviousReference = term(),
-%% Options=[Option,...],
-%% Option={dependency,Dep}
-%% Dep=integer()|'infinity'; The timeout before the runtime component will
-%% terminate if abandoned by this control component.
-%% Option={overload,Overload}; controls how and how often overload checks shall
-%% be performed. Instead of specifying a tuple, the atom 'overload' can be
-%% specified to state no loadcheck. The result will actually be the same
-%% if 'infinity' is used as intervall. It is sometimes necessary to
-%% initialize the overlaod check. This can be done with InitMFA. The
-%% loadchecker must then also be removed by using a RemoveMFA.
-%% Overload=Iterval (int() in milliseconds) |
-%% {LoadMF,Interval}|{LoadMF,Interval,InitMFA,RemoveMFA}
-%% LoadMF={Mod,Func}
-%% InitMFA,RemoveMFA={Mod,Func,ArgList}|void
-%% If just Interval is used, it means using a default overload check.
-%% NodeResult={ok,NAns}|{error,Reason}
-%% NAns=new|{adopted,State,Status,PreviousReference}|already_added
-%% Status = running | {suspended, SReason}
-%%
-%% Starts or tries to connect to an existing runtime component at the local
-%% node, regardless if the system is distributed or not.
-%% Options will override any default options specified at start-up of the
-%% control component.
-add_node(Reference) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,any_ref},?CALL_TIMEOUT).
-
-add_node(Reference,Options) when is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,any_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% add_node(Reference)=NodeResult|{error,{wrong_reference,OtherRef}}|{error,Reason}
-%% add_node(Reference,Options)=NodeResult|{error,{wrong_reference,OtherRef}}|
-%% {error,Reason}
-%%
-%% As add_node/1,/2 but will only connect to an already existing runtime component
-%% if its reference is the same as the one given as argument.
-add_node_if_ref(Reference) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,if_ref},?CALL_TIMEOUT).
-
-add_node_if_ref(Reference,Options) when is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,if_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% add_nodes(Nodes,Reference)={ok,NodeResults}|{error,Reason}
-%% add_nodes(Nodes,Reference,Options)={ok,NodeResults}|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%%
-%% As add_node/1,/2 but for the nodes specified in Nodes.
-%% It is possible but not intended to use this function in a non-distributed
-%% system. By speicifying node() as the node where the runtime component shall
-%% be started. The return value will then follow the rules of non distributed
-%% returnvalues and not have a node indicator.
-add_nodes(Nodes,Reference) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,any_ref},?CALL_TIMEOUT).
-
-add_nodes(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,any_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% add_nodes_if_ref(Nodes,Reference)={ok,NodeResults}|{error,Reason}
-%% add_nodes_if_ref(Nodes,Reference,Options)={ok,NodeResults}|{error,Reason}
-%%
-%% As add_nodes/2,/3 but will only connect to an already existing runtime component
-%% if its reference is the same as the one given as argument.
-add_nodes_if_ref(Nodes,Reference) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,if_ref},?CALL_TIMEOUT).
-
-add_nodes_if_ref(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,if_ref},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% change_options(Options)={ok,NodeResults}|NodeResult|{error,Reason}
-%% change_options(Nodes,Options)={ok,NodeResults}|{error,Reason}
-%% Nodes=[Node,...],
-%% Options= see add_node and add_nodes on available options.
-%%
-%% Change options on all or specified Nodes. This may result in for instance
-%% reinitialization of overloadcheck.
-change_options(Options) when is_list(Options) ->
- gen_server:call(?CONTROLLER,{change_options,all,Options},?CALL_TIMEOUT).
-change_options(Nodes,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{change_options,Nodes,Options},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% init_tracing(TracerData)={ok,[{Node,NodeResult}]} | NodeResult | {error,Reason}
-%% init_tracing(TracerList)={ok,[{Node,NodeResult}]} | {error,Reason}
-%% init_tracing(Nodes,TracerData)={ok,[{Node,NodeResult}]}|{error,Reason}
-%% TracerData = [{trace,LogTD} [,{ti,TiTD}]}] | LogTD
-%% LogTD = {HandlerFun,Data} | collector | {relayer,pid()} |
-%% {relayer,CollectingNode} | {ip,IPPortParameters} |
-%% {file,FilePortParameters}
-%% TiTD = {file,FileName} | {file,FileName,TiMFA}
-%% TiMFA = {Module,Function,ArgumentList} initiating a private loopdata
-%% inside the meta-tracer.
-%% TracerList = [{Node,TracerData}],
-%% IPPortParameters = Portno | {Portno, Qsiz}
-%% Qsiz =
-%% FilePortParameters = {Filename, wrap, Tail, {time, WrapTime}, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize} |
-%% {FileName, wrap, Tail} | FileName
-%% Tail =/= ""
-%% HandlerFun is a function taking 2 arguments.
-%% Nodes = [node()],
-%% CollectingNode = pid() | node(),
-%% NodeResult = {ok,LogResults} | {error, NReason}
-%% LogResults=[LogResult,...]
-%% LogResult={trace_log,LogRes} | {ti_log,LogRes}
-%% LogRes=ok|{error,Reason}
-%%
-%% Starts tracing on the nodes specified. If just providing a TracerData tracing
-%% will be initiated on all our nodes. If it is the non distributed case, that
-%% means only on the local non distributed node.
-%%
-%% {HandlerFun,Data}
-%% Will use the runtime components own process as tracer and handle all
-%% incomming trace message using HandlerFun.
-%% {relayer,CollectingNode}
-%% The runtime component addressed will act tracer and relay all incomming trace
-%% messages to Node or Pid, if CollectingNode is not a traced node connected
-%% to the controll component, the init_tracing call will return an error.
-%% Note that {relayer, Node} only is syntactical sugar for
-%% {relayer, rpc:call(Node,erlang,whereis,[inviso_rt])}
-%% collector
-%% The runtime component is used as tracer or collector of relayed
-%% trace messages using the default handler writing them to io.
-%% ip | file - will open a trace-port on Node using PortParameters
-init_tracing(TracerDataList) ->
- gen_server:call(?CONTROLLER,{init_tracing,TracerDataList},?CALL_TIMEOUT).
-
-init_tracing(Nodes,TracerData) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{init_tracing,Nodes,TracerData},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_tracing(Nodes)={ok,NodeResults}|{error,Reason}
-%% stop_tracing()={ok,NodeResults}|NodeResult
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,State}|{error,Reason}
-%% State=new|idle
-%% Stops tracing on all or specified Nodes. Flushes trace buffert,
-%% closes trace port and removes all trace flags and meta-patterns.
-%% The nodes are called in parallel.
-stop_tracing() ->
- gen_server:call(?CONTROLLER,{stop_tracing,all},?CALL_TIMEOUT).
-
-stop_tracing(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{stop_tracing,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% clear()={ok,NodeResults}|NodeResult
-%% clear(Nodes,Options)={ok,NodeResults}|{error,Reason}
-%% clear(Options)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% Options=[Option,...],
-%% Option=keep_trace_patterns|keep_log_files
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{new,Status}}|{error,Reason}
-%% Status=running|{suspended,SReason}
-%%
-%% Stops all tracing including removing meta-trace patterns. If the node is tracing
-%% or idle, logs belonging to the current tracerdata are removed. Hence the node
-%% is returned to state 'new'. Note that node can still be suspended.
-clear() ->
- gen_server:call(?CONTROLLER,{clear,all,[]},?CALL_TIMEOUT).
-
-clear(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{clear,Nodes,[]},?CALL_TIMEOUT).
-
-clear(Nodes,Options) when is_list(Nodes),is_list(Options) ->
- gen_server:call(?CONTROLLER,{clear,Nodes,Options},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult
-%% flush(Nodes)={ok,NodeResults}
-%% Nodes=[Node,...]
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Sends a flush request to the trace-port driver on the nodes in Nodes.
-%% There will be an error for nodes that are not tracing. It is not an error to
-%% try to flush runtime components not using a trace-port.
-flush() ->
- gen_server:call(?CONTROLLER,{flush,all},?CALL_TIMEOUT).
-flush(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop()=shutdown
-%%
-%% Stops the controll component. Runtime components are left as is. They will
-%% behave according to their dependency values.
-stop() ->
- case catch gen_server:call(?CONTROLLER,stop,?CALL_TIMEOUT) of
- shutdown ->
- shutdown;
- {'EXIT',{noproc,_}} ->
- shutdown;
- {'EXIT',Reason} ->
- exit(Reason)
- end.
-%% -----------------------------------------------------------------------------
-
-%% stop_nodes()={ok,NodeResults}|NodeResult
-%% stop_nodes(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Stops runtime component on Nodes. stop_nodes/0 will if the control component
-%% is running on a distributed node stop all runtime components. And if running
-%% on a non distributed node, stop the local and only runtime component.
-stop_nodes() ->
- gen_server:call(?CONTROLLER,{stop_nodes,all},?CALL_TIMEOUT).
-stop_nodes(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{stop_nodes,Nodes},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% stop_all()={ok,NodeResults}|NodeResult
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% A combination of stop/0 and stop_nodes/0.
-stop_all() ->
- gen_server:call(?CONTROLLER, stop_all,?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tp(Nodes,Module,Function,Arity,MatchSpec,Opts)={ok,NodeResults}|{error,Reason}
-%% tp(Nodes,Module,Function,Arity,MatchSpec)={ok,NodeResults}|{error,Reason}
-%% tp(Module,Function,Arity,MatchSpec)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tp(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% tp(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...]
-%% Module,Function=atom() | '_'
-%% Arity=integer() | '_'
-%% MatchSpec=true|false|[]| matchspec()
-%% PatternList=[Pattern,...],
-%% Pattern={Module,Function,Arity,MatchSpec,Opts},
-%% Opts=[Opt,...]
-%% Opt='only_loaded'; means that the runtime component shall not try to load
-%% a module should it not already be present in the runtime system.
-%% NodeResults=[NodeResult,...]
-%% NodeResult={ok,[Ans]}|{error,Reason},
-%% Ans=integer()|{error,Reason}
-%%
-%% Set trace pattern (global) on specified or all Nodes. The integer replied
-%% if the call was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-tp(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-
-tp(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[global]);
-tp(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-
-tp(Module,Function,Arity,MatchSpec) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,[]}],[global]).
-
-tp(Nodes,PatternList) ->
- trace_pattern(Nodes,PatternList,[global]).
-
-tp(PatternList) ->
- trace_pattern(all,PatternList,[global]).
-%% -----------------------------------------------------------------------------
-
-%% tpl(Nodes,Module,Function,Arity,MatchSpec)={ok,NodeResults}|{error,Reason}
-%% tpl(Module,Function,Arity,MatchSpec)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpl(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% tpl(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% see tp/X for description.
-%%
-%% Set trace pattern (local) on specified or all Nodes. The integer replied
-%% if the command was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-tpl(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-
-tpl(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) ->
- trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[local]);
-tpl(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-
-tpl(Module,Function,Arity,MatchSpec) ->
- trace_pattern(all,[{Module,Function,Arity,MatchSpec,[]}],[local]).
-
-tpl(Nodes, PatternList) ->
- trace_pattern(Nodes,PatternList,[local]).
-
-tpl(PatternList) ->
- trace_pattern(all,PatternList,[local]).
-%% -----------------------------------------------------------------------------
-
-%% ctp(Nodes,Module,Function,Arity)={ok,NodeResults}|{error,Reason}
-%% ctp(Module,Function,Arity)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctp(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% ctp(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% PatternList=[{Mod,Func,Arity},...]
-%% see tp/X for other argument descriptions.
-%%
-%% Clear trace pattern (global) on specified or all Nodes. The integer replied
-%% if the call was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-ctp(Nodes,Module,Function,Arity) ->
- trace_pattern(Nodes,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-
-ctp(Module,Function,Arity) ->
- trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-
-ctp(Nodes,PatternList) when is_list(PatternList) ->
- trace_pattern(Nodes,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [global]).
-
-ctp(PatternList) when is_list(PatternList) ->
- trace_pattern(all,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [global]).
-%% -----------------------------------------------------------------------------
-
-%% ctpl(Nodes,Module,Function,Arity)={ok,NodeResults}|{error,Reason}
-%% ctpl(Module,Function,Arity)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpl(Nodes,PatternList)={ok,NodeResults}|{error,Reason}
-%% ctpl(PatternList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% see ctp/X for argument description.
-%%
-%% Clear trace pattern (local) on specified or all Nodes. The integer replied
-%% if the call was successfully describes the matched number of functions.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-ctpl(Nodes,Module,Function,Arity) ->
- trace_pattern(Nodes,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-
-ctpl(Module,Function,Arity) ->
- trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-
-ctpl(Nodes,PatternList) when is_list(PatternList) ->
- trace_pattern(Nodes,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [local]).
-
-ctpl(PatternList) when is_list(PatternList) ->
- trace_pattern(all,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [local]).
-%% -----------------------------------------------------------------------------
-
-%% Help function doing the control component calling for all tp/X, tpl/X, ctp/X
-%% and ctpl/X functions.
-trace_pattern(Nodes,Patterns,FlagList) ->
- gen_server:call(?CONTROLLER, {trace_pattern, Nodes, Patterns, FlagList},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-%% -----------------------------------------------------------------------------
-
-%% tf(Nodes,PidSpec,FlagList)={ok,NodeResults}|{error,Reason}
-%% tf(PidSpec,FlagList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tf(Nodes,TraceConfList)={ok,NodeResults}|{error,Reason}
-%% tf(NodeTraceConfList)={ok,NodeResults}|{error,Reason}
-%% tf(TraceConfList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeTraceConfList=[{Node,TraceConfList}]
-%% TraceConfList=[{PidSpec,FlagList},...],
-%% FlagList=[Flags],
-%% PidSpec=all|new|existing|pid()|locally_registered_name()
-%% Flags= all process trace flags allowed.
-%% NodeResult={ok,[Ans]}|{error,Reason},
-%% Ans=integer() | {error,Reason}
-%%
-%% Set process trace flags on processes on all or specified Nodes. The integer
-%% return if the call was successfully describes the matched number of processes.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% There are many combinations which does not make musch scense. For instance
-%% specifying a certain pid at all nodes. Or an empty TraceConfList for all
-%% nodes.
-%% When calling several nodes, the nodes are called in parallel.
-tf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) ->
- trace_flags(Nodes,[{PidSpec, FlagList}],true).
-
-tf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) ->
- trace_flags(Nodes,TraceConfList,true);
-tf(PidSpec,FlagList) when is_list(FlagList) ->
- trace_flags(all,[{PidSpec,FlagList}],true).
-
-tf(ArgList) when is_list(ArgList) -> % This one has triple functionality!
- case ArgList of
- [{_Process,Flags}|_] when is_list(Flags),is_atom(hd(Flags))-> % A call to all nodes.
- trace_flags(all,ArgList,true);
- [{_Node,TraceConfList}|_] when is_list(TraceConfList),is_tuple(hd(TraceConfList)) ->
- trace_flags(ArgList,true);
- [{_Node,_TraceConfList,_How}|_] ->
- trace_flags(ArgList);
- [] -> % Stupid but allowed.
- trace_flags(all,ArgList,true) % Actually doesn't matter which we choose.
- end.
-%% -----------------------------------------------------------------------------
-
-%% ctf(Nodes,PidSpec,FlagList)={ok,NodeResults}|{error,Reason}
-%% ctf(PidSpec,FlagList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctf(Nodes,TraceConfList)={ok,NodeResults}|{error,Reason}
-%% ctf(TraceConfList)={ok,NodeResults}|NodeResult|{error,Reason}
-%% see tf/X for arguments.
-%%
-%% Clear process trace flags on all or specified Nodes. The integer replied
-%% if the command was successfully describes the matched number of processes.
-%% The functions without a Nodes argument means all nodes, in a non-distributed
-%% environment it means the local node.
-%% When calling several nodes, the nodes are called in parallel.
-ctf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) ->
- trace_flags(Nodes,[{PidSpec,FlagList}],false).
-
-ctf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) ->
- trace_flags(Nodes,TraceConfList,false);
-ctf(PidSpec,FlagList) when is_list(FlagList) ->
- trace_flags(all,[{PidSpec,FlagList}],false).
-
-ctf(TraceConfList) when is_list(TraceConfList) ->
- trace_flags(all,TraceConfList,false).
-%% -----------------------------------------------------------------------------
-
-%% ctf_all(Nodes)={ok,NodeResults}|{error,Reason}
-%% ctf_all()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%%
-%% Clears all trace flags on all or specified nodes. Just for convenience.
-ctf_all() ->
- gen_server:call(?CONTROLLER,{trace_flags,all,[{all,[all]}],false},?CALL_TIMEOUT).
-
-ctf_all(Nodes) ->
- gen_server:call(?CONTROLLER,{trace_flags,Nodes,[{all,[all]}],false},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% Help function to tf/X and ctf/X making the call to the control component.
-trace_flags(Nodes, TraceConfList, How) ->
- gen_server:call(?CONTROLLER, {trace_flags, Nodes, TraceConfList, How},?CALL_TIMEOUT).
-
-trace_flags(NodeTraceConfList,How) ->
- gen_server:call(?CONTROLLER,{trace_flags,NodeTraceConfList,How},?CALL_TIMEOUT).
-
-trace_flags(NodeTraceConfListHow) ->
- gen_server:call(?CONTROLLER,{trace_flags,NodeTraceConfListHow},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-%% -----------------------------------------------------------------------------
-
-%% tpm_localnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm_localnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,N}|{error,Reason}, Note that N can only be 0 or 1.
-%%
-%% Quick version for setting meta-trace patterns on erlang:register/2. It uses
-%% a default CallFunc and ReturnFunc in the meta-tracer server.
-%% The main purpose of this function is to create ti-log entries for printing
-%% the aliases for process instead of their process identities.
-tpm_localnames() ->
- tpm_localnames(all).
-
-tpm_localnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{local_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tpm_globalnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm_globalnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={SubResult,SubResult}
-%% SubResult={ok,N}|{error,Reason}, Note that N can only be 0 or 1.
-%% As tpm_locanames/0,/1 but for registering names with global. Note that this
-%% actually involves setting meta trace patterns on two functions in global.
-tpm_globalnames() ->
- tpm_globalnames(all).
-
-tpm_globalnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{global_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% init_tpm(Mod,Func,Arity,CallFunc)={ok,NodeResults}|NodeResult|{error,Reason}
-%% init_tpm(Nodes,Mod,Func,Arity,CallFunc)={ok,NodeResults}|{error,Reason}
-%% init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|NodeResult|{error,Reason}
-%% init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|{error,Reason}
-%%
-%% Mod,Func=Pointing out the function which shall be meta traced, atom().
-%% Arity=As above, integer().
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% InitFunc,RemoveFunc={Module,Function}|fun(), functions being called when
-%% to initialize the public loopdata structure, and to reset it.
-%% InitFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD,Output}
-%% Supposed to initialize whatever needs to be done before
-%% handling any incoming meta-trace message for the Mod:Func/Arity.
-%% RemoveFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD}
-%% Called when meta tracing of Mod:Func/Arity is stopped. It is supposed
-%% to clear datastructures away from the PublLD.
-%% Initializes the public loopdata for this function. Note that we can not use wildcards
-%% here (even if it is perfectly legal in Erlang). It also sets the CallFunc and
-%% ReturnFunc for the meta traced function. The function is hence ready to be
-%% meta traced with either tpm/5 or tpm_ms/5.
-%% When calling several nodes, the nodes are called in parallel.
-init_tpm(Mod,Func,Arity,CallFunc) ->
- init_tpm(all,Mod,Func,Arity,CallFunc).
-
-init_tpm(Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {init_tpm,
- [Mod,Func,Arity,CallFunc]}},
- ?CALL_TIMEOUT).
-
-init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- init_tpm(all,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc).
-
-init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tpm(Mod,Func,Arity,MS)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm(Nodes,Mod,Func,Arity,MS)={ok,NodeResults}|{error,Reason}
-%% tpm(Mod,Func,Arity,MS,CallFunc)={ok,NodeResults}|NodeResults|{error,Reason}
-%% tpm(Nodes,Mod,Func,Arity,MS,CallFunc)={ok,NodeResults}|{error,Reason}
-%% tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|NodeResults|{error,Reason}
-%% tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% {ok,NodeResults}|{error,Reason}
-%%
-%% Mod,Func=atom() and not '_'.
-%% Arity=integer()
-%% MS=list(), matchspecification.
-%% Nodes=List of nodenames.
-%% InitFunc,CallFunc,ReturnFunc,RemoveFunc={Module,Function}|fun(),
-%% functions being called when these functions are called by the meta trace
-%% server at certain events.
-%% CallFunc(CallingPid,ActualArgList,PublLD)->{ok,NewPrivLD,Output}
-%% ReturnFunc(CallingPid,ReturnValue,PublLD)->{ok,NewPrivLD,Output}
-%% When a call respectively return_from trace message arrives for the meta
-%% traced function, the corresponding function is called.
-%% The ReturnFunc must handle the fact that a return_from message arrives
-%% for a call which was never noticed. This because the message queue of the
-%% meta tracer may have been emptied.
-%%
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,N}|{error,Reason}, Note that N can only be 0 or 1.
-%%
-%% Activates meta-tracing in the inviso_rt_meta tracer. Except when using tpm/6,/8
-%% and /9 the function must first have been initiated using init_tpm. If running
-%% a non distributed system the variants without Node shall be used. If running
-%% in a distributed environment, without Node means all our nodes.
-%% When calling several nodes, the nodes are called in parallel.
-tpm(Mod,Func,Arity,MS) ->
- tpm(all,Mod,Func,Arity,MS).
-
-tpm(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS]}});
-tpm(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) ->
- tpm(all,Mod,Func,Arity,MS,CallFunc).
-
-tpm(Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS,CallFunc]}}).
-
-tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- tpm(all,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc).
-
-tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% Same as tpm/X but the meta tracer will append {tracer,Tracer} to any enable
-%% list in a trace body action term.
-tpm_tracer(Mod,Func,Arity,MS) ->
- tpm_tracer(all,Mod,Func,Arity,MS).
-
-tpm_tracer(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS]}},
- ?CALL_TIMEOUT);
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) ->
- tpm_tracer(all,Mod,Func,Arity,MS,CallFunc).
-
-tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS,CallFunc]}}).
-
-tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- tpm_tracer(all,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc).
-
-tpm_tracer(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,
- Nodes,
- {tpm_tracer,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% tpm_ms(Mod,Func,Arity,MSname,MS)={ok,NodeResults}|NodeResult|{error,Reason}
-%% tpm_ms(Nodes,Mod,Func,Arity,MSname,MS)={ok,NodeResults}|{error,Reason}
-%% Nodes= List of all nodes where the function shall be carried out.
-%% Mod,Func=Pointing out the function to which we shall add a match-spec., atom().
-%% Arity=As above, integer().
-%% MSname=A name to be used if this MS shall be removed later. term().
-%% MatchSpec=List of match specification, Remember {return_trace}
-%% if expecting return_from messages.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,1}|{ok,0}|{error,Reason} where {ok,1} indicates that
-%% setting the matchspecification for the function succeeded.
-%%
-%% This function adds a list of match-specs to the already existing ones. It
-%% uses an internal database to keep track of existing match-specs. If the
-%% match-spec does not result in any meta traced functions (for whatever reason),
-%% the MS is not saved in the database. The previously known match-specs are
-%% not removed.
-%% The function must previously have been initiated in order for this function
-%% to add a match-spec.
-%% When calling several nodes, the nodes are called in parallel.
-tpm_ms(Mod,Func,Arity,MSname,MS) ->
- tpm_ms(all,Mod,Func,Arity,MSname,MS).
-
-tpm_ms(Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_ms,[Mod,Func,Arity,MSname,MS]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% Same as tpm_ms/5, /6 but the meta tracer will append {tracer,Tracer} to any enable
-%% list in a trace body action term.
-tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->
- tpm_ms_tracer(all,Mod,Func,Arity,MSname,MS).
-
-tpm_ms_tracer(Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{tpm_ms_tracer,[Mod,Func,Arity,MSname,MS]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm_ms(Mod,Func,Arity,MSname)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm_ms(Nodes,Mod,Func,Arity,MSname)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Removes a named match-spec from the meta traced function. Note that it never
-%% is a fault to remove an MS. Not even from a function which is non existant.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm_ms(Mod,Func,Arity,MSname) ->
- ctpm_ms(all,Mod,Func,Arity,MSname).
-
-ctpm_ms(Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{ctpm_ms,[Mod,Func,Arity,MSname]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm(Mod,Func,Arity)={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm(Node,Mod,Func,Arity)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Removes the meta trace pattern for the function, means stops generating output
-%% for this function. The public LD may be cleared by the previously entered
-%% RemoveFunc.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm(Mod,Func,Arity) ->
- ctpm(all,Mod,Func,Arity).
-
-ctpm(Nodes,Mod,Func,Arity) ->
- gen_server:call(?CONTROLLER,
- {meta_pattern,Nodes,{ctpm,[Mod,Func,Arity]}},
- ?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm_localnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm_localnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason}
-%%
-%% Removes meta-trace pattern for erlang:register/2, previously set by tpm_localnames.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm_localnames() ->
- ctpm_localnames(all).
-
-ctpm_localnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{remove_local_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctpm_localnames()={ok,NodeResults}|NodeResult|{error,Reason}
-%% ctpm_localnames(Nodes)={ok,NodeResults}|{error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={SubResult,Subresult}
-%% SubResult=ok|{error,Reason}
-%%
-%% Removes meta-trace pattern for the register functions in global. Note that there
-%% are two of them.
-%% When calling several nodes, the nodes are called in parallel.
-ctpm_globalnames() ->
- ctpm_globalnames(all).
-
-ctpm_globalnames(Nodes) ->
- gen_server:call(?CONTROLLER,{meta_pattern,Nodes,{remove_global_register,[]}},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% ctp_all(Nodes)={ok,NodeResults}|{error,Reason}
-%% ctp_all()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%%
-%% Clears all both global and local trace patterns on all or specified nodes.
-%% Does not effect meta patterns.
-ctp_all() ->
- gen_server:call(?CONTROLLER,{ctp_all,all},?CALL_TIMEOUT).
-
-ctp_all(Nodes) ->
- gen_server:call(?CONTROLLER,{ctp_all,Nodes},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% suspend(Nodes,Reason)={ok,NodeResults}|{error,Reason}
-%% suspend(Reason)={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node],
-%% Reason=term(); supposed to describe the reason why suspended.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%%
-%% Suspend all or specified Nodes with reason Reason. Suspend means that all
-%% process trace flags are removed and all meta-patterns.
-suspend(Nodes, Reason) ->
- gen_server:call(?CONTROLLER,{suspend,Reason,Nodes},?CALL_TIMEOUT).
-
-suspend(Reason) ->
- gen_server:call(?CONTROLLER,{suspend,Reason,all},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% cancel_suspension(Nodes)={ok,NodeResults}|{error,Reason}
-%% cancel_suspension()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok|{error,Reason},
-%% Cancel suspension on all or specified Nodes. Note that this does not imply
-%% that "business" is resumed as before. You must reactivate flags and meta-patter
-%% your self.
-cancel_suspension(Nodes) ->
- gen_server:call(?CONTROLLER,{cancel_suspension,Nodes},?CALL_TIMEOUT).
-
-cancel_suspension() ->
- gen_server:call(?CONTROLLER,{cancel_suspension,all},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% get_status(Nodes)={ok,NodeResults}|{error,Reason}
-%% get_status()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}}|{error,Reason},
-%% State=new|idle|tracing
-%% Status=running|{suspended,SReason}
-%%
-%% Get Status form all or specified runtime components.
-get_status(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{get_status,Nodes},?CALL_TIMEOUT).
-
-get_status() ->
- gen_server:call(?CONTROLLER,{get_status,all},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% get_tracerdata()={ok,NodeResults}|NodeResult|{error,Reason}
-%% get_tracerdata(Nodes)={ok,NodeResults}|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeResult={ok,NResult}|{error,Reason},
-%% NResult=TracerData|no_tracerdata
-%% TracerData will be exactly as it was specified when doing init_tracing.
-%%
-%% Get TracerData form all or specified runtime components.
-get_tracerdata() ->
- gen_server:call(?CONTROLLER,{get_tracerdata,all},?CALL_TIMEOUT).
-
-get_tracerdata(Nodes) when is_list(Nodes) ->
- gen_server:call(?CONTROLLER,{get_tracerdata,Nodes},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% list_logs(TracerData)={ok,NodeResults}|NodeResult|{error,Reason}
-%% list_logs(NodeList)={ok,NodeResults}|{error,Reason}
-%% list_logs()={ok,NodeResults}|NodeResult|{error,Reason}
-%% TracerData see init_tracing/1/2
-%% NodeList=[NodeSpec,...]
-%% NodeSpec=Node|{Node,TracerData}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,FileList}|{ok,no_log}|{error,Reason}
-%% FileList=[FileType,...], one or more of different types.
-%% FileType={trace_log,Dir,Files}|{ti_log,Dir,Files}
-%% Files=[FileNameWithOutPath,...]
-%%
-%% Ask local or specified runtime components for now existing logs given
-%% TracerData. If TracerData is left out, the runtime components TracerData,
-%% if existing, will be used instead.
-list_logs() ->
- gen_server:call(?CONTROLLER,list_logs,?CALL_TIMEOUT).
-
-list_logs(TracerDataOrNodesList) when is_list(TracerDataOrNodesList) ->
- gen_server:call(?CONTROLLER,{list_logs,TracerDataOrNodesList},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-%% fetch_log(LogSpecList,DestDir,Prefix)={ok,NodeResults}|{error,not_distributed}|
-%% {error,Reason}
-%% fetch_log(DestDir,Prefix)={ok,NodeResults}|{error,not_distributed}|
-%% {error,Reason}
-%% fetch_log(ToNode,DestDir,Prefix)=
-%% fetch_log(ToNode,LogSpecList,DestDir,Prefix)=
-%% DestDir=string(), to where the fetched files shall be placed.
-%% Prefix=string(), prefix on locally saved fetched files.
-%% LogSpecList=[LogSpec,...],
-%% LogSpec={Node,FileSpecList}|Node|{Node,TracerData}
-%% TracerData=see init_tracing/1/2
-%% FileSpecList=[{trace_log,Dir,FileList},{ti_log,Dir,FileList}]
-%% where each tuple-item is optional.
-%% FileList=[RemoteFileName,...]
-%% ToNode=atom()
-%% NodeResult={Conclusion,ResultFileSpec}|no_log|{error,NReason}
-%% Conclusion=complete|incomplete
-%% ResultFileSpec=[{trace_log,FileResults},{ti_log,FileResults}]
-%% FileResults=[FileResult,...]
-%% FileResult={ok,FileName}|{error,FReason}
-%% NReason=own_node|Reason
-%% FReason = {file_open,{posix(),FileName}} |
-%% {file_open,{posix(),RemoteFileName}}
-%% {file_open,{posix(),[DestDir,Prefix,RemoteFileName]}}
-%% {file_write,{posix(),FileName}} |
-%% {truncated,FileName}
-%% {truncated,{Reason,FileName}}
-%% posix() - an atom which is named from the Posix error codes used in
-%% Unix, and in the runtime libraries of most C compilers.
-%% See module file in Kernel Reference manual.
-%%
-%% Copies logfiles over distributed erlang to ToNode. This
-%% function can only be used in a distributed system.
-%% The resulting transfered files will have the prefix Prefix and will be
-%% located in DestDir.
-%% Note that the client process using this function will wait until all files
-%% are moved. The job can be cancelled, causing any already copied files to be
-%% removed, by simply terminating the waiting client process.
-fetch_log(DestDir,Prefix) when is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,node(),all,DestDir,Prefix},infinity).
-
-fetch_log(ToNode,DestDir,Prefix) when is_atom(ToNode),is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,ToNode,all,DestDir,Prefix},infinity);
-
-fetch_log(LogSpecList,DestDir,Prefix) when is_list(LogSpecList),is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,node(),LogSpecList,DestDir,Prefix},infinity).
-
-fetch_log(ToNode,LogSpecList,DestDir,Prefix)
- when is_atom(ToNode),is_list(LogSpecList),is_list(DestDir),is_list(Prefix) ->
- gen_server:call(?CONTROLLER,{fetch_log,ToNode,LogSpecList,DestDir,Prefix},infinity).
-%% ------------------------------------------------------------------------------
-
-%% delete_log(Nodes,TracerData)={ok,NodeResults}|{error,Reason}
-%% delete_log(NodeSpecList)={ok,NodeResults}|{error,Reason}
-%% delete_log(Spec)={ok,NodeResults}|NodeResult|{error,Reason}
-%% delete_log(TracerData)={ok,NodeResults}|NodeResult|{error,Reason}
-%% delete_log()={ok,NodeResults}|NodeResult|{error,Reason}
-%% Nodes=[Node,...],
-%% NodeSpecList=[{Node,Spec},...]
-%% Spec=[AbsPathFileName,...]|LogSpecs
-%% LogSpecs=[LogSpec,...]
-%% LogSpec={trace_log,Dir,[FileNameWithoutPath,...]}|
-%% {ti_log,Dir,[FileNameWithoutPath,...]}
-%% TracerData = see init_tracing/1/2
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,no_log}|{ok,LogInfos}|{ok,FileInfos}
-%% LogInfos=[LogInfo,...]
-%% LogInfo={trace_log,FileInfos}|{ti_log,FileInfos}
-%% FileInfos=[FileInfo,...]
-%% FileInfo={ok,FileName}|{error,Reason} whether FileName contains
-%% full path or not depends on if AbsPathFileName or LogSpec was
-%% used when specifying the files.
-%%
-%% Deletes listed files or files corresponding to TracerData from specified
-%% or all Nodes. If no TracerData or list of files is specified in the call the
-%% TracerData at Node will be used to identify log files to delete.
-delete_log() ->
- gen_server:call(?CONTROLLER,{delete_log,all},?CALL_TIMEOUT).
-delete_log(What) ->
- gen_server:call(?CONTROLLER,{delete_log,What},?CALL_TIMEOUT).
-delete_log(Nodes,Spec) ->
- gen_server:call(?CONTROLLER,{delete_log,Nodes,Spec},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% subscribe()= same as subscribe(self())
-%% subscribe(Pid)=ok|{error,Reason}
-%% Pid=pid(),
-%% Reason =
-%% Add Pid or self() to event sending list. Note that it is possible to add a
-%% pid several times and that the Pid then will receive several event messages.
-%% All events will be sent to all subscribers in the event sending list.
-%% Event={inviso_event,ControllerPid,erlang:localtime(),Msg},
-%% Msg=
-%% {connected, Node, {Tag, {State, Status}}}
-%% {disconnected, Node, not_applicable}
-%% {state_change, Node, {State, Status}}
-%% {port_down, Node, Reason}
-%% Node = node() | local_runtime (when running in a non-distributed
-%% environment)
-subscribe() ->
- gen_server:call(?CONTROLLER,{subscribe,self()},?CALL_TIMEOUT).
-
-subscribe(Pid) ->
- gen_server:call(?CONTROLLER,{subscribe,Pid},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% unsubscribe()= same as unsubscribe(self())
-%% unsubscribe(Pid)=ok
-%% Pid=pid(),
-%%
-%% Remove, if present, first occurrence of Pid or self() from event sending
-%% list. Note that it is not an error to remove a non existing subscription.
-unsubscribe() ->
- gen_server:call(?CONTROLLER,{unsubscribe,self()},?CALL_TIMEOUT).
-
-unsubscribe(Pid) ->
- gen_server:call(?CONTROLLER,{unsubscribe,Pid},?CALL_TIMEOUT).
-%% ------------------------------------------------------------------------------
-
-
-
-%% debuging the controller
-%% ----------------------------------------------------------------------------
-state() ->
- ?CONTROLLER ! state.
-
-%% debuging the runtime component
-state(Node) ->
- ?CONTROLLER ! {state, Node}.
-
-%%% end of file
diff --git a/lib/inviso/src/inviso_c.erl b/lib/inviso/src/inviso_c.erl
deleted file mode 100644
index b1597a7f35..0000000000
--- a/lib/inviso/src/inviso_c.erl
+++ /dev/null
@@ -1,1335 +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$
-%%
-%% Author: Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%%
-%% Description: The controlling part of the trace tool Inviso
-%%
-%% This code implements the inviso control component meant to be run on an Erlang
-%% node doing any tracing. It can also be used in a non distributed system where
-%% the control component and the runtime component will run on the same virtual
-%% machine.
-%% The control component is not meant to be started by a supervisor but rather
-%% directly by the user when needed.
-%% This module does not provide any APIs to users. Those are found in inviso.erl.
-%% This module merly has the gen_server call-backs.
-%% ------------------------------------------------------------------------------
-
--module(inviso_c).
--behavior(gen_server).
-
-
-%% ------------------------------------------------------------------------------
-%% gen_server callbacks.
-%% ------------------------------------------------------------------------------
--export([init/1,
- handle_call/3,handle_cast/2,handle_info/2,
- terminate/2,
- code_change/3]).
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Exported internal functions (used in spawn of help process).
-%% ------------------------------------------------------------------------------
--export([log_rec_init/4]).
-
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Records.
-%% ------------------------------------------------------------------------------
-
-%% #state
-%% Record used in the loopdata.
--record(state,{
- nodes=[], % [#node,...]
- distributed, % false | true
- subscribers=[], % [{pid(),monitor_ref()},...]
- rt_options=[{dependency,{infinity,node()}},{overload, default}]
- }).
-%% ------------------------------------------------------------------------------
-
-%% #node
-%% Record storing information about a runtime component connected to this control
-%% component.
--record(node,{
- node, % [atom(),...]
- pid, % pid()
- vsn,
- ref, % monitor_ref()
- tag % term()
- }).
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Macros used in this module.
-%% ------------------------------------------------------------------------------
-
--define(RUNTIME,inviso_rt). % The module API name of the runtime.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% Controller component implmentation.
-%% ==============================================================================
-
-init({_Parent,Options}) ->
- process_flag(trap_exit,true),
- case check_options(Options,start) of
- {ok,Options2} ->
- LoopData=initiate_state(Options2),
- {ok,LoopData};
- Error ->
- {stop,Error}
- end.
-%% ------------------------------------------------------------------------------
-
-handle_call({subscribe,Pid},_From,LD) when is_pid(Pid) ->
- MRef=erlang:monitor(process,Pid),
- {reply,ok,LD#state{subscribers=[{Pid,MRef}|LD#state.subscribers]}};
-handle_call({subscribe,Faulty},_From,LD) ->
- {reply,{error,{badarg,Faulty}},LD};
-handle_call({unsubscribe,Pid},_From,LD) ->
- case lists:keysearch(Pid,1,LD#state.subscribers) of
- {value,{_,MRef}} ->
- erlang:demonitor(MRef),
- {reply,ok,LD#state{subscribers=lists:keydelete(Pid,1,LD#state.subscribers)}};
- false ->
- {reply,ok,LD}
- end;
-handle_call({add_nodes,Nodes,Opts,Tag,Condition},_From,LD) ->
- case check_options(Opts,add_node) of
- {ok,Opts2} ->
- Opts3=merge_options(LD#state.rt_options,Opts2),
- {NewLD,Reply}=do_add_nodes(Nodes,LD,Opts3,Tag,Condition),
- {reply,adapt_reply(NewLD,Reply),NewLD};
- Error ->
- {reply,Error,LD}
- end;
-handle_call({change_options,Nodes,Opts},_From,LD) ->
- case check_options(Opts,add_node) of
- {ok,Opts2} ->
- {reply,adapt_reply(LD,do_change_option(Nodes,Opts2,LD)),LD};
- Error ->
- {reply,Error,LD}
- end;
-handle_call({init_tracing,TracerDataList},_From,LD) ->
- {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};
-handle_call({init_tracing,Nodes,TracerData},_From,LD) when is_list(Nodes) ->
- TracerDataList=
- lists:map(fun(N)->{N,TracerData} end,started_trace_nodes(Nodes,LD)),
- {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};
-handle_call({init_tracing,Nodes,_TracerData},_From,LD) ->
- {reply,{error,{badarg,Nodes}},LD};
-handle_call({trace_pattern,Nodes,Patterns,FlagList},_From,LD) ->
- {reply,adapt_reply(LD,distribute_tp(Nodes,Patterns,FlagList,LD)),LD};
-handle_call({trace_flags,Nodes,Args,How},_From,LD) ->
- {reply,adapt_reply(LD,distribute_tf(Nodes,Args,How,LD)),LD};
-handle_call({trace_flags,NodeArgs,How},_From,LD) ->
- {reply,distribute_tf(NodeArgs,How,LD),LD}; % Always distributed here.
-handle_call({trace_flags,NodeArgHows},_From,LD) ->
- {reply,distribute_tf(NodeArgHows,LD),LD}; % Always distributed here.
-handle_call({meta_pattern,Nodes,Args},_From,LD) ->
- {reply,adapt_reply(LD,distribute_metapattern(Nodes,Args,LD)),LD};
-handle_call({ctp_all,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_ctp_all(Nodes,LD)),LD};
-handle_call({suspend,Reason,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_suspend(Nodes,Reason,LD)),LD};
-handle_call({cancel_suspension,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_cancel_suspension(Nodes,LD)),LD};
-handle_call({stop_tracing,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_stop_tracing(Nodes,LD)),LD};
-handle_call({get_status,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_get_status(Nodes,LD)),LD};
-handle_call({get_tracerdata,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_get_tracerdata(Nodes,LD)),LD};
-handle_call(list_logs,_From,LD) ->
- {reply,adapt_reply(LD,do_list_logs(all,LD)),LD};
-handle_call({list_logs,TracerDataOrNodesList},_From,LD) ->
- {reply,adapt_reply(LD,do_list_logs(TracerDataOrNodesList,LD)),LD};
-handle_call({fetch_log,ToNode,Spec,Dest,Prefix},From,LD) ->
- case LD#state.distributed of
- true -> % It is a distributed system.
- do_fetch_log(ToNode,Spec,Dest,Prefix,From,LD),
- {noreply,LD}; % Reply will come from collector pid.
- false -> % Stupidity! you dont want this!
- {reply,{error,not_distributed},LD}
- end;
-handle_call({delete_log,NodesOrNodeSpecs},_From,LD) ->
- {reply,adapt_reply(LD,do_delete_log(NodesOrNodeSpecs,LD)),LD};
-handle_call({delete_log,Nodes,Specs},_From,LD) when is_list(Nodes) ->
- Reply=do_delete_log(lists:map(fun(N)->{N,Specs} end,Nodes),LD),
- {reply,adapt_reply(LD,Reply),LD};
-handle_call({delete_log,FaultyNodes,_Specs},_From,LD) ->
- {reply,{error,{badarg,FaultyNodes}},LD};
-handle_call({clear,Nodes,Options},_From,LD) ->
- {reply,adapt_reply(LD,do_clear(Nodes,LD,Options)),LD};
-handle_call({flush,Nodes},_From,LD) ->
- {reply,adapt_reply(LD,do_flush(Nodes,LD)),LD};
-handle_call({stop_nodes,Nodes},_From,LD) ->
- {NewLD,Reply}=do_stop_nodes(Nodes,LD),
- {reply,adapt_reply(NewLD,Reply),NewLD};
-handle_call(stop,_From,LD) ->
- {stop,normal,shutdown,LD};
-handle_call(stop_all,_From,LD) ->
- {NewLD,Reply}=do_stop_nodes(started_trace_nodes(all,LD),LD),
- {stop,normal,adapt_reply(NewLD,Reply),NewLD};
-handle_call(Request,_From,LD) -> %% for debug purpose only
- {reply,{error,{invalid_request,Request}},LD}.
-
-handle_cast(_Request,LD) -> % There are no casts.
- {noreply,LD}.
-
-handle_info({connect,_Node,Pid,_VSN,Tag},LD) -> % From connecting runtime.
- {noreply,do_confirm_connection(Pid,Tag,LD)};
-handle_info({trace_event,Event},LD) -> % A runtime component issues an event.
- send_to_subscribers(Event,LD), % Relay to our subscribers.
- {noreply,LD};
-handle_info({'DOWN',Ref,process,_From,Info},LD) -> % A runtime component died?
- {noreply,do_down_msg(Ref,Info,LD)};
-handle_info(state,LD) -> % For debug purposes.
- io:format("trace_c state: ~p~n",[LD]),
- {noreply,LD};
-handle_info(_Msg,LD) ->
- {noreply,LD}.
-
-terminate(_Reason, _LD) ->
- ok.
-
-code_change(_OldVsn, LoopData, _Extra) ->
- {ok, LoopData}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Handle call help functions.
-%% -----------------------------------------------------------------------------
-
-%% Help function which adapts a reply based on if this is a distributed system
-%% or not. The first argument indicates distribution (='true').
-%% If we are not running a distributed system, all node references are removed.
-adapt_reply(#state{distributed=Distributed},Reply) ->
- adapt_reply(Distributed,Reply);
-adapt_reply(true,Reply) -> % We are distributed.
- Reply;
-adapt_reply(false,{ok,[{_Node,LocalReply}]}) ->
- LocalReply;
-adapt_reply(false,{ok,[]}) ->
- {error,not_an_added_node}; % �R DET H�R VERKLIGEN RIKTIGT?
-adapt_reply(false,Reply) ->
- Reply.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% First level help functions to handler functions.
-%% ==============================================================================
-
-%% Function starting a runtime component at the nodes in Nodes. If doing non
-%% distributed, Nodes will be a list of nonode@nohost.
-%% Returns {NewLD,{ok,Replies}} or {LD,{error,Reason}}.
-do_add_nodes(Nodes,LD,Options,Tag,Condition) ->
- do_add_nodes_2(Nodes,LD,Options,Tag,Condition,[]).
-
-do_add_nodes_2([Node|Tail],LD,Options,Tag,Condition,Replies) ->
- case find(fun is_started/2,LD#state.nodes,Node) of
- {value,true} -> % Already started by us.
- do_add_nodes_2(Tail,LD,Options,Tag,Condition,[{Node,{ok,already_added}}|Replies]);
- no_match ->
- case ?RUNTIME:start(Node,Options,Tag,Condition) of
- {node_info,_Node,Pid,VSN,State,Status,new} ->
- NewLD=do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status),
- do_add_nodes_2(Tail,NewLD,Options,Tag,Condition,[{Node,{ok,new}}|Replies]);
- {node_info,_Node,Pid,VSN,State,Status,{tag,Tag2}} ->
- NewLD=do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status),
- do_add_nodes_2(Tail,NewLD,Options,Tag,Condition,
- [{Node,{ok,{adopted,State,Status,Tag2}}}|Replies]);
- Error ->
- do_add_nodes_2(Tail,LD,Options,Tag,Condition,[{Node,Error}|Replies])
- end
- end;
-do_add_nodes_2([],LD,_,_,_,Replies) ->
- {LD,{ok,lists:reverse(Replies)}};
-do_add_nodes_2(Faulty,LD,_,_,_,_) -> % Not a list of nodes.
- {LD,{error,{badarg,Faulty}}}.
-
-do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status) ->
- MRef=erlang:monitor(process,Pid),
- NodeRec=#node{node=Node,pid=Pid,vsn=VSN,ref=MRef,tag=Tag},
- send_to_subscribers({connected,Node,{Tag,{State,Status}}},LD),
- _NewLD=set_node_rec(NodeRec,LD).
-%% ------------------------------------------------------------------------------
-
-%% Function calling change_options sequensially on all nodes in Nodes.
-%% Returns {ok,Replies} or {error,Reason}.
-do_change_option(Nodes,Options,LD) ->
- do_change_option_2(started_trace_nodes(Nodes,LD#state.nodes),Options,LD,[]).
-
-do_change_option_2([Node|Tail],Options,LD,Replies) ->
- case get_node_rec(Node,LD) of
- Rec when is_record(Rec,node) ->
- Answer=?RUNTIME:change_options(Rec#node.pid,Options),
- do_change_option_2(Tail,Options,LD,[{Node,Answer}|Replies]);
- Error ->
- do_change_option_2(Tail,Options,LD,[{Node,Error}|Replies])
- end;
-do_change_option_2([],_Options,_LD,Replies) ->
- {ok,Replies};
-do_change_option_2(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function which calls the runtime components in TracerDataList and initiates
-%% the tracing. TracerDataList may either be just one tracer data which shall be
-%% applied to all runtime components, or a list of nodes and tracerdata.
-do_init_tracing(TracerDataList,LD) ->
- case inviso_rt_lib:is_tracerdata(TracerDataList) of
- true -> % Then we must add all our nodes.
- List=lists:map(fun(N)->{N,TracerDataList} end,started_trace_nodes(all,LD)),
- do_init_tracing_2(List,LD,[]);
- false -> % Assume it is a list of {Node,Tracerdata}
- do_init_tracing_2(TracerDataList,LD,[])
- end.
-
-do_init_tracing_2([{Node,TracerData}|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- Rec when is_record(Rec,node) ->
- case check_modify_tracerdata(TracerData,LD) of
- {ok,NewTracerData} -> % Tracerdata ok and node->pid.
- Reply=?RUNTIME:init_tracing(Rec#node.pid,NewTracerData),
- do_init_tracing_2(Tail,LD,[{Node,Reply}|Replies]);
- {error,Reason} -> % Unknown tracerdata.
- do_init_tracing_2(Tail,LD,[{Node,{error,Reason}}|Replies])
- end;
- {error,Reason} ->
- do_init_tracing_2(Tail,LD,[{Node,{error,Reason}}|Replies])
- end;
-do_init_tracing_2([_|Tail],LD,Replies) -> % Just ignore item we don't understand.
- do_init_tracing_2(Tail,LD,Replies); % Will not end up in Replies either.
-do_init_tracing_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_init_tracing_2(What,_LD,_) ->
- {error,{badarg,What}}.
-%% -----------------------------------------------------------------------------
-
-%% Function setting trace patterns on all nodes mentioned in Nodes. Uses a
-%% parallel mechanism in the runtime component.
-%% Returns {ok,Reply} or {error,Reason}.
-distribute_tp(all,Patterns,FlagList,LD) ->
- distribute_tp(started_trace_nodes(all,LD),Patterns,FlagList,LD);
-distribute_tp(Nodes,Patterns,FlagList,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
- #node{pid=Pid} ->
- {Pid,N};
- Error ->
- {Error,N}
- end
- end,
- Nodes),
- {ok,?RUNTIME:trace_patterns_parallel(RTpids,Patterns,FlagList)};
-distribute_tp(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function which in parallel sets trace flags on all nodes in Nodes. Can be
-%% either a list of node names or 'all'. Note that in the reply list there will be an
-%% error indicating nodes not reachable. Either because such a node disappeared or
-%% because it is not one of "our" nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-distribute_tf(all,Args,How,LD) ->
- distribute_tf(started_trace_nodes(all,LD),Args,How,LD);
-distribute_tf(Nodes,Args,How,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(Node)->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {Pid,Node};
- Error -> % Not an added node then!
- {Error,Node}
- end
- end,
- Nodes),
- {ok,?RUNTIME:trace_flags_parallel(RTpids,Args,How)};
-distribute_tf(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-
-%% As above but specific args for each node.
-distribute_tf(NodeArgs,How,LD) when is_list(NodeArgs) ->
- RTpidArgs=lists:map(fun({Node,Args})->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {Pid,Node,Args};
- Error -> % Not an added node then!
- {Error,Node}
- end
- end,
- NodeArgs),
- {ok,?RUNTIME:trace_flags_parallel(RTpidArgs,How)};
-distribute_tf(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-
-%% As above but both specific args for each node and How (set or remove flag).
-distribute_tf(NodeArgHows,LD) when is_list(NodeArgHows) ->
- RTpidArgHows=
- lists:map(fun({Node,Args,How}) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {Pid,Node,Args,How};
- Error -> % Not an added node then!
- {Error,Node}
- end
- end,
- NodeArgHows),
- {ok,?RUNTIME:trace_flags_parallel(RTpidArgHows)};
-distribute_tf(Faulty,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function making a parallel call to all nodes in Nodes, calling the generic
-%% meta-tracer function stated in Args.
-%% Returns {ok,Reply} or {error,Reason}.
-distribute_metapattern(all,Args,LD) ->
- distribute_metapattern(started_trace_nodes(all,LD),Args,LD);
-distribute_metapattern(Nodes,Args,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
- #node{pid=Pid} ->
- {Pid,N};
- Error ->
- {Error,N}
- end
- end,
- Nodes),
- {ok,?RUNTIME:meta_tracer_call_parallel(RTpids,Args)};
-distribute_metapattern(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function clearing all trace patterns on all node mentioned in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_ctp_all(Nodes,LD) ->
- do_ctp_all_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_ctp_all_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_ctp_all_2(Tail,LD,[{Node,?RUNTIME:clear_all_tp(Pid)}|Replies]);
- Error ->
- do_ctp_all_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_ctp_all_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_ctp_all_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function suspending all runtime components mentioned in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_suspend(Nodes,Reason,LD) ->
- do_suspend_2(started_trace_nodes(Nodes,LD),Reason,LD,[]).
-
-do_suspend_2([Node|Tail],Reason,LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- Answer=?RUNTIME:call_suspend(Pid,Reason),
- do_suspend_2(Tail,Reason,LD,[{Node,Answer}|Replies]);
- Error ->
- do_suspend_2(Tail,Reason,LD,[{Node,Error}|Replies])
- end;
-do_suspend_2([],_Reason,_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_suspend_2(Faulty,_,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function cancelling a suspension at the runtime components mentioned in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_cancel_suspension(Nodes,LD) ->
- do_cancel_suspension_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_cancel_suspension_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- Answer=?RUNTIME:cancel_suspension(Pid),
- do_cancel_suspension_2(Tail,LD,[{Node,Answer}|Replies]);
- Error ->
- do_cancel_suspension_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_cancel_suspension_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_cancel_suspension_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function which stops tracing on all nodes in Nodes. The function is performed
-%% in parallel over the nodes to get a more precise stop-time.
-%% Return {ok,Reply} or {error,Reason}.
-do_stop_tracing(all,LD) ->
- do_stop_tracing(started_trace_nodes(all,LD),LD);
-do_stop_tracing(Nodes,LD) when is_list(Nodes) ->
- RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
- #node{pid=Pid} ->
- {Pid,N};
- Error ->
- {Error,N}
- end
- end,
- Nodes),
- {ok,?RUNTIME:stop_tracing_parallel(RTpids)};
-do_stop_tracing(Faulty,_) ->
- {error,{badarg,Faulty}}.
-%% -----------------------------------------------------------------------------
-
-%% Function fetching the current status of the runtime components in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_get_status(Nodes,LD) ->
- do_get_status_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_get_status_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_get_status_2(Tail,LD,[{Node,?RUNTIME:get_status(Pid)}|Replies]);
- Error ->
- do_get_status_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_get_status_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_get_status_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function retrieving the tracerdata for the nodes in Nodes.
-%% Returns {ok,Reply} or {error,Reason}.
-do_get_tracerdata(Nodes,LD) ->
- do_get_tracerdata_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_get_tracerdata_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_get_tracerdata_2(Tail,LD,[{Node,?RUNTIME:get_tracerdata(Pid)}|Replies]);
- Error ->
- do_get_tracerdata_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_get_tracerdata_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_get_tracerdata_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Function that lists all logfiles associated with a certain tracerdata
-%% or the current tracerdata should no tracerdata be mentioned.
-%% Returns {ok,Replies} or {error,Reason}.
-do_list_logs(all,LD) -> % When doing all known nodes.
- do_list_logs_2(started_trace_nodes(all,LD),LD,[]);
-do_list_logs(TracerDataOrNodesList,LD) ->
- case inviso_rt_lib:is_tracerdata(TracerDataOrNodesList) of
- true -> % It is tracerdata for this node.
- do_list_logs_2([{node(),TracerDataOrNodesList}],LD,[]);
- false ->
- do_list_logs_2(TracerDataOrNodesList,LD,[])
- end.
-
-do_list_logs_2([{Node,TD}|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- case check_modify_tracerdata(TD,LD) of
- {ok,TracerData} ->
- Answer=?RUNTIME:list_logs(Pid,TracerData),
- do_list_logs_2(Tail,LD,[{Node,Answer}|Replies]);
- Error ->
- do_list_logs_2(Tail,LD,[{Node,Error}|Replies])
- end;
- Error ->
- do_list_logs_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_list_logs_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- Answer=?RUNTIME:list_logs(Pid),
- do_list_logs_2(Tail,LD,[{Node,Answer}|Replies]);
- Error ->
- do_list_logs_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_list_logs_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_list_logs_2(Other,_LD,_Replies) ->
- {error,{badarg,Other}}.
-%% -----------------------------------------------------------------------------
-
-%% Function fetching logfiles using distributed erlang. This function does not
-%% return anything significant. This since the reply to the client is always
-%% sent by the CollectPid unless there is badarg fault detected before the
-%% CollectPid is spawned. Note that this function sends a list of fetchers from
-%% which the CollectPid shall expect replies.
-%% We try to catch some bad arguments like Destination and Prefix not being
-%% strings. However the fact that they are lists does not guarantee they are
-%% proper strings.
-do_fetch_log(ToNode,all,Dest,Prefix,From,LD) ->
- do_fetch_log(ToNode,started_trace_nodes(all,LD),Dest,Prefix,From,LD);
-do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when is_list(Dest),is_list(Prefix) ->
- CollectPid=spawn_link(ToNode,?MODULE,log_rec_init,[self(),Dest,Prefix,From]),
- do_fetch_log_2(Specs,LD,CollectPid,[],[]);
-do_fetch_log(_ToNode,_Specs,Dest,Prefix,From,_LD) ->
- gen_server:reply(From,{error,{badarg,[Dest,Prefix]}}).
-
-do_fetch_log_2([{Node,Spec}|Rest],LD,CollectPid,Fetchers,Replies) ->
- if
- Node==node() -> % This is plain stupid!
- do_fetch_log_2(Rest,LD,CollectPid,Fetchers,[{Node,{error,own_node}}|Replies]);
- true ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {NewFetchers,NewReplies}=
- do_fetch_log_3(Fetchers,
- Replies,
- Node,
- ?RUNTIME:fetch_log(Pid,CollectPid,Spec)),
- do_fetch_log_2(Rest,LD,CollectPid,NewFetchers,NewReplies);
- Error -> % Most likely the node does not exist.
- do_fetch_log_2(Rest,LD,CollectPid,Fetchers,[{Node,Error}|Replies])
- end
- end;
-do_fetch_log_2([Node|Rest],LD,CollectPid,Fetchers,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- {NewFetchers,NewReplies}=
- do_fetch_log_3(Fetchers,Replies,Node,?RUNTIME:fetch_log(Pid,CollectPid)),
- do_fetch_log_2(Rest,LD,CollectPid,NewFetchers,NewReplies);
- Error -> % Most likely the node does not exist.
- do_fetch_log_2(Rest,LD,CollectPid,Fetchers,[{Node,Error}|Replies])
- end;
-do_fetch_log_2([],_,CollectPid,Fetchers,Replies) ->
- CollectPid ! {?MODULE,self(),Fetchers,Replies};
-do_fetch_log_2(FaultySpec,_,CollectPid,_Fetchers,_Replies) ->
- CollectPid ! {?MODULE,self(),[],{error,{badarg,FaultySpec}}}.
-
-do_fetch_log_3(Fetchers,Replies,Node,{ok,Fetcher}) ->
- {[{Node,Fetcher}|Fetchers],Replies};
-do_fetch_log_3(Fetchers,Replies,Node,{complete,no_log}) ->
- {Fetchers,[{Node,{complete,no_log}}|Replies]};
-do_fetch_log_3(Fetchers,Replies,Node,{error,Reason}) ->
- {Fetchers,[{Node,{error,Reason}}|Replies]}.
-%% -----------------------------------------------------------------------------
-
-%% Function removing files from the runtime components. We can either ask for
-%% all files associated with the current tracerdata to be removed, or provide
-%% tracerdata or a list of files to be removed.
-%% Returns the client reply.
-do_delete_log(all,LD) ->
- do_delete_log_2(started_trace_nodes(all,LD),LD,[]);
-do_delete_log(NodeSpecs,LD) ->
- case inviso_rt_lib:is_tracerdata(NodeSpecs) of
- true -> % It is tracerdata for all nodes.
- do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end,
- started_trace_nodes(all,LD)),
- LD,[]);
- false ->
- if
- is_list(NodeSpecs),is_list(hd(NodeSpecs)) -> % A list of files.
- do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end,
- started_trace_nodes(all,LD)),
- LD,[]);
- true -> % Then use it as is.
- do_delete_log_2(NodeSpecs,LD,[])
- end
- end.
-
-do_delete_log_2([{Node,Spec}|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_delete_log_2(Tail,LD,[{Node,?RUNTIME:delete_log(Pid,Spec)}|Replies]);
- Error ->
- do_delete_log_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_delete_log_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_delete_log_2(Tail,LD,[{Node,?RUNTIME:delete_log(Pid)}|Replies]);
- Error ->
- do_delete_log_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_delete_log_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_delete_log_2(Faulty,_,_) ->
- {error,{badarg,Faulty}}.
-%% -----------------------------------------------------------------------------
-
-%% Function removing files from runtime components.
-%% Returns {ok,Replies} or {error,Reason}.
-do_clear(Nodes,LD,Options) ->
- do_clear_2(started_trace_nodes(Nodes,LD),LD,Options,[]).
-
-do_clear_2([Node|Tail],LD,Options,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_clear_2(Tail,LD,Options,[{Node,?RUNTIME:clear(Pid,Options)}|Replies]);
- Error ->
- do_clear_2(Tail,LD,Options,[{Node,Error}|Replies])
- end;
-do_clear_2([],_LD,_Options,Replies) ->
- {ok,lists:reverse(Replies)};
-do_clear_2(FaultyNodes,_LD,_Options,_Replies) ->
- {error,{badarg,FaultyNodes}}.
-%% -----------------------------------------------------------------------------
-
-%% Function doing a flush trace-port.
-%% Returns {ok,Replies} or {error,Reason}.
-do_flush(Nodes,LD) ->
- do_flush_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_flush_2([Node|Rest],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{pid=Pid} ->
- do_flush_2(Rest,LD,[{Node,?RUNTIME:flush(Pid)}|Replies]);
- Error ->
- do_flush_2(Rest,LD,[{Node,Error}|Replies])
- end;
-do_flush_2([],_LD,Replies) ->
- {ok,lists:reverse(Replies)};
-do_flush_2(FaultyNodes,_LD,_Replies) ->
- {error,{badarg,FaultyNodes}}.
-%% -----------------------------------------------------------------------------
-
-%% Function stopping runtime components. We can only stop runtime components
-%% belonging to this control component.
-%% Returns {NewLoopdata,Reply}.
-do_stop_nodes(Nodes,LD) ->
- do_stop_nodes_2(started_trace_nodes(Nodes,LD),LD,[]).
-
-do_stop_nodes_2([Node|Tail],LD,Replies) ->
- case get_node_rec(Node,LD) of
- #node{ref=MRef} ->
- erlang:demonitor(MRef),
- case ?RUNTIME:stop(Node) of
- ok ->
- NewLD=delete_node_rec(Node,LD),
- send_to_subscribers({disconnected,Node,void},LD),
- do_stop_nodes_2(Tail,NewLD,[{Node,ok}|Replies]);
- Error ->
- do_stop_nodes_2(Tail,LD,[{Node,Error}|Replies])
- end;
- Error ->
- do_stop_nodes_2(Tail,LD,[{Node,Error}|Replies])
- end;
-do_stop_nodes_2([],LD,Replies) ->
- {LD,{ok,Replies}};
-do_stop_nodes_2(Faulty,LD,_) ->
- {LD,{error,{badarg,Faulty}}}.
-%% -----------------------------------------------------------------------------
-
-%% Function being called when a runtime component sends a connect message to
-%% the controlcomponent. The control component then confirms that is has indeed
-%% taken on that runtime component.
-%% Returns a new loopdata structure.
-do_confirm_connection(Pid,Tag,LD) ->
- case ?RUNTIME:confirm_connection(Pid,Tag) of
- {node_info,Node,_Pid,VSN,State,Status,_Tag} ->
- MRef=erlang:monitor(process,Pid), % We must monitor it from now on.
- Rec=#node{node=Node,vsn=VSN,tag=Tag,ref=MRef,pid=Pid},
- send_to_subscribers({connected,Node,{Tag,{State,Status}}},LD),
- set_node_rec(Rec,LD); % Makes new loopdata.
- _Error -> % Strange, it wanted us as control!?
- LD
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function handling an incomming DOWN message. This can be one of our runtime
-%% components terminating, or a process subscribing to events. Send a trace-event
-%% to subscribers if it was a runtime terminating and remove it from
-%% our list of runtime components.
-%% Note that if a subscriber has subscribed multiple times to events, we will get
-%% multiple DOWN messages too, since we have monitored that process multiple
-%% times. It is therefore sufficient to remove just one subscription entry here
-%% each time (remove on the monitor reference!).
-%% Returns a new LoopData structure.
-do_down_msg(Ref,Info,LD) ->
- case find(fun ref/2,LD#state.nodes,Ref) of
- {value,Node} -> % Yes it was one of our nodes.
- send_to_subscribers({disconnected,Node,Info},LD),
- delete_node_rec(Node,LD);
- no_match -> % Not one of our nodes.
- case lists:keysearch(Ref,2,LD#state.subscribers) of
- {value,{_Pid,_}} -> % It was a subscriber terminating.
- LD#state{subscribers=lists:keydelete(Ref,2,LD#state.subscribers)};
- false -> % Not one of our subscribers either.
- LD % Do nothing then.
- end
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-
-%% Help function which inspects options to start and add_node. Returns a new
-%% list of options in {ok,Options} or {error,Reason}.
-check_options(Options, Context) ->
- check_options_2(Options, Context, []).
-
-check_options_2([],_Context,Result) ->
- {ok,Result};
-check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) ->
- check_options_2(OptionsTail,start,[{subscribe,Pid}|Result]);
-check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) ->
- check_options_2(OptionsTail,start,[{unsubscribe,Pid}|Result]);
-check_options_2([{dependency,How}|OptionsTail],Context,Result) ->
- check_options_2(OptionsTail,Context,[{dependency,How}|Result]);
-check_options_2([{overload,How}|OptionsTail],Context,Result) ->
- check_options_2(OptionsTail,Context,[{overload,How}|Result]);
-check_options_2([overload|OptionsTail],Context,Result) ->
- check_options_2(OptionsTail,Context,[overload|Result]);
-check_options_2([UnKnown|_],_Context,_Result) ->
- {error,{unknown_option,UnKnown}};
-check_options_2(UnKnown,_Context,_Result) ->
- {error,{unknown_option,UnKnown}}.
-%% ------------------------------------------------------------------------------
-
-%% Help function initiating the #state structure, i.e the loopdata. Since there
-%% are some initial values from the record defaults when creating a new #state,
-%% those must be compared with possibly specified Options. Specified Options shall
-%% of course override defaults.
-%% Note that it is not the control component's responsibility to understand all
-%% options later given to a runtime component. It mearly stores them in rt_options
-%% so they can be passed to the runtime component at start-up.
-%% Returns a loopdata structure.
-initiate_state(Options) ->
- ResultingOptions=merge_options((#state{})#state.rt_options,Options),
- LD1=initiate_state_2(ResultingOptions,#state{rt_options=[]}),
- case node() of % Finally set the distribution flag.
- nonode@nohost ->
- LD1#state{distributed=false};
- _ ->
- LD1#state{distributed=true}
- end.
-
-initiate_state_2([{subscribe,Proc}|Tail],LD) when is_pid(Proc);is_atom(Proc)->
- MRef=erlang:monitor(process,Proc),
- initiate_state_2(Tail,LD#state{subscribers=[{Proc,MRef}|LD#state.subscribers]});
-initiate_state_2([Opt|Tail],LD) when is_tuple(Opt),size(Opt)>=1 ->
- initiate_state_2(Tail,initiate_state_3(element(1,Opt),Opt,LD));
-initiate_state_2([Opt|Tail],LD) when is_atom(Opt) ->
- initiate_state_2(Tail,initiate_state_3(Opt,Opt,LD));
-initiate_state_2([_|Tail],LD) ->
- initiate_state_2(Tail,LD);
-initiate_state_2([],LD) ->
- LD.
-
-initiate_state_3(OptName,Opt,LD) ->
- case initiate_state_is_rt_option(OptName) of
- true -> % Yes, it shall be part of the rt_options.
- LD#state{rt_options=[Opt|LD#state.rt_options]};
- false -> % Ignore the option then.
- LD
- end.
-
-%% This is the only place you have to change should there be more rt_options
-%% introduced.
-initiate_state_is_rt_option(overload) -> true;
-initiate_state_is_rt_option(dependency) -> true;
-initiate_state_is_rt_option(_) -> false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a list of default options and a list of overriding
-%% options. The function builds a return value consisting of all default options
-%% unless they are overridden by a overriding option.
-%% An option can be either {Param,.....} or Param. I.e either a tuple with zero
-%% or more values associated with the Parameter, or just an atom.
-merge_options([], Options) ->
- Options;
-merge_options([T|DefaultTail],Options) when is_tuple(T),size(T)>=1 ->
- merge_options(DefaultTail,merge_options_2(element(1,T),T,Options));
-merge_options([Param|DefaultTail],Options) when is_atom(Param) ->
- merge_options(DefaultTail,merge_options_2(Param,Param,Options));
-merge_options([_|DefaultTail],Options) -> % Strange, bad default option!
- merge_options(DefaultTail,Options).
-
-merge_options_2(Param,Opt,Options) ->
- case merge_options_find(Param,Options) of
- true ->
- Options;
- false ->
- [Opt|Options]
- end.
-
-merge_options_find(Param,[T|_]) when is_tuple(T),element(1,T)==Param ->
- true;
-merge_options_find(Param,[Param|_]) ->
- true;
-merge_options_find(Param,[_|Rest]) ->
- merge_options_find(Param,Rest);
-merge_options_find(_,[]) ->
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transforms certain parts of a tracer data. Those are
-%% parts which must be transformed at the controlnode like node to pid mappings.
-%% It also checks the formatting of the tracerdata since runtime components
-%% does not accept too badly formatted tracerdata.
-%% Returns {ok,NewTraceData} or {error,Reason}.
-check_modify_tracerdata(TracerData,LoopData) when is_list(TracerData) ->
- case lists:keysearch(trace,1,TracerData) of
- {value,{_,TraceTD}} -> % Examine the trace part.
- case check_modify_tracerdata(TraceTD,LoopData) of
- {ok,NewTraceTD} ->
- {ok,lists:keyreplace(trace,1,TracerData,{trace,NewTraceTD})};
- {error,Reason} -> % The trace part was faulty.
- {error,Reason} % Ruins everything :-)
- end;
- false -> % Unusual, but no trace part.
- {ok,TracerData} % No modifications necessary.
- end;
-check_modify_tracerdata(collector,_LoopData) ->
- {ok, collector};
-check_modify_tracerdata({relayer,Collector},_LoopData) when is_pid(Collector) ->
- {ok,{relayer,Collector}};
-check_modify_tracerdata({relayer,Collector},LoopData) when is_atom(Collector) ->
- case get_node_rec(Collector,LoopData) of
- Rec when is_record(Rec,node) -> % Collector is a known node.
- {ok,{relayer,Rec#node.pid}};
- {error,not_an_added_node} ->
- {error,{not_an_added_node,Collector}}
- end;
-check_modify_tracerdata({Type,Data},_LoopData) when Type==ip;Type==file ->
- {ok,{Type,Data}};
-check_modify_tracerdata({Handler,Data},_LoopData) when is_function(Handler) ->
- {ok,{Handler,Data}};
-check_modify_tracerdata(Data,_LoopData) ->
- {error,{bad_tracerdata,Data}}.
-%% -----------------------------------------------------------------------------
-
-%% Help function sending an event to all processes subscribing to events.
-%% Note that the function manipulates the from Node indicator incase we are not
-%% running in a distributed system.
-%% Returns nothing significant.
-send_to_subscribers(Msg={Event,_Node,Data},LD) ->
- AdaptedMsg=
- case LD#state.distributed of
- true ->
- Msg;
- false ->
- {Event,local_runtime,Data}
- end,
- TraceEvent={inviso_event,self(),erlang:localtime(),AdaptedMsg},
- send_to_subscribers_2(LD#state.subscribers,TraceEvent).
-
-send_to_subscribers_2([],_) ->
- ok;
-send_to_subscribers_2([{Subscriber,_}|Tail],TraceEvent) ->
- Subscriber ! TraceEvent,
- send_to_subscribers_2(Tail,TraceEvent).
-%% -----------------------------------------------------------------------------
-
-%% Help function converting the Nodes parameter to known nodes. Actually today
-%% it only converts the all atom to all known nodes.
-%% Returns a list of nodes.
-started_trace_nodes(all,LoopData) ->
- lists:map(fun(N)->N#node.node end,LoopData#state.nodes);
-started_trace_nodes(Nodes,_) ->
- Nodes.
-%% ------------------------------------------------------------------------------
-
-%% Help function searching through a list of elements looking for an element
-%% containing Key. How the element shall be interpreted is done by the Fun.
-%% Returns {value,Element} or 'no_match'.
-%% Fun=fun(Element,Key)={return,Value} | false
-find(_,[],_Key) ->
- no_match;
-find(Fun,[H|T],Key) ->
- case Fun(H,Key) of
- {return,Value}->
- {value,Value};
- _ ->
- find(Fun,T,Key)
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions handling the nodes datastructure part of the #state.
-%% #state.nodes is a list of #node.
-%% -----------------------------------------------------------------------------
-
-%% Function used to build find fun, looking for a certain #node with its monitoring
-%% reference set to Ref. Useful when finding out if a DOWN message comes from one
-%% of our runtime components.
-ref(#node{ref=Ref,node=Node},Ref) ->
- {return,Node};
-ref(_,_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% use in find/3
-%% Function used to build find fun, finding out if we have a node with the node
-%% name Node.
-is_started(#node{node=Node},Node) ->
- {return,true};
-is_started(_,_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function replacing or adding an entry for a node. Works on either a list
-%% of #node or a loopdata structure. Returns a new list of #node or a loopdata struct.
-set_node_rec(Rec,LD=#state{nodes=NodeList}) ->
- LD#state{nodes=set_node_rec_2(Rec,NodeList)}.
-
-set_node_rec_2(Rec,[]) ->
- [Rec];
-set_node_rec_2(Rec,[NodeRec|Tail]) when NodeRec#node.node==Rec#node.node ->
- [Rec|Tail];
-set_node_rec_2(Rec,[NodeRec|Tail]) ->
- [NodeRec|set_node_rec_2(Rec,Tail)].
-%% ------------------------------------------------------------------------------
-
-%% Help function finding a node record for Node in a list of #node or in loopdata.
-%% Returns the #node in question or {error,not_an_added_node}.
-get_node_rec(Node,NodeList) when is_list(NodeList) ->
- get_node_rec_2(Node,NodeList);
-get_node_rec(Node,#state{nodes=NodeList}) ->
- get_node_rec_2(Node,NodeList).
-
-get_node_rec_2(_Node,[]) ->
- {error,not_an_added_node};
-get_node_rec_2(Node,[NodeRec|_]) when NodeRec#node.node==Node ->
- NodeRec;
-get_node_rec_2(Node,[_NodeRec|Tail]) ->
- get_node_rec_2(Node,Tail).
-%% ------------------------------------------------------------------------------
-
-%% Help function removing a #node from either a list of #node or from a loopdata
-%% structure. Returns a new list of #node or a new loopdata structure.
-delete_node_rec(Node,LD=#state{nodes=NodeList}) ->
- LD#state{nodes=delete_node_rec_2(Node,NodeList)};
-delete_node_rec(Node,NodeList) when is_list(NodeList) ->
- delete_node_rec_2(Node,NodeList).
-
-delete_node_rec_2(_,[]) ->
- [];
-delete_node_rec_2(#node{node=Node},[#node{node=Node}|Tail]) ->
- Tail;
-delete_node_rec_2(Node,[#node{node=Node}|Tail]) ->
- Tail;
-delete_node_rec_2(Node,[NRec|Tail]) ->
- [NRec|delete_node_rec_2(Node,Tail)].
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Implementation of the help process receiving all logs from the runtime
-%% components. This process is referred to as the CollectPid.
-%% It is responsible for sending the reply back to the control component
-%% client. If a runtime component becomes suspended, the CollectPid is
-%% alerted by the DOWN message.
-%% Note that it may take some time before this process responds back to the client.
-%% Therefore the client must wait for 'infinity'. The job of transferring the
-%% files can be costly. Therefore it is a good idea to stop if no one is really
-%% interested in the result. This collector process monitors the From client in
-%% order to learn if the job can be cancelled. That will also be a possibility
-%% for a client to willfully cancel a fetch job.
-%% =============================================================================
-
-%% Intitial function on which the control component spawns. Note that the start
-%% must be done in two steps since the runtime components must be informed of
-%% the CollectPid. But the CollectPid must also know from which runtime components
-%% it can expect files from.
-%% InitialReplies: contains {Node,Result} for nodes from where there will be no
-%% files, but which must be part of the final reply.
-log_rec_init(Parent,Dest,Prefix,From={ClientPid,_}) ->
- receive
- {?MODULE,Parent,Fetchers,InitialReplies} ->
- RTs=lists:map(fun({N,F})->
- {N,erlang:monitor(process,F),void,void,void}
- end,
- Fetchers),
- CMRef=erlang:monitor(process,ClientPid), % Monitor the client.
- case log_rec_loop(Dest,Prefix,RTs,InitialReplies,CMRef) of
- Reply when is_list(Reply) -> % It is an ok value.
- gen_server:reply(From,{ok,Reply});
- {error,Reason} ->
- gen_server:reply(From,{error,Reason});
- false -> % The client terminated, no response.
- true % Simply terminate, fetchers will notice.
- end
- end.
-
-log_rec_loop(_Dest,_Prefix,[],Replies,_CMRef) -> % All nodes done!
- Replies; % This is the final reply.
-log_rec_loop(Dest,Prefix,RTs,Replies,CMRef) ->
- receive
- {Node,open,{FType,RemoteFName}} ->
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,_,_,_}} ->
- {NewRTs,NewReplies}=
- log_rec_open(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,open_failure,{FType,RemoteFName}} ->
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,_,_,_}} ->
- {NewRTs,NewReplies}=
- log_rec_open_failure(Node,MRef,FType,RemoteFName,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,payload,Bin,FPid} -> % A chunk of data from a fetcher.
- case lists:keysearch(Node,1,RTs) of
- {value,{_,_,_,_,void}} -> % Node has no file open here.
- FPid ! {self(),cancel_transmission}, % No use sending more to me.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef); % Simply ignore payload.
- {value,{_Node,MRef,FType,FName,FD}} ->
- case log_rec_payload(Node,MRef,FType,FName,FD,Bin,RTs,Replies) of
- {ok,{NewRTs,NewReplies}} ->
- FPid ! {self(),chunk_ack}, % For flow control.
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- {error,{NewRTs,NewReplies}} ->
- FPid ! {self(),cancel_transmission}, % No use sending more to me.
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef)
- end;
- false -> % Node is not part of transfere.
- FPid ! {self(),cancel_transmission}, % No use sending more to me.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,end_of_file} ->
- case lists:keysearch(Node,1,RTs) of
- {value,{_,_,_,_,void}} -> % Node has no file open here.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef);
- {value,{_,MRef,FType,FName,FD}} ->
- {NewRTs,NewReplies}=
- log_rec_eof(Node,MRef,FType,FName,FD,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,end_of_transmission} -> % This runtime is done!
- case lists:keysearch(Node,1,RTs) of
- {value,{_Node,MRef,_,_,_}} ->
- erlang:demonitor(MRef),
- log_rec_loop(Dest,Prefix,
- lists:keydelete(Node,1,RTs),
- log_rec_mkreply(Node,complete,Replies),
- CMRef);
- false -> % Strange, not one of our nodes.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,incomplete} -> % This runtime is done (with errors).
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,FType,FName,FD}} ->
- erlang:demonitor(MRef),
- {NewRTs,NewReplies}=
- log_rec_incomplete(Node,FType,FName,FD,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false -> % Not our, or not anylonger.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {Node,{error,Reason}} -> % Remote file read_error.
- case lists:keysearch(Node,1,RTs) of
- {value,{_,MRef,FType,FName,FD}} ->
- {NewRTs,NewReplies}=
- log_rec_error(Node,MRef,FType,FName,FD,RTs,Reason,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- {'DOWN',CMRef,process,_,_} -> % The client got tired waiting.
- log_rec_cancel(Dest,RTs,Replies), % Close and remove all files.
- false; % Indicate no response message.
- {'DOWN',Ref,process,_P,_Info} ->
- case lists:keysearch(Ref,2,RTs) of
- {value,{Node,_,FType,FName,FD}} ->
- {NewRTs,NewReplies}=
- log_rec_incomplete(Node,FType,FName,FD,RTs,Replies),
- log_rec_loop(Dest,Prefix,NewRTs,NewReplies,CMRef);
- false -> % Not our, or not anylonger.
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end;
- _ ->
- log_rec_loop(Dest,Prefix,RTs,Replies,CMRef)
- end.
-
-%% Help function opening a new target file on the receiver. It returns
-%% {NewRTs,NewReplies}.
-%% Note that we must protect us against that some of the strings are not proper
-%% strings, but contains garbage.
-log_rec_open(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies) ->
- case catch log_rec_open_2(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies) of
- {'EXIT',Reason} ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,
- FType,
- {error,{file_open,{Reason,[Dest,Prefix,RemoteFName]}}},Replies),
- {NewRTs,NewReplies};
- Result ->
- Result
- end.
-
-log_rec_open_2(Dest,Prefix,Node,MRef,FType,RemoteFName,RTs,Replies) ->
- FName=Prefix++RemoteFName, % Our file name.
- case file:open(filename:join([Dest,FName]),[write]) of
- {ok,FD} ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,FType,FName,FD}),
- {NewRTs,Replies};
- {error,Reason} ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,FType,{error,{file_open,{Reason,FName}}},Replies),
- {NewRTs,NewReplies}
- end.
-
-%% Help function adding a file that was unsuccessfully opened as failed.
-log_rec_open_failure(Node,MRef,FType,RemoteFName,RTs,Replies) ->
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,
- FType,
- {error,{remote_open,RemoteFName}},Replies),
- {NewRTs,NewReplies}.
-
-%% Help function whih writes the Bin to the FD file. If writing was unsuccessful,
-%% close the file and modify RTs and add a reply to Replies. Note that we can not
-%% stop the runtime from sending us more data belonging to this file. But we will
-%% simply just inore it from now on.
-%% Returns {SuccessCode,{NewRTs,NewReplies}}.
-log_rec_payload(Node,MRef,FType,FName,FD,Bin,RTs,Replies) ->
- case file:write(FD,Bin) of
- ok ->
- {ok,{RTs,Replies}};
- {error,Reason} ->
- file:close(FD),
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- NewReplies=
- log_rec_addreply(Node,FType,{error,{file_write,{Reason,FName}}},Replies),
- {error,{NewRTs,NewReplies}}
- end.
-
-%% Help function whih shall be used when a file has been successfully transfered.
-%% This function closes the output file and updates RTs and the Replies.
-%% Returns {NewRTs,NewReplies}.
-log_rec_eof(Node,MRef,FType,FName,FD,RTs,Replies) ->
- file:close(FD),
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- {NewRTs,log_rec_addreply(Node,FType,{ok,FName},Replies)}.
-
-%% Help function which, if there is an open file, indicates it as truncated in the
-%% replies. And finalize the reply for Node assuming that the node is in total incomplete
-%% Returns {NewRTs,NewReplies}.
-log_rec_incomplete(Node,_FType,_FName,void,RTs,Replies) ->
- NewRTs=lists:keydelete(Node,1,RTs), % The node is done.
- {NewRTs,log_rec_mkreply(Node,incomplete,Replies)};
-log_rec_incomplete(Node,FType,FName,FD,RTs,Replies) ->
- file:close(FD), % Not going to write anymore in this file.
- NewRTs=lists:keydelete(Node,1,RTs), % The node is done.
- NewReplies=log_rec_addreply(Node,FType,{error,{truncated,FName}},Replies),
- {NewRTs,log_rec_mkreply(Node,incomplete,NewReplies)}.
-
-%% Help function handling the case when runtime component experiences an error
-%% transferering the file. That means that there will be no more chunks of this
-%% file. Hence it works a bit like EOF.
-%% Returns {NewRTs,NewReplies}.
-log_rec_error(Node,MRef,FType,FName,FD,RTs,Reason,Replies) ->
- file:close(FD),
- NewRTs=lists:keyreplace(Node,1,RTs,{Node,MRef,void,void,void}),
- {NewRTs,log_rec_addreply(Node,FType,{error,{truncated,{Reason,FName}}},Replies)}.
-%% -----------------------------------------------------------------------------
-
-%% Help function adding a reply to the list of replies.
-%% Replies is a list {Node,FType,Reply} for each file handled, sucessfully or not.
-%% The list may also contain finalized nodes, which will be on the format:
-%% {Node,{Conclusion,[{trace_log,TraceLogReplies},{ti_log,TiLogReplies}]}}.
-log_rec_addreply(Node,FType,Reply,Replies) ->
- [{Node,FType,Reply}|Replies].
-
-%% Help function which converts the {Node,FType,Reply} tuples in Replies to
-%% a finalized reply.
-log_rec_mkreply(Node,Conclusion,Replies) ->
- {RemainingReplies,TiReplies,TraceReplies}=
- log_rec_mkreply_node_ftype(Node,Replies,[],[],[]),
- [{Node,{Conclusion,[{trace_log,TraceReplies},{ti_log,TiReplies}]}}|
- RemainingReplies].
-
-%% Help function taking out the ti_log and trace_log file-types replies for
-%% Node. Returns {RemainingReplies,Ti,Trace}.
-log_rec_mkreply_node_ftype(Node,[{Node,ti_log,Result}|Rest],Replies,Ti,Trace) ->
- log_rec_mkreply_node_ftype(Node,Rest,Replies,[Result|Ti],Trace);
-log_rec_mkreply_node_ftype(Node,[{Node,trace_log,Result}|Rest],Replies,Ti,Trace) ->
- log_rec_mkreply_node_ftype(Node,Rest,Replies,Ti,[Result|Trace]);
-log_rec_mkreply_node_ftype(Node,[Reply|Rest],Replies,Ti,Trace) ->
- log_rec_mkreply_node_ftype(Node,Rest,[Reply|Replies],Ti,Trace);
-log_rec_mkreply_node_ftype(_,[],Replies,Ti,Trace) ->
- {Replies,Ti,Trace}.
-%% -----------------------------------------------------------------------------
-
-%% If the fetching job shall be cancelled, we must close all open files and
-%% remove them including all already closed files. Returns nothing significant.
-log_rec_cancel(Dest,RTs,Replies) ->
- log_rec_cancel_open(Dest,RTs), % First close and remove all open files.
- log_rec_cancel_finished(Dest,Replies). % Remove all already closed files.
-
-log_rec_cancel_open(Dest,[{_Node,_MRef,_FType,_FName,void}|Rest]) ->
- log_rec_cancel_open(Dest,Rest); % There is no open file to close.
-log_rec_cancel_open(Dest,[{_Node,_MRef,_FType,FName,FD}|Rest]) ->
- file:close(FD),
- catch file:delete(filename:join(Dest,FName)), % Will just try to do my best.
- log_rec_cancel_open(Dest,Rest);
-log_rec_cancel_open(_Dest,[]) ->
- true.
-
-log_rec_cancel_finished(Dest,[{_N,_FT,Reply}|Rest]) ->
- [FName]=log_rec_cancel_finished_get_fname([Reply]),
- catch file:delete(filename:join(Dest,FName)),
- log_rec_cancel_finished(Dest,Rest);
-log_rec_cancel_finished(Dest,[{_N,{_Conclusion,[{_,Replies1},{_,Replies2}]}}|Rest]) ->
- FNames1=log_rec_cancel_finished_get_fname(Replies1),
- lists:foreach(fun(FName)->
- catch file:delete(filename:join(Dest,FName))
- end,
- FNames1),
- FNames2=log_rec_cancel_finished_get_fname(Replies2),
- lists:foreach(fun(FName)->
- catch file:delete(filename:join(Dest,FName))
- end,
- FNames2),
- log_rec_cancel_finished(Dest,Rest);
-log_rec_cancel_finished(_Dest,[]) ->
- true.
-
-%% Help function going through all possible reply values for a file. So
-%% consequently there must be a clause here for every possible log_rec_addreply
-%% call above. Returns a list of filenames that shall be removed in order to
-%% restore the disk since the fetch job is cancelled.
-log_rec_cancel_finished_get_fname([{error,{file_open,{_,FName}}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{error,{file_write,{_,FName}}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{ok,FName}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{error,{truncated,{_,FName}}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([{error,{truncated,FName}}|Rest]) ->
- [FName|log_rec_cancel_finished_get_fname(Rest)];
-log_rec_cancel_finished_get_fname([_|Rest]) -> % This shall not happend.
- log_rec_cancel_finished_get_fname(Rest);
-log_rec_cancel_finished_get_fname([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% EOF
diff --git a/lib/inviso/src/inviso_lfm.erl b/lib/inviso/src/inviso_lfm.erl
deleted file mode 100644
index 085048518c..0000000000
--- a/lib/inviso/src/inviso_lfm.erl
+++ /dev/null
@@ -1,431 +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$
-%%
-%% Author: Lennart �hman, [email protected]
-%%
-%% INVISO LogFile Merger.
-%%
-%% Merges all log-entries in all files in Files in chronological order
-%% into what ever is handled by WorkHandlerFun. Note that Files can contain
-%% several files. Both in the sence that it can be a wrapset. But also because
-%% the log is spread over more than one LogFiles (i.e trace_log + ti_log).
-%% It is further possible to use another reader-process (for the logfiles)
-%% than the default one. This is useful if the logfiles are formatted in
-%% another way than as done by a trace-port.
-
--module(inviso_lfm).
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([merge/2,merge/3,merge/4,merge/5,merge/6]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Default handler exports.
-%% -----------------------------------------------------------------------------
-
--export([outfile_opener/1,outfile_writer/4,outfile_closer/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Formatting functions.
-%% -----------------------------------------------------------------------------
-
--export([format_arguments/3,format_argument_string/2]).
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([init_receiver/7]).
-%% -----------------------------------------------------------------------------
-
-%% merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData)=
-%% {ok,Count} | {error,Reason}
-%% merge(Files,OutputFile) =
-%%
-%% Files=[FileDescription,...]
-%% FileDescription=FileSet | {reader,Mod,Func,FileSet}
-%% FileSet={Node,LogFiles} | {Node,[LogFiles,...]}
-%% in the latter case the LogFiles must be sorted, beginning with the oldest.
-%% LogFiles=[{trace_log,Files} [,{ti_log,[FileName]}] ]
-%% either just trace_log or trace_log and ti_log.
-%% Files=[FileName] | [FileName,...]
-%% in the latter case it is a wrapset.
-%% BeginHandlerFun= ( fun(HandlerData)->{ok,NewHandleData} | {error,Reason} )
-%% WorkHandlerFun= ( fun(Node,Term,PidMappings,HandlerData)->
-%% {ok,NewHandlerData} | {error,Reason}
-%% EndHandlerFun= ( fun(HandlerData)->ok | {error,Reason} )
-%% Count=integer(), the total number of handled log entries.
-%%
-%% Merges all logfiles in Files together into one common log file, in chronological
-%% order according to the time-stamps in each log. Each entry is also marked with
-%% the node name in the merged log.
-%% Configuration:
-%% If a non-default reader shall be used, Mod:Func(ReceiverPid,LogFiles) shall
-%% spawn a reader process complying to the receiver/reader message protocoll.
-%% The default reader reads logs generated by a trace-port.
-%% BeginHandler is called before any logentries are processed, typically to open
-%% the out-file, if any.
-%% WorkHandlerFun is called for every log-entry. It typically writes the output.
-%% EndHandlerFun is called when the last reader has finished, typically to
-%% close the outfile.
-%%
-%% Using merge/2 assumes you want to use default handlers writing to a file.
-merge(Files,OutputFile) when is_list(OutputFile) ->
- merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,off).
-merge(Files,WorkHandlerFun,HandlerData) when is_function(WorkHandlerFun) ->
- merge(Files,void,WorkHandlerFun,void,HandlerData,off);
-merge(Files,OutputFile,Dbg) when is_list(OutputFile) ->
- merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,Dbg).
-merge(Files,WorkHandlerFun,HandlerData,Dbg) when is_function(WorkHandlerFun) ->
- merge(Files,void,WorkHandlerFun,void,HandlerData,Dbg).
-merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData) ->
- merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,off).
-merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg) ->
- ReceiverPid=spawn_link(?MODULE,
- init_receiver,
- [self(),Files,BeginHandlerFun,WorkHandlerFun,
- EndHandlerFun,HandlerData,Dbg]),
- wait_for_response(ReceiverPid).
-
-wait_for_response(ReceiverPid) ->
- receive
- {reply,ReceiverPid,Reply} ->
- Reply;
- {'EXIT',ReceiverPid,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Code for the receiver process.
-%% =============================================================================
-
-%% Initial function for the receiver process. This function must be exported.
-init_receiver(From,Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg) ->
- case setup_readers(Files) of % Create the reader processes.
- {ok,Readers} ->
- process_flag(trap_exit,true),
- if
- is_function(BeginHandlerFun) ->
- case catch BeginHandlerFun(HandlerData) of
- {ok,NewHandlerData} ->
- init_receiver_2(From,WorkHandlerFun,EndHandlerFun,
- NewHandlerData,Dbg,Readers);
- {error,Reason} -> % Faulty begin-function.
- From ! {reply,self(),{error,{begin_handler,Reason}}};
- {'EXIT',Reason} ->
- From ! {reply,self(),{error,{begin_handler,Reason}}}
- end;
- true -> % There is no begin-handler.
- init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers)
- end;
- {error,Reason} ->
- From ! {reply,self(),{error,{files,Reason}}}
- end.
-
-init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers) ->
- {NewReaders,EntryStruct}=mk_entrystruct(Readers,Dbg),
- {Reply,NewHandlerData}=
- loop(From,WorkHandlerFun,HandlerData,NewReaders,EntryStruct,Dbg,0),
- if
- is_function(EndHandlerFun) ->
- case EndHandlerFun(NewHandlerData) of
- ok ->
- From ! {reply,self(),Reply};
- {error,_Reason} ->
- From ! {reply,self(),Reply}
- end;
- true -> % Reply directly then, no finish fun.
- From ! {reply,self(),Reply}
- end.
-
-%% Function that spawns a help process for each group of files in the list.
-%% The help process will read entries from the input files in the correct order
-%% and deliver them to the receiver process.
-%% Note that there is a possibility to design your own readers. The default
-%% reader understands trace-port generated logfiles.
-%% Returns a list of {Node,Pid}.
-setup_readers(Files) ->
- setup_readers_2(Files,[]).
-
-setup_readers_2([{reader,Mod,Func,{Node,FileStruct}}|Rest],Acc) ->
- Pid=spawn_link(Mod,Func,[self(),FileStruct]),
- setup_readers_2(Rest,[{Node,Pid}|Acc]);
-setup_readers_2([{Node,FileStruct}|Rest],Acc) ->
- Pid=spawn_link(inviso_lfm_tpfreader,init,[self(),FileStruct]),
- setup_readers_2(Rest,[{Node,Pid}|Acc]);
-setup_readers_2([],Acc) ->
- {ok,Acc};
-setup_readers_2([Faulty|_],_Acc) ->
- {error,{bad_reader_spec,Faulty}}.
-%% -----------------------------------------------------------------------------
-
-%% This is the workloop that polls each reader for messages and writes them
-%% in the correct order.
-loop(From,WorkHFun,HData,Readers,EntryStruct,Dbg,Count) ->
- case find_oldest_entry(EntryStruct) of
- {Pid,Node,PidMappings,Term} ->
- case get_and_insert_new_entry(From,Node,Pid,Readers,EntryStruct,Dbg) of
- {ok,{NewReaders,NewEntryStruct}} ->
- case WorkHFun(Node,Term,PidMappings,HData) of
- {ok,NewHData} ->
- loop(From,WorkHFun,NewHData,NewReaders,NewEntryStruct,Dbg,Count+1);
- {error,Reason} -> % Serious, we cant go on then.
- stop_readers(NewReaders),
- {{error,{writing_output_file,Reason}},HData}
- end;
- {stop,_Reason} -> % The original caller is no longer there!
- stop_readers(Readers),
- {error,HData}
- end;
- done -> % No more readers.
- {{ok,Count},HData}
- end.
-
-%% Help function which finds the oldest entry in the EntryStruct. Note that the
-%% timestamp can actually be the atom 'false'. This happens for instance if it is
-%% a dropped-messages term. But since 'false' is smaller than any tuple, that
-%% term will be consumed immediately as soon as it turns up in EntryList.
-find_oldest_entry(EntryStruct) ->
- case list_all_entries(EntryStruct) of
- [] -> % The we are done!
- done;
- EntryList when is_list(EntryList) -> % Find smallest timestamp in here then.
- {Pid,Node,PidMappings,_TS,Term}=
- lists:foldl(fun({P,N,PMap,TS1,T},{_P,_N,_PMap,TS0,_T}) when TS1<TS0 ->
- {P,N,PMap,TS1,T};
- (_,Acc) ->
- Acc
- end,
- hd(EntryList),
- EntryList),
- {Pid,Node,PidMappings,Term}
- end.
-
-%% Help function which signals all reader process to clean-up and terminate.
-%% Returns nothing significant.
-stop_readers([Pid|Rest]) ->
- Pid ! {stop,self()},
- stop_readers(Rest);
-stop_readers([]) ->
- ok.
-%% -----------------------------------------------------------------------------
-
-%% Help function which tries to replace the entry by Pid in EntryStruct with
-%% a new one from that process. If one is returned on request, it replaces
-%% the old one in EntryStruct. If Pid is done or otherwise dissapears, Pid
-%% is simply removed from Readers and the EntryStruct.
-get_and_insert_new_entry(Node,Pid,Readers,EntryStruct,Dbg) ->
- get_and_insert_new_entry(void,Node,Pid,Readers,EntryStruct,Dbg).
-
-get_and_insert_new_entry(From,Node,Pid,Readers,EntryStruct,Dbg) ->
- Pid ! {get_next_entry,self()},
- receive
- {'EXIT',From,Reason} -> % No one is waiting for our reply!
- {stop,Reason}; % No use continuing then.
- {next_entry,Pid,PidMappings,TS,Term} -> % We got a next entry from Pid!
- ets:insert(EntryStruct,{Pid,Node,PidMappings,TS,Term}),
- {ok,{Readers,EntryStruct}};
- {next_entry,Pid,{error,_Reason}} -> % Reading an entry went wrong.
- get_and_insert_new_entry(From,Node,Pid,Readers,EntryStruct,Dbg);
- {'EXIT',Pid,_Reason} -> % The process has terminated.
- ets:delete(EntryStruct,Pid),
- NewReaders=lists:delete(Pid,Readers),
- {ok,{NewReaders,EntryStruct}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which from a list of reader processes creates the private
-%% storage where the oldest entry from each reader is always kept.
-%% Returns {Readers,EntryStruct}.
-mk_entrystruct(Pids,Dbg) ->
- TId=ets:new(list_to_atom("inviso_lfm_tab_"++pid_to_list(self())),[set]),
- mk_entrystruct_2(Pids,lists:map(fun({_,P})->P end,Pids),Dbg,TId).
-
-mk_entrystruct_2([{Node,Pid}|Rest],Readers,Dbg,EntryStruct) ->
- {ok,{NewReaders,NewEntryStruct}}=
- get_and_insert_new_entry(Node,Pid,Readers,EntryStruct,Dbg),
- mk_entrystruct_2(Rest,NewReaders,Dbg,NewEntryStruct);
-mk_entrystruct_2([],Readers,_Dbg,EntryStruct) ->
- {Readers,EntryStruct}.
-%% -----------------------------------------------------------------------------
-
-%% Help function that returns a list of our oldest entry structure.
-%% [{Pid,Node,PidMappings,TimeStamp,Term},...]
-list_all_entries(EntryStruct) ->
- ets:tab2list(EntryStruct).
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Default handlers for the receiver
-%% =============================================================================
-
-%% These functions are also exported in order to make them available when creating
-%% other funs in other modules.
-
-%% Default begin-handler.
-outfile_opener(FileName) ->
- case file:open(FileName,[write]) of
- {ok,FD} ->
- {ok,FD}; % Let the descriptor be handlerdata.
- {error,Reason} ->
- {error,{open,Reason}}
- end.
-
-%% Default work-handler.
-%% DEN H�R �R L�NGT IFR�N F�RDIG!!!
-outfile_writer(Node,Term,PidMappings,FD) ->
- io:format(FD,"~w ~w ~w~n",[Node,PidMappings,Term]),
- {ok,FD}.
-
-%% Default end-handler.
-outfile_closer(FD) ->
- file:close(FD),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Formatting functions.
-%% =============================================================================
-
-%% This section contains a useful formatting function formatting an (function)
-%% argument list. It also offers a working example of how to write
-%% own datatype translators (which will be used by the formatting function to
-%% further enhance the output).
-
-%% format_arguments(Args,FOpts,Transaltors)=Args2 | <failure>
-%% Args=list(), list of the argument as usually given in a trace message,
-%% a stack trace or similar.
-%% FOpts=term(), formatting options understood by the translation functions.
-%% Translations=[Translator,...]
-%% Translator=fun(Term,FOpts)=TResult | {M,F}, where M:F(Term,FOpts)=TResult
-%% TResult={ok,TranslationString} | false
-%% Arg2=list(), list of Args where terms may be replaced by own representations.
-%% Note that terms not effected will remain as is, but if an own representation
-%% is choosen, that must be a string in order for any io format function to
-%% print it exactly as formatted here.
-format_arguments([Arg|Rest],FOpts,Translators) -> % More than one argument.
- [format_argument(Arg,FOpts,Translators)|format_arguments(Rest,FOpts,Translators)];
-format_arguments([],_FOpts,_Translators) ->
- []. % The empty list.
-
-%% Help function handling the various Erlang datatypes. There must hence be one
-%% clause here for every existing datatype.
-format_argument(List,FOpts,Translators) when is_list(List) ->
- case format_argument_own_datatype(List,FOpts,Translators) of
- {true,TranslationStr} ->
- TranslationStr;
- false ->
- format_argument_list(List,FOpts,Translators)
- end;
-format_argument(Tuple,FOpts,Translators) when is_tuple(Tuple) ->
- case format_argument_own_datatype(Tuple,FOpts,Translators) of
- {true,TranslationStr} -> % It was one of our special datatypes.
- TranslationStr;
- false -> % Regular tuple.
- format_argument_tuple(Tuple,FOpts,Translators)
- end;
-format_argument(Binary,FOpts,Translators) when is_binary(Binary) ->
- case format_argument_own_datatype(Binary,FOpts,Translators) of
- {true,TranslationStr} -> % It was one of our special datatypes..
- TranslationStr;
- false -> % Regular binary.
- format_argument_binary(Binary,FOpts,Translators)
- end;
-format_argument(Atom,_FOpts,_Translators) when is_atom(Atom) ->
- Atom;
-format_argument(Integer,_FOpts,_Translators) when is_integer(Integer) ->
- Integer;
-format_argument(Float,_FOpts,_Translators) when is_float(Float) ->
- Float;
-format_argument(Pid,_FOpts,_Translators) when is_pid(Pid) ->
- Pid;
-format_argument(Port,_FOpts,_Translators) when is_port(Port) ->
- Port;
-format_argument(Ref,_FOpts,_Translators) when is_reference(Ref) ->
- Ref;
-format_argument(Fun,_FOpts,_Translators) when is_function(Fun) ->
- Fun.
-
-%% Help function handling the case when an element is a list.
-format_argument_list([Element|Rest],FOpts,Translators) ->
- [format_argument(Element,FOpts,Translators)|
- format_argument_list(Rest,FOpts,Translators)];
-format_argument_list([],_FOpts,_Translators) ->
- [].
-
-%% Help function handling the case when an element is a tuple.
-format_argument_tuple(Tuple,FOpts,Translators) ->
- list_to_tuple(format_argument_tuple(Tuple,FOpts,Translators,size(Tuple),[])).
-
-format_argument_tuple(_,_,_,0,List) ->
- List;
-format_argument_tuple(Tuple,FOpts,Translators,Index,List) ->
- E=format_argument(element(Index,Tuple),FOpts,Translators),
- format_argument_tuple(Tuple,FOpts,Translators,Index-1,[E|List]).
-
-%% Help function handling the case when an element is a binary.
-format_argument_binary(Binary,_FOpts,_Translators) ->
- Binary.
-
-%% Help function trying to use the translations.
-format_argument_own_datatype(Term,FOpts,[Fun|Rest]) when is_function(Fun) ->
- case catch Fun(Term,FOpts) of
- {ok,TranslationStr} ->
- {true,TranslationStr};
- _ ->
- format_argument_own_datatype(Term,FOpts,Rest)
- end;
-format_argument_own_datatype(Term,FOpts,[{M,F}|Rest]) ->
- case catch M:F(Term,FOpts) of
- {ok,TranslationStr} ->
- {true,TranslationStr};
- _ ->
- format_argument_own_datatype(Term,FOpts,Rest)
- end;
-format_argument_own_datatype(Term,FOpts,[_|Rest]) ->
- format_argument_own_datatype(Term,FOpts,Rest);
-format_argument_own_datatype(_Term,_FOpts,[]) -> % There is no applicable format.
- false.
-%% -----------------------------------------------------------------------------
-
-%% format_argument_string(String,_FOpts)={ok,QuotedString} | false
-%% String=string() | term()
-%% QuotedString="String"
-%% Example of datatype checker that checks, in this case, that its argument is
-%% a string. If it is, it returns a deep list of the characters to print in order
-%% to make it a quoted string.
-format_argument_string(List=[_|_],_FOpts) -> % Must be at least one element.
- case format_argument_string_2(List) of
- true ->
- {ok,[$",List,$"]};
- false ->
- false
- end;
-format_argument_string(_,_FOpts) ->
- false.
-
-format_argument_string_2([C|Rest]) when (((C<127) and (C>=32)) or ((C>=8) and (C=<13))) ->
- format_argument_string_2(Rest);
-format_argument_string_2([_|_]) ->
- false;
-format_argument_string_2([]) ->
- true.
-%% -----------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_lfm_tpfreader.erl b/lib/inviso/src/inviso_lfm_tpfreader.erl
deleted file mode 100644
index 6de4d11fe0..0000000000
--- a/lib/inviso/src/inviso_lfm_tpfreader.erl
+++ /dev/null
@@ -1,388 +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$
-%%
-%% Author: Lennart �hman, [email protected]
-
-%%
-%% INVISO LogFileMerger TracePort File READER.
-%%
-%% This module implements a reader process capable of reading traceport files
-%% and feeding them according to the logfile merger process message protocoll
-%% to the logfile merger process.
-%% This module can also serve as example for writing file readers for other
-%% file formats.
-%%
-%% A reader process must:
-%% Support the reader-receiver protocoll.
-%% receive next_entry message: {get_next_entry,ReceiverPid}
-%% recieve stop message should the receiver wish to quit: {stop,ReceiverPid}.
-%% send next_entry message, either with entry or fault-code.
-%% next_entry message contains:{next_entry,self(),PidMappings,Timestamp,Term}
-%% {next_entry,self(),Error}
-%% recognize receiver termination (EXIT-signal).
-%% Understand logfile structure, both filename structure and content.
-%% Understand content (log-entry) details to extract the entry and entry
-%% components as timestamp and originating pid (to make pid-mappings).
-%% Understand any trace information files (ti).
-%%
-%% The logfile structure written by inviso_rt_meta is:
-%% {Pid,Alias,Op,TimeStamp} where:
-%% Pid=pid(), if Alias==unalias: pid()|other_than_pid()
-%% Op=alias|unalias,
-%% TimeStamp=now()
-%% -----------------------------------------------------------------------------
--module(inviso_lfm_tpfreader).
-
--export([init/2]).
-%% -----------------------------------------------------------------------------
-
--export([handle_logfile_sort_wrapset/1]). % Exported as a service to other readers.
-%% -----------------------------------------------------------------------------
-
-%% init(RecPid,FileStruct)=N/A
-%% RecPid=pid(), the process id of the log file merger.
-%% FileStruct=LogFiles | [LogFiles,...]
-%% LogFiles=[{trace_log,[File,...]} [,{ti_log,[File]}] ]
-%% File=string()
-%% Spawn on this function to start a reader process for trace-port generated
-%% logfiles, possibly with inviso-generated ti-files.
-init(RecPid,LogFiles=[Tuple|_]) when is_tuple(Tuple) -> % Only one LogFiles.
- init(RecPid,[LogFiles]);
-init(RecPid,FileStruct) when is_list(FileStruct) ->
- logfiles_loop(RecPid,FileStruct).
-%% -----------------------------------------------------------------------------
-
-logfiles_loop(RecPid,[LogFiles|Rest]) ->
- {TIalias,TIunalias}=handle_ti_file(LogFiles),% If there is a ti-file, read it.
- Files=handle_logfiles(LogFiles), % Returns a sorted list of logfiles.
- case open_next_file(Files) of
- {ok,FileName,FD,NewFiles} ->
- case loop(RecPid,FileName,NewFiles,TIalias,TIunalias,FD) of
- next ->
- logfiles_loop(RecPid,Rest);
- stop ->
- true % Terminate normally.
- end;
- done -> % Hmm, already out of files.
- true; % Then lets terminate normally.
- {error,Reason} -> % Couldn't even open the first file.
- exit(Reason)
- end;
-logfiles_loop(_RecPid,[]) -> % No more files in LogFiles.
- true. % Terminate normally.
-
-%% This workloop reads an entry from the input file upon request from the merger
-%% process and sends it back to the merger process (Parent). If the file ends
-%% there are more files to open and read in Files, the next file will be opened.
-loop(RecPid,FileName,Files,TIalias,TIunalias,FD) ->
- receive
- {get_next_entry,RecPid} -> % The receiver request the next entry.
- case fetch_next(FileName,FD,Files) of
- {ok,Term,NewCurrFile,NewFiles,NewFD} ->
- TS=find_timestamp_in_term(Term),
- PidMappings=make_pid_mappings(Term,TIalias,TIunalias,TS),
- RecPid ! {next_entry,self(),PidMappings,TS,Term},
- loop(RecPid,NewCurrFile,NewFiles,TIalias,TIunalias,NewFD);
- {error,Reason} -> % Not a properly formatted entry.
- RecPid ! {next_entry,self(),{error,Reason}},
- loop(RecPid,FileName,Files,TIalias,TIunalias,FD);
- done -> % No more files to read in this LogFiles.
- next % Are there more Files in FileStruct?
- end;
- {stop,RecPid} -> % The receiver process is done.
- file:close(FD), % Close file and terminate normally.
- stop
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function which reads the next trace-entry from the file handled by FD, or if
-%% that file reaches EOF opens the next file in Files. Files must be sorted in
-%% the correct order.
-%% Returns {ok,Term,NewFileName,NewFiles,NewFD}, {error,Reason} or 'done'.
-fetch_next(FileName,FD,Files) ->
- case read_traceport_file(FileName,FD) of
- {ok,Term} -> % There were more terms in the file.
- {ok,Term,FileName,Files,FD}; % No changes necessary then.
- eof -> % This file is empty, try next file!
- file:close(FD),
- case open_next_file(Files) of
- {ok,NewFileName,NewFD,NewFiles} -> % A new file has been opened.
- fetch_next(NewFileName,NewFD,NewFiles); % Try again.
- done -> % No more files.
- done;
- {error,Reason} -> % Problems opening files.
- {error,Reason}
- end;
- {error,Reason} -> % Problems reading the file.
- {error,Reason}
- end.
-
-read_traceport_file(FileName,FD) ->
- case file:read(FD,5) of % Trace-port file entries start with 5 bytes.
- {ok,<<0,Size:32>>} -> % Each entry in a traceport file begins.
- case file:read(FD,Size) of
- {ok,Bin} when is_binary(Bin),size(Bin)=:=Size ->
- try binary_to_term(Bin) of
- Term -> % Bin was a properly formatted term!
- {ok,Term}
- catch
- error:_Reason -> % Not a properly formatted term!
- {error,{binary_to_term,[FileName,Bin]}}
- end;
- {ok,Bin} -> % Incorrect length.
- {error,{faulty_length,[FileName,Size,Bin]}};
- eof -> % This is premature end of file!
- {error,{premature_eof,FileName}}
- end;
- {ok,<<1,DroppedMsgs:32>>} ->
- {ok,{drop,DroppedMsgs}};
- {ok,JunkBin} -> % Don't understand, report it as error.
- {error,{junk,[FileName,JunkBin]}};
- eof -> % A correct end of file!
- eof
- end.
-
-%% Help function which opens a file in raw binary mode and returns
-%% {ok,FileName,FD,Rest} or {error,Reason}.
-open_next_file([]) -> % There are no more files to open.
- done;
-open_next_file([FileName|Rest]) ->
- case file:open(FileName,[read,raw,binary]) of
- {ok,FD} ->
- {ok,FileName,FD,Rest};
- {error,Reason} ->
- {error,{open,[FileName,Reason]}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-
-%% Help function which extract the originating process id from the log entry
-%% term and returns a list of all associations to the PID found in TIalias.
-make_pid_mappings(_,void,_,_) -> % Trace Information is not used.
- []; % Simply no pid mappings then!
-make_pid_mappings(Term,TIalias,TIunalias,TS)
- when element(1,Term)==trace;element(1,Term)==trace_ts ->
- Pid=element(2,Term), % The pid.
- TempAliases=find_aliases(ets:lookup(TIalias,Pid),TS),
- remove_expired_aliases(TempAliases,TIalias,TIunalias,TS),
- lists:map(fun({_,_,Alias})->Alias end,
- find_aliases(ets:lookup(TIalias,Pid),TS));
-make_pid_mappings(_Term,_TIalias,_TIunalias,_TS) -> % Don't understand Term.
- []. % Simply no translations then!
-
-%% Help function traversing a list of ets-alias-table entries and returning a
-%% list of those old enough to have happend before TS.
-%% Note that it is possible to have an Offset in microseconds. This because an
-%% association may end up in the ti-file a short time after logentries starts
-%% to appear in the log file for the process in question. We therefore like to
-%% allow some slack,
-find_aliases(List,TS) ->
- lists:filter(fun({_,Now,_}) when Now<TS -> true;
- (_) -> false
- end,
- List).
-%% ------------------------------------------------------------------------------
-
-%% Help function which removes aliases that are no longer valid from the
-%% ETS table. It uses unalias entries which are older than TS but younger than
-%% the alias association.
-%% Returns nothing significant.
-remove_expired_aliases([{Pid,Now1,Alias}|Rest],TIalias,TIunalias,TS) ->
- Candidates=ets:lookup(TIunalias,Alias),
- lists:foreach(fun({_,Now2,P})
- when (Now2>Now1) and
- (Now2<TS) and
- ((P==Pid) or (not(is_pid(P)))) ->
- ets:delete_object(TIalias,{Pid,Now1,Alias}),
- true; % This alias is infact no longer.
- (_) ->
- false
- end,
- Candidates),
- remove_expired_aliases(Rest,TIalias,TIunalias,TS);
-remove_expired_aliases([],_,_,_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-find_timestamp_in_term({trace_ts,_,_,_,TS}) ->
- TS;
-find_timestamp_in_term({trace_ts,_,_,_,_,TS}) ->
- TS;
-find_timestamp_in_term(_) -> % Don't know if there is a timestamp.
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Help function handling a trace-information file and building the TIstruct storage.
-%% -----------------------------------------------------------------------------
-
-%% Help function which opens a standard ti-file, reads its content and
-%% builds two ETS-table where PID is primary index in the one for aliases, and
-%% the alias is primary index in the one for unalias.
-%% Returns a handle to the two ETS tables.
-%%
-%% This function currently handles:
-%% (1) plain straight raw binary files.
-handle_ti_file(FileStruct) ->
- case lists:keysearch(ti_log,1,FileStruct) of
- {value,{_,[FileName]}} when is_list(FileName) -> % There is one ti-file in this set.
- case file:open(FileName,[read,raw,binary]) of
- {ok,FD} ->
- TIdAlias=ets:new(list_to_atom("inviso_ti_atab_"++pid_to_list(self())),
- [bag]),
- TIdUnalias=ets:new(list_to_atom("inviso_ti_utab_"++pid_to_list(self())),
- [bag]),
- handle_ti_file_2(FD,TIdAlias,TIdUnalias), % Fill the table.
- file:close(FD),
- {TIdAlias,TIdUnalias};
- {error,_Reason} -> % Hmm, unable to open the file.
- {void,void} % Treat it as no ti-file.
- end;
- {value,_} -> % Some other file-set.
- {void,void}; % Pretend we don't understand.
- false -> % No ti-file in this set.
- {void,void}
- end.
-
-handle_ti_file_2(FD,TIdAlias,TIdUnalias) ->
- case file:read(FD,5) of % First read the header.
- {ok,<<_,Size:32>>} ->
- case file:read(FD,Size) of % Read the actual term.
- {ok,Bin} when size(Bin)=:=Size ->
- try binary_to_term(Bin) of
- {Pid,Alias,alias,NowStamp} -> % Save this association.
- ets:insert(TIdAlias,{Pid,NowStamp,Alias}),
- handle_ti_file_2(FD,TIdAlias,TIdUnalias);
- {Pid,Alias,unalias,NowStamp} ->
- ets:insert(TIdUnalias,{Alias,NowStamp,Pid}),
- handle_ti_file_2(FD,TIdAlias,TIdUnalias);
- _Term -> % Don't understand!
- handle_ti_file_2(FD,TIdAlias,TIdUnalias)
- catch
- error:_Reason -> % Badly formatted term
- handle_ti_file_2(FD,TIdAlias,TIdUnalias)
- end;
- {ok,_JunkBin} -> % To short probably.
- handle_ti_file_2(FD,TIdAlias,TIdUnalias); % Just drop it.
- eof -> % Should not come here, but
- {TIdAlias,TIdUnalias} % not much we can do, drop it and stop.
- end;
- {ok,_} -> % Also an error.
- handle_ti_file_2(FD,TIdAlias,TIdUnalias);
- eof -> % This is the normal eof point.
- {TIdAlias,TIdUnalias}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Help functions sorting out what kind of logfiles we have to deal with.
-%% -----------------------------------------------------------------------------
-
-%% Help function which takes the filestruct argument and retrieves the names
-%% of all log-files mentioned there. If there are several logfiles, this function
-%% sorts them beginning with the oldest. That means that this function must
-%% have knowledge of how wrap-sets and so on works.
-%% Today known set-types:
-%% (1) file: One plain file.
-%% (2) wrap_set: List of files belonging to a wrap-set. Must be sorted.
-handle_logfiles(FileStruct) ->
- handle_logfiles_2(lists:keysearch(trace_log,1,FileStruct)).
-
-handle_logfiles_2({value,{_,[FileName]}}) when is_list(FileName)-> % One single plain file.
- [FileName];
-handle_logfiles_2({value,{_,Files}}) when is_list(Files) -> % A wrap-set.
- handle_logfile_sort_wrapset(Files);
-handle_logfiles_2(_) ->
- []. % Pretend there were no files otherwise.
-
-%% Help function which sorts the files in WrapSet beginning with the oldest.
-%% It assumes that a logfile is Name++SeqNo++Suffix.
-%% First the Name and Suffix must be established. We look at all files to find
-%% that out.
-%% Returns a list of sorted filenames.
-%% This function is exported since it might turn useful in own implemented
-%% readers.
-handle_logfile_sort_wrapset(Set=[_FileName]) -> % Only one file! Done then :-)
- Set;
-handle_logfile_sort_wrapset([]) -> % Also pretty simple :-)
- [];
-handle_logfile_sort_wrapset(FileSet) ->
- Prefix=find_common_prefix(FileSet),
- Suffix=find_common_prefix(lists:map(fun(Str)->lists:reverse(Str) end,FileSet)),
- find_hole_in_wrapset(FileSet,length(Prefix),length(Suffix)).
-
-%% Help function which finds the longest common prefix of all strings in the
-%% argument-list. Returns that string.
-find_common_prefix(Files=[[FirstChar|_]|_]) ->
- find_common_prefix_2(Files,FirstChar,[],[]);
-find_common_prefix([_|_]) -> % Means that prefix is "".
- "".
-
-find_common_prefix_2([[CurrChar|RestString]|Rest],CurrChar,Files,RevPrefix) ->
- find_common_prefix_2(Rest,CurrChar,[RestString|Files],RevPrefix);
-find_common_prefix_2([_String|_],_CurrChar,_Files,RevPrefix) ->
- lists:reverse(RevPrefix); % Found a difference.
-find_common_prefix_2([],CurrChar,Files=[[FirstChar|_]|_],RevPrefix) ->
- find_common_prefix_2(Files,FirstChar,[],[CurrChar|RevPrefix]);
-find_common_prefix_2([],CurrChar,_,RevPrefix) ->
- lists:reverse([CurrChar|RevPrefix]). % Actually, prefix was entire string!
-
-%% Help function which returns a sorted list of FileSet with the oldest first.
-find_hole_in_wrapset(FileSet,PreLen,SufLen) ->
- NumberedFiles=find_hole_in_wrapset_2(FileSet,PreLen,SufLen),
- find_hole_in_wrapset_3(lists:sort(NumberedFiles),0,[]). % Wrap-sets start at 0.
-
-find_hole_in_wrapset_2([FileName|Rest],PreLen,SufLen) ->
- [{list_to_integer(lists:sublist(FileName,PreLen+1,length(FileName)-PreLen-SufLen)),
- FileName}|
- find_hole_in_wrapset_2(Rest,PreLen,SufLen)];
-find_hole_in_wrapset_2([],_,_) ->
- [].
-
-find_hole_in_wrapset_3([{N,FileName}|Rest],N,Acc) ->
- find_hole_in_wrapset_3(Rest,N+1,[FileName|Acc]);
-find_hole_in_wrapset_3([{_,FileName}|Rest],_N,Acc) -> % FileName is the oldest one.
- [FileName|lists:map(fun({_,FN})->FN end,Rest)]++lists:reverse(Acc);
-find_hole_in_wrapset_3([],_,Acc) -> % Means all were in order.
- lists:reverse(Acc).
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/inviso/src/inviso_tool.erl b/lib/inviso/src/inviso_tool.erl
deleted file mode 100644
index 7d3cfb9da0..0000000000
--- a/lib/inviso/src/inviso_tool.erl
+++ /dev/null
@@ -1,3255 +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%
-%%
-%% Description:
-%% The inviso_tool implementation. A tool that uses inviso.
-%%
-%% Authors:
-%% Lennart Öhman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool).
-
-
-%% This is the inviso tool, which is a tool using the inviso trace application.
-%% It is developed to make tracing using trace cases possible in an environment
-%% of distributed Erlang nodes.
-%% A current restriction is that the Erlang nodes are supposed to have the same
-%% code. This since inviso tool can at this point not handle subsets of nodes.
-%% Instead all participating Erlang nodes are treated the same.
-%%
-%% The main functionality of the inviso tool are:
-%%
-%% (1) Handles start and stop of tracing at participating nodes.
-%% (2) Interprets trace-case files at a distributed network level.
-%% (The inviso runtime component is responsible for interpreting
-%% trace cases at a local level, if run in an autostart).
-%% (3) Keeps a command history log from which:
-%% (a) Sequences easily can be repeated.
-%% (b) Autostart configuration files can be created (understood by the
-%% default inviso autostart mechanism).
-%% (4) Performs reactivation in case tracing is suspended (manually or by
-%% an overload mechanism).
-%% (5) Can reconnect crashed nodes and by using the history bringing them
-%% up to speed.
-
-%% Distributed Erlang
-%% ------------------
-%% Inviso is built to run in a distributed environment.
-%% The inviso tool can also be used in a non distributed environment.
-
-%% Short description
-%% -----------------
-%% Start-up of the inviso tool
-%% During the start-up of the tool, the tool starts runtime components at
-%% all participating nodes. A runtime component can already be running at
-%% a particular node and will then simply be adopted.
-%%
-%% Session
-%% A session is said to start when tracing is initiated, and ends when
-%% made to stop by the user. When a session is stopped, tracing is stopped
-%% at all participating nodes. Note that participating nodes may come and
-%% go though the time-frame of a session. That means that if a node is
-%% reconnected it may resume its tracing in the current session through
-%% a 'restart_session'. A runtime component that is already tracing at the
-%% time start-session will simply be part of the session without its
-%% ingoing tracing being changed.
-%%
-%% Reactivation
-%% A node that is suspended can be reactivated to resume tracing. Note that
-%% tracing has in this situation never been stopped at the node in question.
-%% The inviso tool resumes the node and applies the history to it.
-%%
-%% Reconnect
-%% A node that is "down" from the inviso tool's perspective can be
-%% reconnected. During reconnection the tool restarts the runtime component
-%% at that node but does not (re)initiate tracing. The latter is called
-%% restart_session and must be done explicitly, unless the node in question
-%% is in fact already tracing. If the node is already tracing (due to an autostart
-%% for instance), it automatically becomes part of the ongoing session (if
-%% there is an ongoing session).
-%%
-%% Restart Session
-%% A node that has been down and has been reconnected can be made to
-%% initialize and resume its tracing. This is done by starting the session
-%% at the node in question and redoing the current history.
-
-%% Trace files within a session
-%% Since it is possible to init-tracing (from an inviso perspective) several
-%% times within the same session, a session may leave several trace log files
-%% behind. This must be resolved by the tracer data generator function
-%% (user supplied) by marking filenames in a chronological order but still
-%% making them possible to identify as part of the same session
-
-
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/0,start/1,stop/0,stop/1]).
--export([reconnect_nodes/0,reconnect_nodes/1,
- start_session/0,start_session/1,
- reinitiate_session/0,reinitiate_session/1,
- restore_session/0,restore_session/1,restore_session/2,
- stop_session/0,
- reset_nodes/0,reset_nodes/1,
- atc/3,sync_atc/3,sync_atc/4,
- sync_rtc/2,sync_rtc/3,
- dtc/2,sync_dtc/2,sync_dtc/3,
- inviso/2]).
--export([reactivate/0,reactivate/1,
- save_history/1,
- get_autostart_data/1,get_autostart_data/2,
- get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
--export([flush/0,flush/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Debug exports.
-%% -----------------------------------------------------------------------------
-
--export([get_loopdata/0]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% OTP exports and call backs.
-%% -----------------------------------------------------------------------------
-
--export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([tc_executer/4,reactivator_executer/6]).
--export([std_options_generator/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
-%% Defines the inviso function calls that shall be possible to do through the
-%% inviso API in this tool.
--define(INVISO_CMDS,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0},
- {suspend,1},{cancel_suspension,0}]).
-%% -----------------------------------------------------------------------------
-
-%% These inviso functions shall be included in the command history log. Others
-%% are not relevant to be redone during a recactivation, a restart session or
-%% exported to an autostart file.
--define(INVISO_CMD_HISTORY,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0}]).
-%% -----------------------------------------------------------------------------
-
-%% Since many function calls to inviso may take long time, especially if they
-%% involve difficult and many trace patterns to set, the default gen_server:call
-%% time out can not be used. We just do not want to get stuck for ever if some
-%% error occurs.
--define(CALL_TIMEOUT,60000).
-
-%% Default max time to wait for a trace case called synchronously to return.
--define(SYNC_TC_TIMEOUT,10000).
-
-%% Runtime components shall terminate when the tool terminates.
--define(DEFAULT_DEPENDENCY,{dependency,0}).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definitions.
-%% -----------------------------------------------------------------------------
-
-%% The loopdata record.
--record(ld,{
- dir=".", % Working dir of the tool.
- nodes=down, % The nodesD database, defaults to non-distr.
- c_node, % Location of inviso_c.
- c_pid, % The inviso control component.
- regexp_node, % Node for regexp expansions.
- tc_dict, % Trace case definition db.
- chl, % Command history log.
- session_state=passive, % passive | tracing
- tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
- tracer_data, % Current session nr and TDGargs.
- reactivators=[], % Pids of now running reactivators.
- tc_def_file, % Trace case definition file.
- optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
- initial_tcs=[], % Initial trace cases.
- started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
- history_dir, % File path for history file.
- keep_nodes=[], % Nodes that shall not be cleared when stopping.
- debug=false % Internal debug mode
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API
-%% =============================================================================
-
-%% start()={ok,Pid} | {error,{already_started,pid()}}
-%% start(Config)
-%% Config=[{Opt,Value},...], list of tuple options.
-%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
-%% history_dir|keep_nodes
-%% Starts the inviso_tool process. Options in Config are the same as those
-%% which are kept in the #ld structure.
-start() ->
- start([]).
-start(Config) ->
- gen_server:start({local,?MODULE},?MODULE,Config,[]).
-%% -----------------------------------------------------------------------------
-
-%% stop(UntouchedNodes)=
-%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
-%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason} | patterns_untouched
-%% Stops the inviso tool and the inviso control component. Runtime components are
-%% stopped by them selves depending on their dependcy of the control component.
-%% All runtime components that are not marked as to be kept will have their
-%% trace patterns cleared before the inviso control component is shutdown.
-%% The NodeResults indicates which nodes were successfullt handled.
-stop() ->
- stop([]).
-stop(UntouchedNodes) ->
- gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
-%% reconnect_nodes(Nodes)={ok,NodesResults}
-%% NodesResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}} | {error,NReason}
-%% State=tracing | inactive
-%% Status=running | suspended
-%% NReason=unknown_node | already_connected | down
-%% (Re)starts the inviso runtime components at Nodes. Depending on its state
-%% (new,idle or tracing) and if the tool is running a session or not, it becomes
-%% part of the tool's ongoing session. If the newly reconnected node is not
-%% tracing but the tool runs a session, the node must be reinitiated to become
-%% tracing.
-reconnect_nodes() ->
- gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
-reconnect_nodes(Node) when is_atom(Node) ->
- reconnect_nodes([Node]);
-reconnect_nodes(Nodes) when is_list(Nodes) ->
- gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% start_session(MoreTDGargs)=
-%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
-%% tracer data generator function.
-%% SessionNr=integer(), trace sessions are numbered by the tool.
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
-%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
-%% Initiates tracing at all participating nodes.
-start_session() ->
- start_session([]).
-start_session(MoreTDGargs) ->
- gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
-%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
-%% of the tool or not in state inactive will be marked as failing by the
-%% tool in the InvisoReturn.
-%% To reinitate a node means to (inviso) init tracing at it according to saved
-%% tracer data generator arguments for the current session and then redo the current
-%% history to bring it up to speed. Note that the tool must be running a session
-%% for reinitiate to work.
-reinitiate_session() ->
- gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
-reinitiate_session(Nodes) ->
- gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% restore_session()=
-%% restore_session(MoreTDGargs)=
-%% restore_session(FileName)=
-%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% The two first clauses will start a new session using the last history. This
-%% implies that there must have been a session running prior.
-%% The two last clauses starts a session and reads a history file and executes the
-%% tracecases in it at all inactive nodes.
-%% In both cases the reused or read history becomes the current histoy, just if the
-%% session had been initiated manually. The tool may not
-%% have a session ongoing, and nodes already tracing (nodes which were adopted)
-%% are not effected. Just like when starting a session manually.
-restore_session() ->
- restore_session([]).
-restore_session([]) -> % This cant be a filename.
- gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
-restore_session(FileNameOrMoreTDGargs) ->
- case is_string(FileNameOrMoreTDGargs) of
- true -> % Interpret it as a filename.
- restore_session(FileNameOrMoreTDGargs,[]);
- false -> % The we want to use last session history!
- gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
- end.
-restore_session(FileName,MoreTDGargs) ->
- gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
-%% SessionNr=integer()
-%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
-%% NodeResult=ok | {error,Reason}
-%% NonDistributedNodeResult=[ok] | []
-%% Stops inviso tracing at all participating nodes. The inviso runtime components
-%% will go to state idle. It is now time to fetch the logfiles. Will most often
-%% succeed. Will only return an error if the entire inviso call returned an
-%% error. Not if an individual node failed stop tracing successfully.
-%% Any running trace case, including reactivator processes will be terminated.
-stop_session() ->
- gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reset_nodes()=NodeResult | {error,Reason}
-%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
-%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
-%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
-%% must not be having a running session.
-reset_nodes() ->
- gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
-reset_nodes(Nodes) ->
- gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% atc(TC,Id,Vars)=ok | {error,Reason}
-%% TC=atom(), name of the trace case.
-%% Id=term(), given name of this usage of TC.
-%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
-%% Function activating a trace case. The trace case must be defined in the
-%% trace case dictionary. The 'ok' return value is only a signal that the
-%% trace case has started successfully. It may then run for as long as it is
-%% programmed to run. An erroneous return value does not necessarily mean that
-%% the trace case has not been executed. It rather means that is undetermined
-%% what happend.
-atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
-%% sync_atc(TC,Id,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As atc/3 but waits for the trace case to finish.
-sync_atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_atc(TC,Id,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_rtc(TC,Vars)=Result | {error,Reason}
-%% sync_rtc(TC,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
-%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
-sync_rtc(TC,Vars) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_rtc(TC,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% dtc(TC,Id)=ok | {error,Reason}
-%% Deactivates a previosly activated trace case. This function can only be used
-%% on trace cases that has a deactivation defined in the trace case dictionary.
-%% There is of course really no difference between a file containing an activation
-%% compared to a deactivation. But to be able cancelling activations out from the
-%% history log, a defined deactivation is essential.
-%% As with activation, the returned 'ok' simply indicates the start of the trace
-%% case.
-dtc(TC,Id) ->
- gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_dtc(TC,Id)=Result | {error,Reason}
-%% sync_dtc(TC,Id,TimeOut)=
-%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
-%% parameters.
-sync_dtc(TC,Id) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_dtc(TC,Id,TimeOut) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% inviso(Cmd,Args)=Result
-%% Cmd=atom(), the (inviso) function name that shall be called.
-%% Args=list(), the arguments to Cmd.
-%% Result=term(), the result from the inviso function call.
-%% This function executes a Cmd in the inviso tool context. The inviso call will
-%% be logged in history log and thereby repeated in case of a reactivation.
-%% Note that this function is intended for use with inviso function API without
-%% specifying any nodes, since the function call is supposed to be carried out on
-%% all nodes.
-%% When these functions are written to an autostart config file by the tool there
-%% is supposed to be a translation to inviso_rt functions.
-inviso(Cmd,Args) ->
- gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reactivate()=ok | {error,Reason}
-%% reactivate(Node)=ok | {error,Reason}
-%% Moves a runtime component from suspended to the state running. This can be
-%% done for both tracing and inactive nodes. The later is necessary since you
-%% may have stopped tracing with a node suspended.
-%% In case the node is tracing, commands in the command history log are redone at
-%% the node in questions.
-%% Note that this function returns 'ok' before the node is running. This because the
-%% the reactivated history is done by a separate process and there is no guarantee
-%% when it will be ready. The reactivated node will not be marked as running in
-%% the tool until done reactivating.
-%% Further it is important to understand that if there are "ongoing" tracecases
-%% (i.e tracecase scripts that are currently executing) and this node was running
-%% at the time that tracecase script started to execute, the list of nodes bound
-%% to the Nodes variable in that script executer includes this node. Making it
-%% no longer suspended makes it start executing inviso commands from where ever
-%% such are called. Hence the reactivation may be interferred by that tracecase.
-reactivate() -> % Non-distributed API.
- reactivate(node()).
-reactivate(Node) ->
- gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
-%% Saves the currently collected command history log to a file. The file will
-%% be a binary-file. If FileName is an absolute path, it will be saved to that
-%% file. Otherwise the history dir will be used. If no history dir was specified
-%% the tool dir will be used, prepended to FileName.
-save_history(FileName) ->
- gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
-%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
-%% Dependency=inviso dependency parameter which will be used for every
-%% autostarted runtime component (included in Options).
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
-%% Options=add_nodes options to the inviso runtime component.
-%% M,F=atom(), the module and function for tracerdata generation.
-%% CompleteTDGargs=list(), all arguments as they are given to the tracer
-%% data generator function.
-%% AutostartData=[CaseSpec,...]
-%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
-%% FileName=string(), pointing out the trace case file. Note that this
-%% is the same as the path used by the tool.
-%% Bindings=Var bindings used according to the history for the
-%% invocation.
-%% M,F=atom(), the function that shall be called (normally some inviso).
-%% Args=list(), the actual arguments. Note that this may contain things
-%% which can not be written to file (ports, pids,...).
-%% Function returning information on how to autostart a node to make it trace
-%% according to the current history. The inviso_tool does not know how to write
-%% the necessary files at the nodes in question. That must be done by the user
-%% of the tool, guided by the return value from this function.
-%% Note that there will be two types of trace case files. Regular trace case
-%% files and binaries returned from this function. The latter contains the
-%% inviso commands which have been executed. Note that the order amongst the
-%% trace cases and binaries is of importance (otherwise they will be redone in
-%% an incorrect order).
-get_autostart_data(Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
-get_autostart_data(Nodes,Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_activities()={ok,Ongoing} | {error,Reason}
-%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
-%% TraceCases={tracecases,TraceCaseList}
-%% TraceCaseList=[{{TCname,Id},Phase},...]
-%% Phase=activating | deactivating
-%% Reactivators={reactivating_nodes,ReactivatingNodes}
-%% ReactivatingNodes=[Node,...]
-%% Returns a list of assynchronous tracecases and nodes doing reactivation at
-%% this momement. This can be useful to implement "home brewn" synchronization,
-%% waiting for the runtime components to reach a certain state.
-get_activities() ->
- gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_status(Node)={ok,StateStatus} | {error,Reason}
-%% StateStatus={State,Status} | reactivating | down
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-get_node_status() ->
- get_node_status(local_runtime).
-get_node_status(Node) ->
- gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
-%% Status=tracing | not_tracing, info about current/last session.
-%% SessionNr=integer()
-%% TDGargs=list(), list of the arguments that will be given to the tracer data
-%% generator function (not including the leading Nodes list).
-%% Returns data about the current or last session.
-get_session_data() ->
- gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
-%% flush(Nodes)={ok,NodesResults} | {error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Makes runtime components flush their trace ports.
-flush() ->
- gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
-flush(Nodes) ->
- gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_loopdata()=#ld
-%% Debug API returning the internal loopdata structure. See #ld above for details.
-get_loopdata() ->
- gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal APIs.
-%% -----------------------------------------------------------------------------
-
-%% tc_executer_reply(To,Reply)=nothing significant
-%% To=pid()
-%% Reply=term()
-%% Internal API used by a trace case executer process to signal its completion.
-tc_executer_reply(To,Reply) ->
- gen_server:cast(To,{tc_executer_reply,Reply}).
-%% -----------------------------------------------------------------------------
-
-%% Internal API used by a reactivator process indicating it is done with the
-%% history log it has got so far.
-%% Timeout set to infinity since the tool may be busy, then the reactivator just
-%% have to wait. If the tool crashes the reactivator will be go down too automatically.
-reactivator_reply(TPid,Counter) ->
- gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% gen_server implementation.
-%% =============================================================================
-
-init(Config) ->
- case fetch_configuration(Config) of % From conf-file and Config.
- {ok,LD} when is_record(LD,ld) ->
- case start_inviso_at_c_node(LD) of
- {ok,CPid} ->
- LD2=start_runtime_components(LD),
- LD3=read_trace_case_definitions(LD2),
- process_flag(trap_exit,true),
- start_subscribe_inviso_events(LD3#ld.c_node),
- {ok,LD3#ld{c_pid=CPid}};
- {error,Reason} -> % Most likely already running.
- {stop,{error,Reason}}
- end;
- {error,Reason} ->
- {stop,{error,{start_up,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the inviso control component at node c_node, or "here"
-%% if it is not a distributed network.
-start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
- case inviso:start() of
- {ok,Pid} ->
- {ok,Pid};
- {error,Reason} ->
- {error,Reason}
- end;
-start_inviso_at_c_node(#ld{c_node=CNode}) ->
- case rpc:call(CNode,inviso,start,[]) of
- {ok,Pid} ->
- {ok,Pid};
- {error,{already_started,_}} -> % A control component already started.
- {error,{inviso_control_already_running,CNode}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the runtime components at all particapting nodes.
-%% It also updates the nodes structure in the #ld to indicate which nodes where
-%% successfully started. Returns a new #ld.
-%% Note that a runtime component may actually be running at one or several nodes.
-%% This is supposed to be the result of an (wanted) autostart. Meaning that the
-%% inviso tool can not handle the situation if a runtime component is not doing
-%% what it is supposed to do. In case a runtime component is already running it
-%% will be adopted and therefore marked as running.
-start_runtime_components(LD=#ld{c_node=undefined}) ->
- start_runtime_components_2(local_runtime,undefined,LD);
-start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
- start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
-start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
- start_runtime_components_2(Nodes,CNode,LD).
-
-start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(local_runtime,OptG),
- case inviso:add_node(mk_rt_tag(),Opts) of
- {ok,NAnsw} -> % Should be more clever really!
- NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
- LD#ld{nodes=NewNodesD};
- {error,_Reason} ->
- LD
- end;
-start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(Node,OptG),
- case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
- {ok,NodeResults} ->
- NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
- start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
- {error,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD);
- {badrpc,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD)
- end;
-start_runtime_components_2([],_,LD) ->
- LD.
-
-start_runtime_components_mk_opts(Node,{M,F,Args}) ->
- case catch apply(M,F,[Node|Args]) of
- {ok,Opts} when is_list(Opts) ->
- start_runtime_component_mk_opts_add_dependency(Opts);
- _ ->
- [?DEFAULT_DEPENDENCY]
- end.
-
-%% The options generator is not supposed to generate the dependency. Hence this
-%% function adds and if necessary removes an incorrectly added dependency tag.
-start_runtime_component_mk_opts_add_dependency(Opts) ->
- case lists:keysearch(dependency,1,Opts) of
- {value,_} -> % Not allowed!!!
- [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
- false ->
- [?DEFAULT_DEPENDENCY|Opts]
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function subscribing to inviso events from the inviso controller. This
-%% will make it possible to follow runtime components going down.
-start_subscribe_inviso_events(undefined) ->
- inviso:subscribe();
-start_subscribe_inviso_events(CNode) ->
- rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% gen_server handle call back functions.
-%% -----------------------------------------------------------------------------
-
-handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
- when is_list(UntouchedNodes) ->
- {stop,
- normal,
- remove_all_trace_patterns(CNode,
- UntouchedNodes++KeepNodes,
- get_available_nodes(NodesD)),
- LD};
-handle_call({stop,BadArg},_From,LD) ->
- {reply,{error,{badarg,BadArg}},LD};
-
-handle_call({reconnect_nodes,Nodes},_From,LD) ->
- case h_reconnect_nodes(Nodes,LD) of
- {ok,{Nodes2,NodesErr,NewLD}} ->
- if
- Nodes==local_runtime ->
- {reply,
- build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
- NewLD};
- is_list(Nodes) ->
- {reply,
- {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
- NewLD}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
-
-handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % No session running.
- if
- is_list(MoreTDGargs) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
- NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
- Nodes2,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true -> % Faulty TDGargs.
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true -> % The tool must be tracing.
- {M,F,_Args}=LD#ld.tdg,
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
- {ok,{NodesErr,ReturnVal,NewLD}} ->
- {reply,
- {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
- NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Must have a running session!
- {reply,{error,no_session},LD}
- end;
-
-handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
- when is_list(MoreTDGargs) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case catch make_absolute_path(FileName,LD#ld.dir) of
- AbsFileName when is_list(AbsFileName) ->
- case file:read_file(AbsFileName) of
- {ok,Bin} ->
- if
- is_list(MoreTDGargs) ->
- case catch replace_history_chl(OldCHL,
- binary_to_term(Bin)) of
- {ok,CHL} -> % The file was well formatted.
- case h_restore_session(MoreTDGargs,
- LD#ld{chl=CHL}) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=
- tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- Error -> % Badly formatted file.
- {reply,
- {error,{bad_file,{AbsFileName,Error}}},
- LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- {error,Reason} ->
- {reply,{error,{read_file,Reason}},LD}
- end;
- Error ->
- {reply,{error,{bad_filename,{FileName,Error}}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-%% This is doing restore session on the current history.
-handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case history_exists_chl(CHL) of
- true -> % There is a history to redo.
- if
- is_list(MoreTDGargs) ->
- case h_restore_session(MoreTDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- false ->
- {reply,{error,no_history},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-%% To stop tracing means stop_tracing through the inviso API. But we must also
-%% remove any help processes executing inviso commands (trace case executers
-%% and reactivators).
-%% Note that to be really sure we should actually wait for EXIT-signals from those
-%% processes before returning a successful returnvalue to the caller. In theory
-%% those processes could issue an inviso call effecting a new trace session started
-%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
-%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
-%% if the node is tracing or not
-handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
- case is_tracing(SState) of
- true ->
- NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
- NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
- case h_stop_session(LD) of
- {ok,{SessionNr,Result}} ->
- NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
- {reply,
- {ok,{SessionNr,Result}},
- LD#ld{session_state=passive_sessionstate(),
- nodes=NewNodesD,
- chl=NewCHL,
- reactivators=NewReAct,
- started_initial_tcs=[]}};
- {error,Reason} -> % Now we're really in deep shit :-)
- {reply,{error,{unrecoverable,Reason}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % We can not be in a session.
- {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
- true ->
- {reply,{error,session_active},LD}
- end;
-
-%% Calling a trace-case, or "turning it on".
-handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_atc(TC,Id,Vars,LD) of
- {ok,NewLD} -> % Trace case executed.
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_rtc(TC,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-
-handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_dtc(TC,Id,LD) of
- {ok,NewLD} ->
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- if
- is_integer(TimeOut);TimeOut==infinity ->
- case h_sync_dtc(TC,Id,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- is_list(Args) ->
- case h_inviso(Cmd,Args,LD) of
- {ok,{Reply,NewLD}} ->
- {reply,Reply,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,Args}},LD}
- end;
- false -> % Can't do if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
- case get_state_nodes(Node,NodesD) of
- {trace_failure,_} ->
- {reply,{error,trace_failure},LD};
- {State,suspended} -> % The node is infact suspended.
- case h_reactivate(Node,CNode) of
- ok ->
- case {State,is_tracing(LD#ld.session_state)} of
- {tracing,true} -> % Only then shall we redo cmds.
- {reply,ok,redo_cmd_history(Node,LD)};
- _ -> % All other just no longer suspended.
- {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- reactivating ->
- {reply,{error,reactivating},LD};
- {_,running} ->
- {reply,{error,already_running},LD};
- down ->
- {reply,{error,not_available},LD};
- false ->
- {reply,{error,unknown_node},LD}
- end;
-
-handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
- case lists:keysort(2,get_loglist_chl(CHL)) of
- [] -> % Empty history or no history.
- {reply,{error,no_history},LD};
- Log ->
- case h_save_history(HDir,Dir,FileName,Log) of
- {ok,AbsFileName} ->
- {reply,{ok,AbsFileName},LD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end
- end;
-
-handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
- {ok,ASD} = build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict),
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- {M,F,_}=LD#ld.tdg,
- OptsG=LD#ld.optg, % Addnodes options generator.
- {reply,
- h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
- LD};
-
-handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
- handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
-handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
- Nodes=get_all_nodenames_nodes(NodesD),
- handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
-
-handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
- TraceCases=get_ongoing_chl(CHL),
- RNodes=get_all_nodes_reactivators(Reactivators),
- ReturnList1=
- if
- TraceCases==[] ->
- [];
- true ->
- [{tracecases,TraceCases}]
- end,
- ReturnList2=
- if
- RNodes==[] ->
- ReturnList1;
- true ->
- [{reactivating_nodes,RNodes}|ReturnList1]
- end,
- {reply,{ok,ReturnList2},LD};
-
-handle_call({get_node_status,Node},_Node,LD) ->
- case get_state_nodes(Node,LD#ld.nodes) of
- false ->
- {reply,{error,unknown_node},LD};
- StateStatus ->
- {reply,{ok,StateStatus},LD}
- end;
-
-handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
- case get_latest_session_nr_tracer_data(TD) of
- undefined ->
- {reply,{error,no_session},LD};
- SessionNr ->
- TDGargs=get_latest_tdgargs_tracer_data(TD),
- case is_tracing(SState) of
- true ->
- {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
- false ->
- {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
- end
- end;
-
-handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- Nodes=get_tracing_nodes(NodesD),
- {reply,h_flush(CNode,Nodes),LD};
-handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
- {reply,h_flush(CNode,Nodes),LD};
-
-handle_call(get_loopdata,_From,LD) ->
- {reply,LD,LD};
-
-%% Internal handle_call callbacks.
-
-handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
- HighestUsedCounter=get_highest_used_counter_chl(CHL),
- if
- HighestUsedCounter>Counter -> % There are now more log entries.
- NewUnsortedLog=get_loglist_chl(CHL),
- {reply,{more,NewUnsortedLog},LD};
- true -> % No Counter is youngest log entry.
- NodesD=LD#ld.nodes,
- Node=get_node_reactivators(RPid,LD#ld.reactivators),
- {reply,
- done,
- LD#ld{nodes=set_running_nodes(Node,NodesD),
- reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Handling a notification from a trace case execution process. Receiving this
-%% indicated that this phase of the trace case is finnished.
-handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
- case Phase of
- activating -> % The trace case is running now.
- {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
- {noreply,NewLD};
- stopping ->
- {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
- {noreply,NewLD};
- _ ->
- {noreply,LD}
- end;
-handle_cast(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% This is the case when a runtime component goes down. We stop all running
-%% reactivators for this node. Note that there can also be tracecases ongoing
-%% where this node is part of the Nodes variable. But there is not much we can
-%% do about that. Other then informing the user that it is unwise to reconnect
-%% this node before those tracecases have stopped being ongoing.
-handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
- {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-%% This is the case when a runtime component gets suspended. Much of the same
-%% problem as described above applies.
-handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
- {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-handle_info(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% Called when the tool server stops. First clause, termination is initiated by
-%% our self and therefore controlled another way. In the second case we are
-%% stopping for some external reason, and we must then do more here in terminate/2.
-terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
- stop_inviso_at_c_node(CNode);
-terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
- remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
- stop_inviso_at_c_node(CNode).
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% =============================================================================
-%% Handler first level help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% reconnect_nodes
-%% -----------------------------------------------------------------------------
-
-%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
-%% the set of nodes handled by the tool. It is not possible to reconnect a node
-%% that is not marked as down. This partly because we otherwise risk losing the
-%% trace_failure state (which can not be rediscovered).
-h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
- _ -> % Allready connected!
- {ok,{[],{error,already_connected},LD}}
- end;
-h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when is_list(Nodes) ->
- {Nodes2,NodesErr}=
- lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- down -> % Yes this node can be reconnected.
- {[N|Nodes2],NodesErr};
- false -> % Not part of the node-set!
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ -> % Allready connected!
- {Nodes2,[{N,{error,already_connected}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes),
- LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
- {ok,{Nodes2,NodesErr,LD2}};
-h_reconnect_nodes(Nodes,_LD) ->
- {error,{badarg,Nodes}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% start_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the tracing at all nodes. Note that the tracer data
-%% is calculated using a user defined function. This is how for instance the
-%% file names (of the log files) are determined.
-%% Before the nodes are initiated their (possibly remaining) trace patterns are
-%% cleared, both local and global.
-h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case get_inactive_running_nodes(NodesD) of
- [] -> % There are no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,[]);
- Nodes -> % List of nodes or 'local_runtime'.
- case h_start_session_ctp_all(CNode,Nodes) of
- {ok,Errors,[]} -> % Now no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,Errors);
- {ok,Errors,Nodes2} -> % Now these nodes are fresh.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} -> % Generated our tracerdata.
- case h_start_session_2(CNode,TracerList,Errors) of
- {ok,ReturnValue} -> % Some nodes are initialized now.
- {NewNodesD,Nodes3}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- {ok,{SessionNr,
- ReturnValue,
- Nodes3, % The nodes that shall get initial tracases.
- LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} -> % Faulty tracer data generator func.
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} -> % Error clearing patterns.
- {error,Reason}
- end
- end.
-
-h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- if
- CNode==undefined ->
- {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
- true ->
- {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
- end.
-
-%% Help function clearing all trace patterns on all nodes.
-h_start_session_ctp_all(CNode,Nodes) ->
- case remove_all_trace_patterns(CNode,[],Nodes) of
- ok -> % Non-distributed case1.
- {ok,[],local_runtime};
- {error,Reason} -> % Non-distributed case2 and general failure.
- {error,Reason};
- {ok,NodeResults} ->
- h_start_session_ctp_all_2(NodeResults,[],[])
- end.
-
-h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
-h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
-h_start_session_ctp_all_2([],Errors,Nodes) ->
- {ok,Errors,Nodes}.
-
-%% Help function doing the actual init_tracing.
-h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
- case inviso:init_tracing(TracerData) of
- {ok,LogResult} when is_list(LogResult) ->
- {ok,{ok,LogResult}};
- {error,already_initated} -> % Perhaps adopted!?
- {ok,{error,already_initiated}}; % Not necessarily wrong.
- {error,Reason} ->
- {error,Reason}
- end;
-h_start_session_2(CNode,TracerList,Errors) ->
- case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
- {ok,NodeResults} ->
- {ok,{ok,Errors++NodeResults}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting all initial trace cases. They are actually handled
-%% the same way as user started trace cases. We actually only start initial
-%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
-%% adopted some nodes some already tracing nodes, and such are supposed to have
-%% the correct patterns and flags set.
-do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
- Id=make_ref(), % Trace case ID.
- case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
- {ok,NewLD} -> % Trace case was successfully started.
- NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
- do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
- {error,_Reason} ->
- do_initial_tcs(Rest,Nodes,LD)
- end;
-do_initial_tcs([_|Rest],Nodes,LD) ->
- do_initial_tcs(Rest,Nodes,LD);
-do_initial_tcs([],_Nodes,LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% This help functio is used instead of do_initial_tcs/3 if there actually are no
-%% nodes to do the trace cases on. The reason we must have this function is that
-%% the tracecases must still be entered into the history with bindings and all.
-%% But we let them be marked as 'running' immediately (no need for the activator
-%% process).
-add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} ->
- Id=make_ref(), % Trace case ID.
- FakeProcH=make_ref(), % Need something to enter as activator.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
- NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
- add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
- started_initial_tcs=NewInitialTcs});
- {error,_Reason} -> % Not much we can do about that.
- add_initial_tcs_to_history(Rest,LD)
- end;
- false ->
- add_initial_tcs_to_history(Rest,LD)
- end;
-add_initial_tcs_to_history([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reinitiate_session
-%% -----------------------------------------------------------------------------
-
-%% Function doing the reinitiation. That means first do init_tracing at the nodes
-%% in question. Then redo the command history to bring them up to speed.
-%% But first the runtime component is cleared of all trace patterns.
-h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- case h_reinitiate_session_2(Nodes,NodesD,CNode) of
- {ok,{[],NodesErr}} -> % No nodes to reinitiate.
- {ok,{NodesErr,{ok,[]},LD}};
- {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} ->
- case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
- {ok,ReturnValue} -> % Ok, now we must redo cmd history.
- {NewNodesD,_Nodes}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
- {ok,{NodesErr,ReturnValue,NewLD}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Help function finding out which nodes in Nodes actually can be reinitiated.
-%% A node must be up, inactive and not suspended in order for this to work. All the
-%% rest is just a matter of how detailed error return values we want to generate.
-h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
- case get_state_nodes(local_runtime,NodesD) of
- {inactive,running} -> % Only ok case.
- case inviso:ctp_all() of
- ok ->
- {ok,{local_runtime,[]}};
- {error,Reason} -> % This is strange.
- {error,Reason}
- end;
- {_,suspended} ->
- {ok,{[],{error,suspended}}};
- down ->
- {ok,{[],{error,down}}};
- _ ->
- {ok,{[],{error,already_in_session}}}
- end;
-h_reinitiate_session_2(Nodes,NodesD,CNode) when is_list(Nodes) ->
- {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- {inactive,running} -> % Only ok case.
- case rpc:call(CNode,inviso,ctp_all,[[N]]) of
- {ok,[{N,ok}]} ->
- {[N|Nodes2],NodesErr};
- {ok,[{N,{error,Reason}}]} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {error,Reason} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {badrpc,Reason} ->
- {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
- end;
- {_,suspended} ->
- {Nodes2,[{N,{error,suspended}}|NodesErr]};
- down ->
- {Nodes2,[{N,{error,down}}|NodesErr]};
- false ->
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ ->
- {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes)};
-h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
- {error,{badarg7,Nodes}}.
-
-%% Help function redoing the command history log at all nodes that actually
-%% started to trace. Note that we do not modify the return value which will be
-%% given to the caller just because we decide not to redo commands. The user
-%% must conclude him self from the inviso return value that commands were not
-%% redone at a particular node.
-h_reinitiate_session_chl(local_runtime,LD) ->
- h_reinitiate_session_chl([local_runtime],LD);
-h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
- case get_state_nodes(Node,NodesD) of
- {tracing,running} -> % Only case when we shall redo!
- h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
- _ -> % No redo of chl in other cases.
- h_reinitiate_session_chl(Rest,LD)
- end;
-h_reinitiate_session_chl([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% restore_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting a session (init tracing) and redoes the history
-%% found in CHL.
-h_restore_session(MoreTDGargs,LD) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
- {ok,{SessionNr,ReturnVal,NewLD}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
- {ok,{SessionNr,ReturnVal,NewLD2}};
- {error,Reason} -> % Risk of out of control.
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% stop_session
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping tracing at tracing nodes.
-h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case h_stop_session_2(CNode,NodesD) of
- {ok,Result} ->
- {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
- {error,Reason} ->
- {error,Reason}
- end.
-
-h_stop_session_2(undefined,NodesD) -> % The non distributed case.
- case get_tracing_nodes(NodesD) of
- {up,{inactive,_}} -> % Already not tracing!
- {ok,[]};
- {up,_} ->
- case inviso:stop_tracing() of
- {ok,_State} ->
- {ok,[ok]};
- {error,no_response} ->
- {ok,[]};
- {error,Reason} ->
- {error,Reason}
- end;
- down ->
- {ok,[]}
- end;
-h_stop_session_2(CNode,NodesD) ->
- Nodes=get_tracing_nodes(NodesD),
- case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
- {ok,NodeResults} ->
- {ok,lists:map(fun({N,{ok,_}})->{N,ok};
- (NodeError)->NodeError
- end,
- NodeResults)};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing any trace flags, trace patterns and meta trace patterns
-%% at Nodes. This will cause the nodes to become "fresh".
-h_reset_nodes(local_runtime,_CNode) ->
- inviso:clear([keep_log_files]);
-h_reset_nodes(Nodes,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
- {ok,NodeResults} ->
- {ok,NodeResults};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% atc
-%% -----------------------------------------------------------------------------
-
-%% Function handling ativating a trace case. Trace cases that do not have a
-%% particular on/off handling (but just on in some scense) are handled here too.
-%% The trace case is entered into the Command History Log.
-%% Note that the trace case can not be executed at this node but must be
-%% executed where the inviso control component is.
-%% Further it is possible to either activated the tracecase for all running and
-%% tracing nodes, or just for a specified list of nodes.
-%% TC=tracecase_name(),
-%% Id=term(), identifiying this usage so we can turn it off later.
-%% Vars=list(), list of variable-value bindnings.
-h_atc(TC,Id,Vars,LD) ->
- h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
-
-h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- if
- is_list(Nodes) -> % Nodes predefined.
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
- true -> % Use all tracing and running nodes.
- Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-
-h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
- {ok,ProcH} = exec_trace_case_on(CNode,TraceCase,Bindings,Nodes),
- %% Trace cases have no return values.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
- {ok,LD#ld{chl=NewCHL}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_atc
-%% -----------------------------------------------------------------------------
-
-h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% rtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling running a trace case without marking it as activated. It
-%% is in the history mearly indicated as activated
-h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% dtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling turning a trace case off. The trace case must be registered
-%% as having an off mechanism. If it has an off mechanism and was previously entered
-%% into the Command History Log and is done with its activation phase, it will be
-%% executed and removed from the CHL.
-h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} ->
- NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_dtc
-%% -----------------------------------------------------------------------------
-
-h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
- NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} -> % Script fault.
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- false ->
- {error,no_deactivation}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% inviso
-%% -----------------------------------------------------------------------------
-
-%% Function executing one inviso command. The returnvalue from the inviso
-%% function call will be the return value to the client. The command is
-%% entered into the history command log.
-%% Note that the inviso call may have to be done at another node, dictated
-%% by the c_node field. Further, if the module name is not an atom it is
-%% most likely a regexp, which must be expanded at the regexp_node. Note
-%% this is only relevant for tp and tpl.
-h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
- Arity=length(Args),
- case check_proper_inviso_call(Cmd,Arity) of
- {true,RegExpFlag} -> % Yes it is an inviso call.
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
- {ok,Result} ->
- case check_inviso_call_to_history(Cmd,Arity) of
- true -> % This function shall be added to chl.
- {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
- false -> % Do not add it.
- {ok,{Result,LD}}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Not an inviso function.
- {error,invalid_function_name}
- end.
-
-h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
- case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
- {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
- {error,{'EXIT',Reason}};
- Result ->
- {ok,Result}
- end;
-h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
- case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
- {ok,NewArgs} ->
- case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- {error,{badrpc,Reason}} -> % Includes runtime failure.
- {error,{badrpc,Reason}};
- Result ->
- {ok,Result}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reactivate
-%% -----------------------------------------------------------------------------
-
-h_reactivate(_Node,undefined) -> % The non-distributed case.
- case inviso:cancel_suspension() of
- ok ->
- ok;
- {error,Reason} ->
- {error,Reason}
- end;
-h_reactivate(Node,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
- {ok,[{Node,ok}]} ->
- ok;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% save_history
-%% -----------------------------------------------------------------------------
-
-h_save_history(HDir,Dir,FileName,SortedLog) ->
- Dir0=
- if
- is_list(HDir) -> % There is a history dir specified.
- HDir; % Use it then.
- true ->
- Dir % Else use the tool dir.
- end,
- case catch make_absolute_path(FileName,Dir0) of
- AbsFileName when is_list(AbsFileName) ->
- Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
- case file:write_file(AbsFileName,term_to_binary(Log2)) of
- ok ->
- {ok,AbsFileName};
- {error,Reason} ->
- {error,{write_file,Reason}}
- end;
- {'EXIT',_Reason} ->
- {error,{bad_filename,FileName}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% get_autostart_data
-%% -----------------------------------------------------------------------------
-
-%% Help function building the structures used when exporting autostart information
-%% from the tool. Note that we remove the tool-dependency and insert the one
-%% specify in the get_autostart_data call.
-h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
- Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
-
-h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when is_list(Nodes) ->
- {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
-h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
- {error,{badarg,Nodes}}.
-
-h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
- Opts0=start_runtime_components_mk_opts(Node,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
- h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
-h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% flush
-%% -----------------------------------------------------------------------------
-
-h_flush(undefined,_Nodes) ->
- inviso:flush();
-h_flush(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% tc_executer_reply
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its activation phase and
-%% shall now be marked in the Command History Log as running.
-h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {activating,{TC,Id}} ->
- case Result of
- {ok,Value} -> % The trace case is successful activated.
- {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
- {error,_} -> % Then pretend it never happend :-)
- {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
- end;
- _ -> % Where did this come from?
- {ok,LD} % Well just ignore it then.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its stopping phase and
-%% shall now be nulled in the Command History Log (meaning that it will not
-%% be repeated in the event of a reactivation).
-h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {stopping,{TC,Id}} ->
- case Result of
- {ok,_Result} -> % _Result is returned from the tracecase.
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
- {error,_} -> % This is difficult, is it still active?
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
- end;
- _ -> % Strange.
- {ok,LD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Terminate.
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping the inviso control component. Does not return
-%% anything significant.
-stop_inviso_at_c_node(undefined) -> % Non distributed case.
- inviso:stop();
-stop_inviso_at_c_node(CNode) ->
- rpc:call(CNode,inviso,stop,[]).
-%% -----------------------------------------------------------------------------
-
-%% Help function that removes all trace patterns from the nodes that are not
-%% marked as such were patterns shall be left after stopping of inviso.
-%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
-%% 'ok' is returned incase of success, ot 'patterns_untouched'.
-remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
- case KeepNodes of
- undefined -> % No, remove patterns from localruntime.
- inviso:ctp_all();
- _ ->
- patterns_untouched
- end;
-remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
- Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
- case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
- {ok,NodeResults} ->
- F=fun(N) ->
- case lists:member(N,KeepNodes) of
- true ->
- {N,patterns_untouched};
- false ->
- case lists:keysearch(N,1,NodeResults) of
- {value,Result} ->
- Result; % {Node,ok}
- false -> % Extremely strange.
- {N,{error,general_error}}
- end
- end
- end,
- {ok,lists:map(F,Nodes)};
- {error,{badrpc,Reason}} ->
- {error,{inviso_control_node_error,Reason}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Second level help functions.
-%% =============================================================================
-
-%% Help function building a reply to a reconnection call based on which nodes
-%% where asked to be reconnected and which of those are actually now working.
-%% We actually make an effort to serve the return value in the same order as the
-%% nodes were mentioned in the original call (Nodes).
-build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {error,down};
- {State,Status} ->
- {ok,{State,Status}}
- end;
-build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
- NodesErr;
-build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
- case lists:member(Node,Nodes2) of
- true -> % Ok, look in the #ld.nodes.
- case get_state_nodes(Node,NodesD) of
- down -> % Somekind of failure, still down.
- [{Node,{error,down}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
- {State,Status} -> % {State,Status}
- [{Node,{ok,{State,Status}}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
- false -> % Error already from the beginning.
- {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
- [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
-build_reconnect_nodes_reply([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function building a return value to reinitiate_session. Nodes contains
-%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
-%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
-build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
- {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
-build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
- NodeResult;
-build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
- NodesErr.
-build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
- case lists:keysearch(Node,1,NodesErr) of
- {value,{_,Error}} ->
- [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
- false ->
- case lists:keysearch(Node,1,NodeResults) of
- {value,Value} ->
- [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
- end
- end;
-build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a history log where stop and stopping entries have
-%% been removed. Further all tracecase log entries must be set to running since
-%% there can not be such a thing as an activating tracecase stored away in a
-%% saved historyfile!
-%% We must also take away any #Ref.
-build_saved_history_data(SortedLog) ->
- CleanedLog=
- lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
- (_) -> true
- end,
- SortedLog),
- lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
- ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
- ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
- ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
- end,
- CleanedLog).
-%% -----------------------------------------------------------------------------
-
-%% This help function builds the AutoStartData structure which is returned from
-%% get_austostart_data. An AutoStartData structure is a list of trace-files and
-%% inviso commands. The order is significant since it is the idea that doing
-%% the trace case files and inviso commands in that order will bring a node to
-%% a certain state in a trace perspective.
-%% Returns {ok,AutoStartData} or {error,Reason}
-build_autostart_data(SortedLog,TCdict) ->
- build_autostart_data_2(SortedLog,TCdict,[]).
-
-build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
- build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
-build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
- build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
-build_autostart_data_2([],_TCdict,Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function placing the filename in the AutoStartData structure.
-build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
- {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
- {ok,FName}=get_tc_activate_fname(TC),
- build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
-%% -----------------------------------------------------------------------------
-
-%% Help function generating tracerdata to init inviso tracing. The generation
-%% is done by the TracerDataGenerator, TDG, function.
-%% Individual tracerdata is generated for each node in Nodes.
-%% Returns {ok,TracerData} or {error,Reason}.
-call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
- case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerData ->
- {ok,TracerData}
- end;
-call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
- case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerList ->
- {ok,TracerList}
- end.
-
-call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
- [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
- call_tracer_data_generator_2(M,F,TDGargs,Rest)];
-call_tracer_data_generator_2(_,_,_,[]) ->
- [].
-
-call_tracer_data_generator_3(M,F,TDGargs,Node) ->
- apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
-
-%% This function creates the arguments that the tracer data generator function
-%% accepts (in an apply call). The reason for making it a sepparate function is
-%% that the arguments are constructed in more situations than just when actually
-%% doing the apply. By having a function it will become obvious where to change
-%% should the arguments change.
-call_tracer_data_generator_mkargs(Node,TDGargs) ->
- inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
-%% -----------------------------------------------------------------------------
-
-%% This function acts as standard options generator function. That is returning
-%% the options argument to inviso:add_node/3. Note that this function must not
-%% return the dependency part of that option.
-std_options_generator(_Node) ->
- []. % No particular options(!)
-%% -----------------------------------------------------------------------------
-
-
-%% Help function checking that Vars contains a binding for every variable
-%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
-%% is disregarded, since it is always added by the inviso_tool.
-%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
-%% according to file:eval functionality.
-check_bindings(Vars,TraceCase) ->
- case catch check_bindings_2(Vars,
- get_tc_varnames(TraceCase),
- erl_eval:new_bindings()) of
- {'EXIT',_Reason} ->
- {error,variable_error};
- {error,Reason} -> % Missing a bindning.
- {error,Reason};
- {ok,Bindings} ->
- {ok,Bindings}
- end.
-
-check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
- check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
-check_bindings_2(Vars,[VarName|Rest],Bindings) ->
- case lists:keysearch(VarName,1,Vars) of
- {value,{_,Val}} ->
- check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
- false -> % Mandatory variable missing.
- {error,{missing_variable,VarName}} % Quite here then.
- end;
-check_bindings_2(_,[],Bindings) ->
- {ok,Bindings}.
-%% -----------------------------------------------------------------------------
-
-%% This help function checks that the command the user tries to do is amongst
-%% the inviso API. It at the same time returns what kind of command it is.
-%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
-%% needs to have its argument modified by module regexp expansion or not.
-check_proper_inviso_call(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMDS) of
- true -> % It is part of inviso API.
- {true,check_proper_inviso_call_regexp(Cmd,Arity)};
- false ->
- false
- end.
-
-%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
-check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking if this inviso command shall be added to the command
-%% history log. Returns true or false.
-check_inviso_call_to_history(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function traversing the arguments and expanding module names stated
-%% as regular expressions. This means that the resulting arguments may be longer
-%% than the orginal ones.
-%% When we run this function it has been determined that we are a distributed
-%% system.
-%% Also note that if there are no regexps in Args, no regexpansion will be
-%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
-%% If RegExpNode is unavailable the nodes found in Nodes will be used until
-%% one that works is found.
-expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
- {ok,Args};
-expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
- case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
- NewPatternList when is_list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
- case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
- NewPatternList when is_list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
- expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
-
-
-expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
- [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
- {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {error,_Reason} ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity,MS,Opts}|
- expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes).
-
-expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
- [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
- {error,_Reason} ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
-%% -----------------------------------------------------------------------------
-
-
-
-%% Help function running the activation of a trace case. Note that this must
-%% be done at the inviso control component's Erlang node *and* that it must be
-%% done in its own process since there is no telling for how long a trace case
-%% may run.
-%% Returns {ok,ActivationHandler}.
-exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- activating)}.
-
-%% Help function running the deactivation of a trace case.
-exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} -> % There is a deactivation.
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- stopping)};
- false ->
- {error,no_deactivation}
- end.
-
-exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
- if
- CNode==undefined -> % The non distributed case.
- spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
- true ->
- spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
- end.
-
-%% This function is run in its own process and is responsible for executing
-%% the trace case.
-tc_executer(TcFName,Bindings,Phase,Parent) ->
- case catch file:script(TcFName,Bindings) of
- {ok,Value} ->
- tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
- {'EXIT',Reason} ->
- tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
- Error ->
- tc_executer_reply(Parent,{Phase,self(),Error})
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a reactivator process redoing command history at
-%% Node. It also updates the loopdata to indicate that Node is now in state
-%% reactivating. It is a good idea to only handle one node per reactivator process.
-%% This because if the node terminates and comes back up, the reactivator must be
-%% stopped.
-redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
- P=start_reactivator(Node,CNode,TCdict,CHL),
- LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
- reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
-
-%% Help function starting a reactivator process replaying the command history log.
-%% Returns a pid of the reactivator process.
-start_reactivator(Node,CNode,TCdict,CHL) ->
- UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
- if
- CNode==undefined -> % The non-distributed case.
- spawn_link(?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]]);
- true ->
- spawn_link(CNode,
- ?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]])
- end.
-
-%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
-%% commands one by one. We wait until one command is finished until we do the
-%% next. Commands marked as nullified are not performed. In fact when a command
-%% is nullified only the stop will be found in the CHL. Its activation will be
-%% removed.
-reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
- SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
- Log=reactivator_skip_log_entries(SortedLog,StartCounter),
- case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
- done ->
- true; % Simply terminate the reactivator then.
- {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
- reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
- end.
-
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
- reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{M,F,Args,_Ref},NextC}|Rest]) ->
- reactivator_executer_cmd(Node,M,F,Args),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
- case lists:member({TCname,Id},DoneCases) of
- true -> % We have activated it, must stop then.
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,_,FNameOff}} ->
- reactivator_executer_tc(Node,Bindings,FNameOff),
- NewDoneCases=lists:delete({TCname,Id},DoneCases),
- reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
- {ok,_} -> % No stop-filename, strange!
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Even stranger, does not exist!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
- false -> % Never activated in the first place.
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
-%% Done all log entries found this lap. See if there are more entries by now.
-reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
- case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
- done -> % No more entries in the CHL.
- done;
- {more,NewUnsortedLog} -> % Repeat the procedure
- {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
- end.
-
-%% This help function activates a tracecase.
-reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-%% Help function executing a trace case in the reactivators context. Does not
-%% return anything significant.
-reactivator_executer_tc(Node,Bindings,FileName) ->
- catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
-
-%% Help function handling trace case that are simply executed - rtc.
-reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
- catch apply(M,F,Args); % Non-distributed.
-reactivator_executer_cmd(Node,M,F,Args) ->
- catch apply(M,F,[[Node]|Args]).
-
-%% Help function returning a list of log entries missing the first entries
-%% having a counter less or equal to C1.
-reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries(Log,_) ->
- Log.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning the node name to use in an rpc call.
-get_rpc_nodename(undefined) ->
- node();
-get_rpc_nodename(CNode) ->
- CNode.
-%% -----------------------------------------------------------------------------
-
-mk_rt_tag() ->
- inviso_tool.
-%% -----------------------------------------------------------------------------
-
-is_string([C|Rest]) when C>=32, C=<255 ->
- is_string(Rest);
-is_string([]) ->
- true;
-is_string(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions for handling the configuration file.
-%% -----------------------------------------------------------------------------
-
-%% The inviso tool is configured via start arguments and/or a configuration file.
-%% Start arguments will override any definitions in a configuration file.
-%% The configuration file is pointed out by either a start argument or the
-%% inviso application parameter 'inviso_tool_config_file'.
-
-%% Help function building the internal configuration structure. Configurations
-%% in the start argument will override parameters found in a configuration file.
-fetch_configuration(Config) ->
- case fetch_config_filename(Config) of
- {ok,FName} -> % We are supposed to use a conf-file.
- case read_config_file(FName) of
- {ok,LD} -> % Managed to open a file.
- NewLD=read_config_list(LD,Config),
- {ok,NewLD};
- Error = {error,_Reason} -> % Problem finding/opening file.
- Error
- end;
- false -> % No filename specified.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end.
-
-%% Help function determining the name of the file which shall be consulted as
-%% the main configuration file.
-%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
-fetch_config_filename(Config) ->
- case catch lists:keysearch(config_file,1,Config) of
- {value,{_,FName}} when is_list(FName) ->
- {ok,FName};
- _ -> % No filename in the start argument.
- fetch_config_filename_2()
- end.
-
-fetch_config_filename_2() ->
- case application:get_env(inviso_tool_config_file) of
- {ok,FName} when is_list(FName) ->
- {ok,FName};
- _ -> % Application parameter not specified.
- false % Means no config file will be used.
- end.
-
-%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
-read_config_file(FName) ->
- case catch file:consult(FName) of
- {ok,Terms} ->
- {ok,read_config_list(#ld{},Terms)};
- {error,Reason} ->
- {error,{file_consult,Reason}};
- {'EXIT',Reason} ->
- {error,{failure,Reason}}
- end.
-
-%% Help function traversing the Terms list entering known tag-values into #ld.
-read_config_list(LD,Terms) ->
- LD#ld{
- nodes = case mk_nodes(proplists:get_value(nodes,Terms,LD#ld.nodes)) of
- {ok,Nodes} -> Nodes;
- _ -> LD#ld.nodes
- end,
- c_node = proplists:get_value(c_node,Terms,LD#ld.c_node), % atom8)
- regexp_node = proplists:get_value(regexp_node,Terms,LD#ld.regexp_node), % atom()
- tc_def_file = proplists:get_value(tc_def_file,Terms,LD#ld.tc_def_file),
- tdg = proplists:get_value(tdg,Terms,LD#ld.tdg),
- debug = proplists:get_value(debug,Terms,LD#ld.debug),
- initial_tcs = proplists:get_value(initial_tcs,Terms,LD#ld.initial_tcs),
- dir = proplists:get_value(dir,Terms,LD#ld.dir),
- optg = proplists:get_value(optg,Terms,LD#ld.optg)
- }.
-
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which, if it exists, consults the trace definition file. The
-%% idea behind the trace definition file is to point out which trace cases there
-%% are, where to find them and how to turn them on and off.
-%% Trace case definitions are:
-%% {TCname,Type,VariableNameList,ActivatioFileName} |
-%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
-%% TCname=atom()
-%% Type=on | on_off
-%% VariableNameList=[atom(),...]
-%% ActivationFileName=DeactivationFileName=string()
-read_trace_case_definitions(LD) ->
- case LD#ld.tc_def_file of
- TCfileName when is_list(TCfileName) ->
- case catch file:consult(TCfileName) of
- {ok,Terms} ->
- Dir=LD#ld.dir, % The working directory of the tool.
- TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
- LD#ld{tc_dict=TCdict};
- _ ->
- LD
- end;
- _ ->
- LD
- end.
-
-read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
- FileName=make_absolute_path(FName,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on,
- VarNames,
- FileName,
- TCdict));
-read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
- FileNameOn=make_absolute_path(FNameOn,Dir),
- FileNameOff=make_absolute_path(FNameOff,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on_off,
- VarNames,
- FileNameOn,
- FileNameOff,
- TCdict));
-read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
- read_trace_case_definitions_2(Rest,Dir,TCdict);
-read_trace_case_definitions_2([],_Dir,TCdict) ->
- TCdict.
-
-%% Help function returning an absolute path to FName if FName is not already
-%% absolute. Dir is the working dir of the tool and supposed to be absolute.
-make_absolute_path(FName,Dir) ->
- case filename:pathtype(FName) of
- absolute -> % Then do nothing, allready absolute.
- FName;
- _ ->
- filename:join(Dir,FName)
- end.
-%% -----------------------------------------------------------------------------
-
-get_status(undefined,_Node) ->
- inviso:get_status();
-get_status(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Internal data structure functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% The nodes database structure.
-%% -----------------------------------------------------------------------------
-
-%% The purpose of the nodes database structure is to keep track of what runtime
-%% nodes we have, and their current status.
-%% Implementation:
-%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
-%% non-distributed case.
-%% AvailableStatus={up,Status1} | down
-%% Status1={State,Status} | reactivating
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-%% reactivating=the node is now being brought up to date.
-%% inactive=not tracing, can be initiated and then reactivated.
-%% The following states can occure.
-%% {inactive,running}
-%% Mainly when we start the tool, before a session has been started.
-%% {tracing,running}
-%% When a trace session is on-going.
-%% {trace_failure,running}
-%% If init_tracing failed for some reason.
-%% {tracing,suspended}
-%% reactivating
-%% The node is tracing (has always been) but was suspended. It is now
-%% no longer suspended and the tool is redong commands.
-%% {inactive,suspended}
-%% We can end up here if a session is stopped with this node suspended.
-
-%% Returns a nodes database structure filled with the nodes Nodes.
-mk_nodes(Nodes) when is_list(Nodes) ->
- {ok,lists:map(fun(N) when is_atom(N)->{N,down} end,Nodes)};
-mk_nodes(local_runtime) -> % The non-distributed case.
- down;
-mk_nodes(_Nodes) ->
- error.
-%% -----------------------------------------------------------------------------
-
-%% Updates the nodes database structure for each node that has been added.
-%% This is the case when we start the tool or reactivate a node. Note that a node
-%% may have become adopted instead of started.
-%% Returns a new nodes database structure.
-update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added to the control component.
- case get_status(CNode,[Node]) of % Examine if it is tracing or not.
- {ok,[{Node,NodeResult2}]} ->
- Result=mk_nodes_state_from_status(NodeResult2),
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
- {error,_Reason} -> % Strange, mark it as down now.
- update_added_nodes_2(CNode,Node,down,NodesD,Rest)
- end;
- Result ->
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
- end;
-update_added_nodes(_CNode,[],NodesD) ->
- NodesD;
-update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added, most likely autostart.
- mk_nodes_state_from_status(inviso:get_status());
- Result ->
- Result % Simply replace NodesD.
- end.
-
-update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} -> % Node already exists, replace!
- update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
- false -> % Strange, unknown node!
- update_added_nodes(CNode,Rest,NodesD)
- end.
-
-update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
- {up,{tracing,running}};
-update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
- {up,{tracing,suspended}};
-update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
- {up,{inactive,suspended}};
-update_added_nodes_3({ok,new}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,already_added}) ->
- already_added; % This is an error value!
-update_added_nodes_3({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Function marking all nodes that, according to the returnvalue from init_tracing,
-%% now are successfully initiated as tracing and running. Note that nodes that
-%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
-%% Also note that we assume that the nodes must be running to have made it this far.
-%% A node can of course have become suspended in the process, but that node will
-%% be marked as suspended later when that inviso event message arrives to the tool.
-%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
-%% as a result of the init_tracing call (judged from the LogResults).
-set_tracing_running_nodes(undefined,{ok,_LogResults},_AvailableStatus) -> % Non-distr. case.
- {{up,{tracing,running}},local_runtime};
-set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
- {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
-set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
- {down,[]}; % This is questionable!
-set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
- set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
-
-set_tracing_running_nodes_2(CNode,[{Node,{ok,_LogResults}}|Rest],NodesD,Nodes) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
- false -> % Strange.
- set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
- case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
- {ok,[{Node,NodeResult}]} ->
- Result=mk_nodes_state_from_status(NodeResult),
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
- {error,_Reason} -> % Strange, mark it as down.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
-set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
- {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
-
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in the NodesD structure and sets it to 'down'.
-%% Returns a new nodes structure.
-set_down_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,down}|Rest];
-set_down_nodes(Node,[NodeStruct|Rest]) ->
- [NodeStruct|set_down_nodes(Node,Rest)];
-set_down_nodes(_,[]) ->
- [];
-set_down_nodes(_,_) -> % Non-distributed case.
- down. % One can argue if this can happend.
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in NodesD to now be suspended. Note that if the node is
-%% reactivating it must be moved to state tracing because that is what is doing.
-set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
- [{Node,{up,{tracing,suspended}}}|Rest];
-set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
- [{Node,{up,{State,suspended}}}|Rest];
-set_suspended_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_suspended_nodes(Node,Rest)];
-set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
- [];
-set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{tracing,suspended}};
-set_suspended_nodes(_,{up,{State,_}}) ->
- {up,{State,suspended}}.
-%% -----------------------------------------------------------------------------
-
-%% This function is called when reactivation is completed. Hence it moves the
-%% node to no longer suspended. Note this can mean that the node is either
-%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
-set_running_nodes(Node,NodesD) when is_list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
- false -> % Very strange!
- NodesD
- end;
-set_running_nodes(_,NodesD) -> % The non-distributed case.
- set_running_nodes_2(NodesD).
-
-set_running_nodes_2({up,reactivating}) ->
- {up,{tracing,running}};
-set_running_nodes_2({up,{State,suspended}}) ->
- {up,{State,running}}.
-%% -----------------------------------------------------------------------------
-
-%% Function marking node as now reactivating. That means it is not suspended
-%% any longer (and tracing), but still not part of the set of nodes which shall
-%% get all commands. Returns a new NodesD.
-set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,{up,reactivating}}|Rest];
-set_reactivating_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_reactivating_nodes(Node,Rest)];
-set_reactivating_nodes(_,[]) ->
- [];
-set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
- {up,reactivating}.
-%% -----------------------------------------------------------------------------
-
-%% Function called when stop-tracing is done. That is all nodes in Nodes shall
-%% be inactive now. Note that an inactive node can still be suspended.
-%% Returns a new NodesD.
-set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{inactive,running}};
-set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
- {up,{inactive,Status}};
-set_inactive_nodes(_,down) ->
- down;
-set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,{up,reactivating}}} ->
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
- {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
- _ -> % This should not happend.
- set_inactive_nodes(Rest,NodesD)
- end;
-set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
- set_inactive_nodes(Rest,NodesD);
-set_inactive_nodes([],NodesD) ->
- NodesD.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all node names. Note that it can only be used in the
-%% distributed case.
-get_all_nodenames_nodes(NodesD) ->
- lists:map(fun({Node,_})->Node end,NodesD).
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are up, tracing and running (not suspended),
-%% or 'void' in the non-distributed case. This is the list of nodes that shall get
-%% inviso commands.
-get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
- [Node|get_nodenames_running_nodes(Rest)];
-get_nodenames_running_nodes([{_Node,_}|Rest]) ->
- get_nodenames_running_nodes(Rest);
-get_nodenames_running_nodes([]) ->
- [];
-get_nodenames_running_nodes(_) ->
- void. % When non distributed, N/A.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that can be made to initiate tracing.
-get_inactive_running_nodes({up,{inactive,running}}) ->
- local_runtime;
-get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
- [];
-get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
- [Node|get_inactive_running_nodes(Rest)];
-get_inactive_running_nodes([{_Node,_}|Rest]) ->
- get_inactive_running_nodes(Rest);
-get_inactive_running_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that are currently tracing (not necessarily running).
-%% In the non-distributed case the status of the runtime component will be
-%% returned.
-%% Note that nodes showing trace_failure will be included since we like to stop
-%% tracing at those nodes too.
-get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([_|Rest]) ->
- get_tracing_nodes(Rest);
-get_tracing_nodes([]) ->
- [];
-get_tracing_nodes(AvailableStatus) ->
- AvailableStatus.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are currently up.
-get_available_nodes(down) ->
- undefined;
-get_available_nodes([{_Node,down}|Rest]) ->
- get_available_nodes(Rest);
-get_available_nodes([{Node,_}|Rest]) ->
- [Node|get_available_nodes(Rest)];
-get_available_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function returning the "state" of Node. Mainly used to check if the node is
-%% suspended or not.
-%% Returns {State,Status} | reactivating | down
-%% where
-get_state_nodes(Node,NodesD) when is_list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- get_state_nodes_2(AvailableStatus);
- false ->
- false
- end;
-get_state_nodes(_,NodesD) -> % Non distributed case.
- get_state_nodes_2(NodesD).
-
-get_state_nodes_2({up,{trace_failure,Status}}) ->
- {trace_failure,Status};
-get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
- {State,suspended};
-get_state_nodes_2({up,reactivating}) ->
- reactivating;
-get_state_nodes_2({up,{State,running}}) ->
- {State,running};
-get_state_nodes_2(down) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Help function in the case we need to consult the state/status of a runtime
-%% component. Returns a nodesD value that can be added to the nodes database.
-mk_nodes_state_from_status({ok,{tracing,running}}) ->
- {up,{tracing,running}};
-mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
- {up,{tracing,suspended}};
-mk_nodes_state_from_status({ok,{_,running}}) ->
- {up,{inactive,running}};
-mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
- {up,{inactive,suspended}};
-mk_nodes_state_from_status({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The session_state.
-%% -----------------------------------------------------------------------------
-
-%% The session state reflects if the inviso_tool is tracing or not.
-%% This means that if the tool is tracing a reconnected node can be made to
-%% restart_session.
-
-%% Returns the correct value indicating that we are tracing now.
-tracing_sessionstate() ->
- tracing.
-%% -----------------------------------------------------------------------------
-
-%% Returns true or false depending on if we are tracing now or not.
-is_tracing(tracing) ->
- true;
-is_tracing(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Returns the correct value indicating that the tool is not tracing.
-passive_sessionstate() ->
- idle.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The tracer_data datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tracer_data structure collects the tracer data arguments used to init tracing
-%% by this inviso tool. The args are saved per session. Each session has
-%% a number.
-%% Implementation:
-%% Sessions=[{SessionNr,TDGargs},...]
-%% SessionNr=integer()
-%% TDGargs=list(), args given to the tracer data generator
-%% minus the first argument which is the Node name.
-
-%% Function taking tracerdata args structure inserting yet another session.
-%% Returns {SessionNr,NewTDs}.
-insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
- {SNr+1,[{SNr+1,TDGargs}|TDs]};
-insert_td_tracer_data(TDGargs,undefined) ->
- {1,[{1,TDGargs}]}.
-%% -----------------------------------------------------------------------------
-
-%% Returns the latest session nr.
-get_latest_session_nr_tracer_data(undefined) ->
- undefined;
-get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
- SessionNr.
-%% -----------------------------------------------------------------------------
-
-%% Returns the tracer data arguments used when creating the trace data for the
-%% latest session.
-get_latest_tdgargs_tracer_data(undefined) ->
- undefined;
-get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
- TDGargs.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% The tc_dict or trace case dictionary datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tc_dict stores information about all available trace cases.
-%% Implementation:
-%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
-%% TCname=atom()
-%% Type=on | on_off
-%% VarNames=[atom(),...]
-%% FNameOn=FNameOff=string()
-
-%% Returns the empty trace case dictionary.
-mk_tc_dict() ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function inserting a new trace case into the trace case dictionary.
-insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
- [{TCname,on,VarNames,FNameOn}|TCdict].
-insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
- [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
-%% -----------------------------------------------------------------------------
-
-%% Function finding a trace case definition in the tc_dict structure.
-%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
-get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
- {ok,Tuple};
-get_tracecase_tc_dict(TCname,[_|Rest]) ->
- get_tracecase_tc_dict(TCname,Rest);
-get_tracecase_tc_dict(_,[]) ->
- false;
-get_tracecase_tc_dict(_,_) -> % There are no trace cases!
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
-%% function.
-%% Returning {ok,ActivationFileName}.
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
- {ok,FNameOn};
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
- {ok,FNameOn}.
-
-get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
- {ok,FNameOff};
-get_tc_deactivate_fname(_) -> % Not a case with off function.
- false.
-
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
- VarNames;
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
- VarNames.
-
-%% -----------------------------------------------------------------------------
-
-
-%% The Command History Log (CHL) stores commands to make it possible to
-%% reactivate suspended nodes, reconnect restarted nodes, and to make
-%% autostart files.
-%% Each time tracing is initiated (that is started) the CHL is cleared since
-%% it would not make scense to repeat commands from an earlier tracing at
-%% reactivation for instance.
-
-%% Implementation: {NextCounter,OnGoingList,ETStable}
-%% NextCounter=integer(), next command number - to be able to sort them in order.
-%% OnGoingList=[{ProcH,{TCname,ID}},...]
-%% ID=term(), instance id for this execution of this trace case.
-%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
-%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
-%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
-%% {{TCname,#Ref},Counter,Bindings} % An rtc
-%% {{M,F,Args,#Ref},Counter}
-%% Counter=integer(), the order-counter for this logged entry.
-%% State1=activating | stopping
-%% Where:
-%% activating: the activation file for the tracecase is running.
-%% running : activation is completed.
-%% stopping : set on the previously running ETS entry when deactivation
-%% file is currently executing.
-%% stop : entered with own Counter into the ETS table when
-%% deactivation file is executing. Remains after too.
-%% Result=term(), the result returned from the tr-case or inviso call.
-
-
-%% Returning an initial empty CHL.
-mk_chl(undefined) ->
- {1,[],ets:new(inviso_tool_chl,[set,protected])};
-mk_chl({_,_,TId}) ->
- ets:delete(TId),
- mk_chl(undefined).
-
-%% Help function returning 'true' if there is a current history.
-history_exists_chl(undefined) ->
- false;
-history_exists_chl({_,_,_}) ->
- true.
-
-%% Function looking up the state of this trace case.
-find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
- {ok,Bindings};
- [{_,_,State,_}] -> % activating or stopping.
- State;
- [] ->
- false
- end.
-
-%% Function finding the Trace case associated with a process handle
-%% doing this trace case's activation or stopping.
-find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
- case lists:keysearch(ProcH,1,OnGoingList) of
- {value,{_,{TCname,Id}}} ->
- [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
- {State,{TCname,Id}}; % Should be activating or stopping.
- false ->
- false
- end.
-
-%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
-%% is called for trace cases that do not have on/off functionality.
-set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
- ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
- {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function marking a trace case as now running. That is the activation
-%% phase is completed. It is normaly completed when the process executing
-%% the trace case signals that it is done.
-set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
- [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function marking trace case TCname with identifier Id as now in its stopping
-%% state. Where ProcH is the handler to the process running the stopping
-%% trace case.
-set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
- [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
- ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
- {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function removing a TCname-Id from the CHL. This is mostly used
-%% if activating the trace case failed for some reason. We do not then
-%% expect the user to stop the trace case. Hence it must be removed now.
-%% A reactivation process may have noticed the activating-entry and started
-%% to activate it. But since the general state reached after an unsuccessful
-%% activation can not easily be determined, we don't try to do much about it.
-del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function removing the entry TCname+Id from the CHL. This makes it
-%% possible to activate a tracecase with this id again. The entry was
-%% previously marked as stopping.
-nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter+1,NewOnGoingList,TId}.
-
-%% Function stopping all processes saved as being now running tc executers.
-%% This is useful as cleanup during stop tracing for instance.
-%% Returns a new CHL which is not in all parts correct. Entries in the
-%% ETS table are for instance not properly state-changed. But the CHL will
-%% from now on only be used to create command files and similar.
-stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
- exit(ProcH,kill),
- stop_all_tc_executer_chl({NextCounter,Rest,TId});
-stop_all_tc_executer_chl({NextCounter,[],TId}) ->
- {NextCounter,[],TId}.
-
-%% Function adding a "plain" inviso call to the CHL.
-add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
- {NextCounter+1,OnGoingList,TId}.
-
-%% Function adding a run trace case entry to the chl.
-add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
- {NextCounter+1,OnGoingList,TId}.
-%% Returns the highest used counter number in the command history log.
-get_highest_used_counter_chl({NextCounter,_,_}) ->
- NextCounter-1.
-
-%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
-%% assynchronous tracecases.
-get_ongoing_chl(undefined) ->
- [];
-get_ongoing_chl({_,OngoingList,TId}) ->
- get_ongoing_chl_2(OngoingList,TId).
-
-get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_C,activating,_B}] ->
- [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
- [{_,_C,stopping,_B}] ->
- [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
- end;
-get_ongoing_chl_2([],_) ->
- [].
-
-%% Function returning a list of log entries. Note that the list is unsorted
-%% in respect to Counter.
-get_loglist_chl({_,_,TId}) ->
- L=ets:tab2list(TId),
- lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
- (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
- (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
- (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
- (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
- end,
- L);
-get_loglist_chl(_) -> % The history is not initiated, ever!
- [].
-
-%% Function returning a list of log entries, but only those which are not
-%% cancelled out by deactivations.
-% get_loglist_active_chl({_,_,TId}) ->
-% L=ets:tab2list(TId),
-% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
-% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
-% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
-% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
-% end,
-% L);
-% get_loglist_chl(_) -> % The history is not initiated, ever!
-% [].
-
-
-%% This helpfunction recreates a history from a saved history list. This function
-%% is supposed to crash if the log is not well formatted. Note that we must restore
-%% the counter in order for the counter to work if new commands are added to the
-%% history.
-replace_history_chl(OldCHL,SortedLog) ->
- {_,Ongoing,TId}=mk_chl(OldCHL),
- {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
- {ok,{Counter+1,Ongoing,NewTId}}.
-
-replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,Id},C,running,B,undefined}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
- ets:insert(TId,{{M,F,Args,make_ref()},C}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,make_ref()},C,B}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[],Counter) ->
- {TId,Counter}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Reactivators data structure.
-%% -----------------------------------------------------------------------------
-
-%% Function adding a new node-reactivatorpid pair to the reactivators structure.
-%% In this way we know which reactivators to remove if Node terminates, or when
-%% a node is fully updated when a reactivator is done.
-add_reactivators(Node,Pid,Reactivators) ->
- [{Node,Pid}|Reactivators].
-
-%% Function removing a reactivator entry from the reactivators structure.
-del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
- Rest;
-del_reactivators(RPid,[Element|Rest]) ->
- [Element|del_reactivators(RPid,Rest)];
-del_reactivators(_,[]) -> % This should not happend.
- [].
-
-get_node_reactivators(RPid,Reactivators) ->
- case lists:keysearch(RPid,2,Reactivators) of
- {value,{Node,_}} ->
- Node;
- false -> % This should not happend.
- false
- end.
-
-%% Returns a list of list all nodes that are currently reactivating.
-get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
- [Nodes|get_all_nodes_reactivators(Rest)];
-get_all_nodes_reactivators([]) ->
- [].
-
-%% Function stopping all running reactivator processes. Returns a new empty
-%% reactivators structure. Note that this function does not set the state of
-%% Nodes. It must most often be set to running.
-stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
- exit(Pid,kill),
- stop_all_reactivators(Rest);
-stop_all_reactivators([]) ->
- []. % Returns an empty reactivators.
-
-%% Help function stopping the reactivator (if any) that reactivates Node.
-%% Returns a new list of reactivators structure.
-stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
- exit(Pid,kill),
- Rest;
-stop_node_reactivators(Node,[NodePid|Rest]) ->
- [NodePid|stop_node_reactivators(Node,Rest)];
-stop_node_reactivators(_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Started initial trace cases data structure.
-%% -----------------------------------------------------------------------------
-
-%% This datastructure keeps information about ongoing trace cases started
-%% automatically at init_tracing. These must be automatically stopped when calling
-%% stop_tracing.
-
-add_initial_tcs(TCname,Id,StartedInitialTcs) ->
- [{TCname,Id}|StartedInitialTcs].
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/inviso/src/inviso_tool_lib.erl b/lib/inviso/src/inviso_tool_lib.erl
deleted file mode 100644
index f221c4b6de..0000000000
--- a/lib/inviso/src/inviso_tool_lib.erl
+++ /dev/null
@@ -1,379 +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$
-%%
-%% Description:
-%% Support module to the inviso tool.
-%%
-%% Authors:
-%% Lennart Öhman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool_lib).
-
-%% -----------------------------------------------------------------------------
-%% Exported library APIs
-%% -----------------------------------------------------------------------------
--export([inviso_cmd/3,expand_module_names/3,make_patterns/7,std_tdg/2]).
--export([mk_tdg_args/2,mk_complete_tdg_args/2,get_datetime_from_tdg_args/1]).
--export([debug/3]).
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
--define(DBG_OFF,off). % No internal debug indicator.
-
-
-%% =============================================================================
-%% Functions for inviso_cmd
-%% =============================================================================
-
-%% Help function which executes a trace control call. The reason for having a special
-%% function is that we either want to do rpc if the trace control component is
-%% located on another Erlang node than this one. Or call trace_c directly if
-%% it actually is on this node.
-%% Returns whatever the inviso function returns. In case of badrpc it is wrapped
-%% in an error-tuple.
-inviso_cmd(NodeName,Func,Args) ->
- case node() of
- NodeName -> % Control component on this node.
- apply(inviso,Func,Args);
- _ -> % On other node, must do RPC.
- case rpc:call(NodeName,inviso,Func,Args) of
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- Result ->
- Result
- end
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for expand_module_names
-%% =============================================================================
-
-%% Help function which expands the module name depending on how it is expressed.
-%% Setting Nodes to 'void' makes it non-distributed, expanding only here.
-%% The following special cases are handled:
-%% '_' =All modules, no expansion into individual module names. Instead
-%% it is intended to use the '_' mechanism in the trace_pattern BIF.
-%% Can therefore not be combined with a directory regexp.
-%% "*" =Is translated into ".*".
-%% "abc"=Means ".*abc.*". Can only be used for module or directory names
-%% containing upper or lowercase, digits and a slash.
-%% Returns {multinode_expansion,NodeModules},
-%% {singlenode_expansion,Modules},
-%% 'module', 'wildcard' or {error,Reason},
-%% To limit the places where expansion is done, the option {expand_only_at,Node}
-%% can be provided in the Opts list.
-%% In the non-distributed case the singlenode_expansion will be returned.
-expand_module_names(_Nodes,Mod={_,'_'},_) ->
- {error,{faulty_regexp_combination,Mod}};
-expand_module_names(Nodes,{DirStr,ModStr},Opts) when is_list(DirStr), is_list(ModStr) ->
- case expand_module_names_special_regexp(DirStr) of
- {ok,NewDirStr} ->
- case expand_module_names_special_regexp(ModStr) of
- {ok,NewModStr} ->
- expand_module_names_2(Nodes,NewDirStr,NewModStr,Opts);
- {error,_Reason} ->
- {error,{faulty_regexp,ModStr}}
- end;
- {error,_Reason} ->
- {error,{faulty_regexp,DirStr}}
- end;
-expand_module_names(_,'_',_Opts) -> % If we want to trace all modules
- wildcard; % we shall not expand it.
-expand_module_names(_Nodes,Mod,_Opts) when is_atom(Mod) ->
- module; % If it is an atom, no expansion.
-expand_module_names(Nodes,"*",Opts) -> % Treat this as a reg.exp.
- expand_module_names(Nodes,".*",Opts);
-expand_module_names(Nodes,ModStr,Opts) when is_list(ModStr) ->
- case expand_module_names_special_regexp(ModStr) of
- {ok,NewModStr} ->
- expand_module_names_2(Nodes,NewModStr,Opts);
- {error,_Reason} ->
- {error,{faulty_regexp,ModStr}}
- end.
-
-expand_module_names_2(void,ModStr,Opts) -> % Non-distributed case.
- {singlenode_expansion,inviso_rt_lib:expand_regexp(ModStr,Opts)};
-expand_module_names_2(Nodes,ModStr,Opts) ->
- case get_expand_regexp_at_opts(Opts) of
- {ok,Node} -> % Expansion only at this node.
- case inviso_rt_lib:expand_regexp([Node],ModStr,Opts) of
- [{Node,Modules}] when is_list(Modules) ->
- {singlenode_expansion,Modules};
- [{Node,_}] -> % Most likely badrpc.
- {error,{faulty_node,Node}}
- end;
- false -> % Expand on all nodes.
- Result=inviso_rt_lib:expand_regexp(Nodes,ModStr,Opts),
- {multinode_expansion,Result}
- end.
-expand_module_names_2(void,DirStr,ModStr,Opts) -> % Non-distributed case.
- {singlenode_expansion,inviso_rt_lib:expand_regexp(DirStr,ModStr,Opts)};
-expand_module_names_2(Nodes,DirStr,ModStr,Opts) ->
- case get_expand_regexp_at_opts(Opts) of
- {ok,Node} -> % Expansion only at this node.
- case inviso_rt_lib:expand_regexp([Node],DirStr,ModStr,Opts) of
- [{Node,Modules}] when is_list(Modules) ->
- {singlenode_expansion,Modules};
- [{Node,_}] -> % Most likely badrpc.
- {error,{faulty_node,Node}}
- end;
- false -> % Expand on all nodes.
- Result=inviso_rt_lib:expand_regexp(Nodes,DirStr,ModStr,Opts),
- {multinode_expansion,Result}
- end.
-
-%% Help function which converts a special regexp into a proper one. With
-%% special regexps we mean e.g:"abc" which is supposed to mean ".*abc.*".
-%% Always returns a regexp or {error,Reason}.
-expand_module_names_special_regexp(Str) ->
- StrLen=length(Str),
- case re:run(Str,"[0-9a-zA-Z_/]*") of
- {match,[{0,StrLen}]} -> % Ok, it is the special case.
- {ok,".*"++Str++".*"}; % Convert it to a proper regexp.
- {match,_} ->
- {ok,Str}; % Keep it and hope it is a regexp.
- nomatch ->
- {ok,Str}; % Keep it and hope it is a regexp.
- {error,Reason} -> % Can't continue with this!
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for make_pattern.
-%% =============================================================================
-
--define(DEPTH,3). % Max recursive safety catch depth.
-
-%% Help function that creates trace-patterns for each module in the list.
-%% It can handle both lists of modules or lists of nodes and modules.
-%% It will also in the process apply safety catches, if such are not disabled,
-%% in order to prevent certain patterns to form.
-%% The safety catch function is supposed to return either 'ok' or {new,NewTracePattern}.
-%% Where the NewTracePattern is a list of zero or more {M,F,Arity,MS}. The
-%% NewTracePatter is then the replacement for the tried trace-pattern.
-%% Note that the new trace-pattern(s) are also tried against all safety catches.
-%% This can possibly result in even replacements of the replacements. There is
-%% a depth meter to prevent the safety catch mechanism from circularly expanding
-%% trace patterns for ever.
-%% Returns a list of [{Node,PatternList},...] or [Pattern,...].
-%% The latter is used when the modules have been expanded on a single node.
-make_patterns(Catches,Opts,Dbg,NodeModsOrMods,F,A,MS) ->
- OwnArg=get_ownarg_opts(Opts),
- case get_disable_safety_opts(Opts) of
- true -> % Do not use the safety catches.
- make_patterns_2(void,OwnArg,Dbg,NodeModsOrMods,F,A,MS);
- false ->
- make_patterns_2(Catches,OwnArg,Dbg,NodeModsOrMods,F,A,MS)
- end.
-
-make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when is_list(Mods) ->
- TPs=make_patterns_3(Catches,OwnArg,Dbg,Mods,F,A,MS,[]),
- [{Node,join_patterns(TPs)}|make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS)];
-make_patterns_2(Catches,OwnArg,Dbg,[{_Node,_}|Rest],F,A,MS) -> % badrpc!?
- make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS);
-make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when is_list(Modules) ->
- TPs=make_patterns_3(Catches,OwnArg,Dbg,Modules,F,A,MS,[]),
- join_patterns(TPs);
-make_patterns_2(_,_,_,[],_,_,_) ->
- [].
-
-make_patterns_3(void,OwnArg,Dbg,[M|Rest],F,A,MS,Result) -> % S-catches not used!
- make_patterns_3(void,OwnArg,Dbg,Rest,F,A,MS,[{M,F,A,MS}|Result]);
-make_patterns_3(Catches,OwnArg,Dbg,[M|Rest],F,A,MS,Result) ->
- NewTPs=try_safety_catches(Catches,OwnArg,[{M,F,A,MS}],Dbg,[],?DEPTH),
- make_patterns_3(Catches,OwnArg,Dbg,Rest,F,A,MS,[NewTPs|Result]);
-make_patterns_3(_,_,_,[],_,_,_,Result) ->
- lists:flatten(Result).
-
-try_safety_catches(_Catches,_OwnArg,TPs,Dbg,_Accum,0) -> % Max depth here!
- debug(max_catch_depth,Dbg,[TPs]),
- TPs; % Just return them unchanged.
-try_safety_catches(Catches,OwnArg,[TP={M,F,A,MS}|Rest],Dbg,Accum,Depth) ->
- case try_safety_catch(Catches,OwnArg,M,F,A,MS,Dbg) of
- ok -> % This pattern is safe!
- try_safety_catches(Catches,OwnArg,Rest,Dbg,[TP|Accum],?DEPTH);
- {new,NewTPs} -> % Then we must try them too!
- NewTPs2=try_safety_catches(Catches,OwnArg,NewTPs,Dbg,[],Depth-1),
- try_safety_catches(Catches,OwnArg,Rest,Dbg,[NewTPs2|Accum],?DEPTH)
- end;
-try_safety_catches(_,_,[],_,Accum,_) ->
- Accum.
-
-try_safety_catch([{SafetyMod,SafetyFunc}|Rest],OwnArg,M,F,A,MS,Dbg) ->
- case (catch apply(SafetyMod,SafetyFunc,[M,F,A,MS,OwnArg])) of
- ok -> % This catch has no oppinion about it.
- try_safety_catch(Rest,OwnArg,M,F,A,MS,Dbg); % Continue with the next.
- {new,NewTPs} -> % Replace it with this or these new.
- debug(safety_catch,Dbg,[new,{SafetyMod,SafetyFunc},M,F,A,MS,NewTPs]),
- {new,NewTPs}; % and stop trying safety cathes.
- {'EXIT',Reason} -> % Something wrong with the safety catch.
- debug(safety_catch,Dbg,['EXIT',{SafetyMod,SafetyFunc},M,F,A,MS,Reason]),
- try_safety_catch(Rest,OwnArg,M,F,A,MS,Dbg) % Skip it and go on.
- end;
-try_safety_catch([],_,_,_,_,_,_) ->
- ok. % Since it passed all, it is safe!
-%% -----------------------------------------------------------------------------
-
-%% Help function that joins patterns together. This is necessary since you can
-%% only set the pattern once for a module-function-arity. This function can not
-%% remove conflicting match-spec "commands". Match-specs will simply be concatenated.
-%% Returns a list of patterns where each mod-func-arity is unique.
-join_patterns(Patterns) ->
- join_patterns_2(Patterns,[]).
-
-join_patterns_2([{M,F,Arity,MS}|Rest],Result) ->
- case join_patterns_is_already_done(M,F,Arity,Result) of
- false -> % No we have not collapsed this one.
- case join_patterns_3(M,F,Arity,Rest) of
- [] -> % No this combination is unique.
- join_patterns_2(Rest,[{M,F,Arity,MS}|Result]);
- MSs -> % We got a list of all other TPs.
- join_patterns_2(Rest,[{M,F,Arity,MS++MSs}|Result])
- end;
- true -> % We already joined this M-F-Arity.
- join_patterns_2(Rest,Result) % Simply skip it, already done.
- end;
-join_patterns_2([],Result) ->
- Result. % Reversed but does not matter!
-
-%% Help function checking if we have already built a trace-pattern for
-%% this M-F-Arity. If so, the found M-F-Arity is already handled.
-join_patterns_is_already_done(M,F,Arity,[{M,F,Arity,_}|_]) ->
- true;
-join_patterns_is_already_done(M,F,Arity,[_|Rest]) ->
- join_patterns_is_already_done(M,F,Arity,Rest);
-join_patterns_is_already_done(_,_,_,[]) ->
- false.
-
-%% Help function which simply concatenates all match-specs for this
-%% M-F-Arity.
-join_patterns_3(M,F,Arity,[{M,F,Arity,MS}|Rest]) ->
- [MS|join_patterns_3(M,F,Arity,Rest)];
-join_patterns_3(M,F,Arity,[_|Rest]) ->
- join_patterns_3(M,F,Arity,Rest);
-join_patterns_3(_,_,_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Function for tracer data creation.
-%% =============================================================================
-
--define(I2L(Arg),integer_to_list(Arg)).
-
-%% The inviso_tool uses a tracer-data generator function to create the tracer_data
-%% specification for each node that shall participate in tracing controlled
-%% through the inviso-tool. If no own tracer data generator function is specified,
-%% this function is used.
-std_tdg(Node,{{Y,Mo,D},{H,Mi,S}}) ->
- NameStr=atom_to_list(Node)++"_"++?I2L(Y)++"-"++?I2L(Mo)++"-"++?I2L(D)++"_"++
- ?I2L(H)++"-"++?I2L(Mi)++"-"++?I2L(S),
- LogTD={file,NameStr++".log"},
- TiTD={file,NameStr++".ti"},
- [{trace,LogTD},{ti,TiTD}].
-%% ------------------------------------------------------------------------------
-
-%% mk_tdg_args(DateTime,Args)=TDGargs
-%% DateTime={Date,Time}
-%% Date=tuple(),
-%% Time=tuple(),
-%% Args=list()
-%% TDGargs=list(),
-%% Creates the TDGargs list used when calling functions making the CompleteTDGargs.
-mk_tdg_args(DateTime,Args) ->
- [DateTime|Args].
-%% ------------------------------------------------------------------------------
-
-%% mk_complete_tdg_args(Node,TDGargs)=CompleteTDGargs
-%% Returns the list of all arguments a tracer data generator function must accept.
-mk_complete_tdg_args(Node,TDGargs) ->
- [Node|TDGargs].
-%% ------------------------------------------------------------------------------
-
-%% get_datetime_from_tdg_args(TDGargs)=DateTime
-%% Function returning the DateTime tuple in a TDGargs list.
-get_datetime_from_tdg_args([DateTime|_]) ->
- DateTime.
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Various help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% Functions handling set trace-pattern options.
-%% -----------------------------------------------------------------------------
-
-%% Gets additional arguments given to various configurable functions.
-%% Returns a list.
-get_ownarg_opts(Opts) ->
- case lists:keysearch(arg,1,Opts) of
- {value,{_,OwnArg}} when is_list(OwnArg) ->
- OwnArg;
- {value,{_,OwnArg}} ->
- [OwnArg];
- false ->
- []
- end.
-%% -----------------------------------------------------------------------------
-
-get_disable_safety_opts(Opts) ->
- case lists:member(disable_safety,Opts) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_expand_regexp_at_opts(Opts) ->
- case lists:keysearch(expand_only_at,1,Opts) of
- {value,{_,Node}} when is_atom(Node) ->
- {ok,Node};
- _ ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for the internal debugging system.
-%% =============================================================================
-
-%% The debug system is meant to provide tracing of inviso tool at different levels.
-%%
-%% debug(What,Level,Description) -> nothing significant.
-%% What : controls what kind of event. This can both be certain parts of the tool
-%% as well as certain levels (info to catastrophy).
-%% Level: Determines if What shall be printed or not.
-%% Description: this is what happend.
-debug(_What,?DBG_OFF,_Description) ->
- true; % Debug is off, no action.
-debug(What,On,Description) ->
- debug_2(What,On,Description).
-
-debug_2(What,_,Description) ->
- io:format("INVISO DEBUG:~w, ~p~n",[What,Description]).
-%% -----------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_tool_sh.erl b/lib/inviso/src/inviso_tool_sh.erl
deleted file mode 100644
index b02f498c5b..0000000000
--- a/lib/inviso/src/inviso_tool_sh.erl
+++ /dev/null
@@ -1,1749 +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%
-%%
-%% Description:
-%% The runtime component of the trace tool Inviso.
-%%
-%% Authors:
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool_sh).
-
-%% Inviso Session Handler.
-%% This is the code for the session handler process. Its purpose is that we have
-%% one session handler process for each trace session started through the
-%% start_session inviso tool API. The session handler process is responsible for:
-%%
-%% -Knowing the state/status of all participating runtime components.
-%% -Keeping storage of all tracerdata all our participants have used. This means
-%% also to find out the tracerdata of runtime components connecting by them
-%% selves.
-%%
-%% STORAGE STRATEGY
-%% ----------------
-%% The local information storage can be changed by two things. Either by executing
-%% commands issued through our APIs. Or by receiving trace_event from the control
-%% component. When we execute commands, a corresponding event will also follow.
-%% Meaning that in those situations we are informed twice.
-%% A simple strategy could be to wait for the event even when doing the changes
-%% to the runtime components our self (through commands). But that may result in
-%% a small time frame where someone might do yet another command and failing
-%% because the local information storage is not uptodate as it would have been
-%% expected to be. Therefore we always update the local storage when making changes
-%% to a runtime component our selves. There will eventually be a double update
-%% through an incoming event. But the storage must coop with that, preventing
-%% inconsitancies to happend. An example of a strategy is that the tracerdata table
-%% is a bag, not allowing for double entries of the same kind. Therefore a double
-%% update is harmless there.
-
-%% ------------------------------------------------------------------------------
-%% Module wide constants.
-%% ------------------------------------------------------------------------------
--define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
--define(TRACING,tracing). % A state defined by the control component.
--define(RUNNING,running). % A status according to control componet.
-
--define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% API exports.
-%% ------------------------------------------------------------------------------
--export([start_link/5,start_link/8]).
--export([cancel_session/1,stop_session/3]).
--export([reactivate/1,reactivate/2]).
--export([ctpl/5,tpl/5,tpl/6,tpl/7,
- tf/2,tf/3,
- tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
- tpm_ms/7,ctpm_ms/6,ctpm/5
- ]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Internal exports.
-%% ------------------------------------------------------------------------------
--export([init/1,handle_call/3,handle_info/2,terminate/2]).
-
--export([get_loopdata/1]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Includes.
-%% ------------------------------------------------------------------------------
--include_lib("kernel/include/file.hrl"). % Necessary for file module.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
-%% {ok,Pid} | {error,Reason}
-%% From= pid(), the initial client expecting the reply.
-%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
-%% CtrlNode=atom() | 'void', the node where the trace control component is.
-%% CtrlPid=pid(), the pid of the trace control component.
-%% SafetyCatches=
-%% Dir=string(), where to place fetched logs and the merged log.
-%% Dbg=debug structure.
-%% NodesIn=[Node,...], list of nodes already in another session.
-%% NodesNotIn=[Node,...], list of nodes not in another session.
-%%
-%% Starts a session-handler. It keeps track of the the state and status of all
-%% participating runtime components. Note that there is a non-distributed case too.
-%% In the non-distributed case there is no things such as CtrlNode.
-start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
- gen_server:start_link(?MODULE,
- {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
- []).
-
-start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
- gen_server:start_link(?MODULE,
- {self(),From,NodeParams,CtrlNode,CtrlPid,
- SafetyCatches,Dbg,NodesIn,NodesNotIn},
- []).
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing where it is ongoing. Fetches all logfiles.
-stop_session(SID,Dir,Prefix) ->
- gen_server:call(SID,{stop_session,Dir,Prefix}).
-%% ------------------------------------------------------------------------------
-
-%% stop_session(SID) = ok
-%%
-%% Cancels the session brutaly. All runtime components are made to stop tracing,
-%% all local log files are removed using the tracerdata we know for them.
-cancel_session(SID) ->
- gen_server:call(SID,cancel_session).
-%% ------------------------------------------------------------------------------
-
-%% reactivate(SID) = {ok,
-%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
-%% SID=session id, pid().
-%% Nodes=[Node,...]
-%% NodeResult=[{Node,Result},...]
-%% Result={Good,Bad}
-%% Good,Bad=integer(), the number of redone activities.
-%%
-%% Function which reactivates runtime components being suspended. This is done
-%% replaying all trace flags (in the correct order) to the corresponding nodes.
-%% Note that this may also mean turning flags off. Like first turning them on
-%% then off a split second later.
-reactivate(SID) ->
- gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
-reactivate(SID,Nodes) ->
- gen_server:call(SID,{reactivate,Nodes}).
-%% ------------------------------------------------------------------------------
-
-
-%% tpl(SessionID,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
-%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
-%% ModRegExp=DirRegExp= string()
-%% Func='_' | FunctionName
-%% Arity='_' | integer()
-%% MS=[] | false | a match specification
-%% Opts=[Opts,...]
-%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
-%% Nodes=[NodeName,...]
-tpl(SID,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
-tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
-tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
-tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
-%% See tpl/X for arguments.
-%%
-%% Removes local trace-patterns from functions.
-ctpl(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-tpm_localnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_localnames,Nodes}).
-
-%% tpm_globalnames(SID,Nodes) ->
-%% gen_server:call(SID,{tpm_globalnames,Nodes}).
-
-init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
-init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,
- {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-
-tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
-
-ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
-
-ctpm(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-%% tf(SessionID,Nodes,TraceConfList)=
-%% TraceConfList=[{PidSpec,Flags},...]
-%% PidSpec=pid()|atom()|all|new|existing
-%% Flags=[Flag,...]
-tf(SID,TraceConfList) ->
- gen_server:call(SID,{tf,TraceConfList}).
-tf(SID,Nodes,TraceConfList) ->
- gen_server:call(SID,{tf,Nodes,TraceConfList}).
-%% ------------------------------------------------------------------------------
-
-
-get_loopdata(SID) ->
- gen_server:call(SID,get_loopdata).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Genserver call-backs.
-%% ==============================================================================
-
-%% Initial function for the session handler process. The nodes participating in
-%% the session must previously have been added to our control component by the tool.
-%% The session handler first finds out the state/status of the specified runtime
-%% components, then it tries to initiate tracing on those where it is applicable.
-%% Note that a reply to the initial (tool)client is done from here instead from
-%% the tool-server.
-init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
- {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
- case is_tool_internal_tracerdata(TracerData) of
- false -> % We shall initiate local runtime.
- case inviso:init_tracing(TracerData) of
- ok ->
- gen_server:reply(From,{ok,{self(),ok}}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)};
- {error,Reason} -> % It might have become suspended?!
- gen_server:reply(From,{error,Reason}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)}
- end;
- true -> % We shall not pass this one on.
- gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [],
- [?LOCAL_RUNTIME],
- SafetyCatches,
- Dbg)}
- end;
-init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
- case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
- {ok,States} -> % A list of {Node,{State,Status},Opts}.
- {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
- case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
- {ok,Result} -> % Resulted in state changes!
- RTStates=set_tracing_rtstates(to_rtstates(States),Result),
- ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
- gen_server:reply(From,{ok,{self(),ReplyValue}}),
- {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
- NodeParams2,Nodes2,SafetyCatches,Dbg)};
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,{init_tracing,Reason}}),
- {stop,{init_tracing,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end;
- {error,Reason} -> % Unable to get the state/status.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,Reason}),
- {stop,{error,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end.
-%% ------------------------------------------------------------------------------
-
-%% To stop a session means stop the tracing and remove all local files on the
-%% runtime nodes. We do have a table with all tracer data and that is how we are
-%% going to recreate what files to remove.
-%% Since runtime components may actually change state when this procedure is
-%% on-going, we do not care! It is the state in the session handling process at
-%% the time of start of this procedure which is used.
-handle_call(cancel_session,_From,LD) ->
- CtrlNode=get_ctrlnode_ld(LD),
- RTStates=get_rtstates_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok-> % Hopefully all nodes are stopped now.
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- TRDstorage=get_trdstorage_ld(LD),
- remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
- {stop,normal,ok,LD}; % LD actually not correct now!
- {error,Reason} -> % Some serious error when stop_tracing.
- {stop,normal,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% *Stop all tracing on runtime components still tracing.
-%% *Copy all local log files to the collection directory.
-handle_call({stop_session,Dir,Prefix},_From,LD) ->
- case check_directory_exists(Dir) of % Check that this directory exists here.
- true ->
- RTStates=get_rtstates_ld(LD),
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok -> % Hopefully no node is still tracing now.
- TRDstorage=get_trdstorage_ld(LD),
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {FailedNodes,FetchedFiles}=
- transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
- TRDstorage,Dbg,AvailableNodes),
- RemoveNodes= % We only delete local logs where fetch ok.
- lists:filter(fun(N)->
- case lists:keysearch(N,1,FailedNodes) of
- {value,_} ->
- false;
- false ->
- true
- end
- end,
- AvailableNodes),
- remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
- {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
- {error,Reason} -> % Some general failure, quit.
- {stop,normal,{error,Reason},LD}
- end;
- false -> % You specified a non-existing directory!
- {reply,{error,{faulty_dir,Dir}},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-handle_call({reactivate,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- {OurNodes,OtherNodes}=
- remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
- CtrlNode=get_ctrlnode_ld(LD),
- ACTstorage=get_actstorage_ld(LD),
- case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
- {ok,Results} -> % A list of {Node,Result}.
- if
- OtherNodes==[] -> % Normal case, no non-session nodes.
- {reply,{ok,Results},LD};
- true -> % Add error values for non-session nodes.
- {reply,
- {ok,
- lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
- Results},
- LD}
- end;
- {error,Reason} -> % Then this error takes presidence.
- {reply,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% Call-back for set trace-pattern for both global and local functions.
-handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
- Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
- {reply,Reply,LD};
-handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
- {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
- Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
- {reply,ErrorReply++Reply,LD};
-%% ------------------------------------------------------------------------------
-
-%% Call-back handling the removal of both local and global trace-patterns.
-%% NOT IMPLEMENTED YET.
-handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
- Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
- {reply,Reply,LD};
-%% ------------------------------------------------------------------------------
-
-handle_call({tpm_localnames,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm_ms,
- [Mod,Func,Arity,MSname,MS],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- ctpm_ms,
- [Mod,Func,Arity,MSname],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-%% Call-back for setting process trace-flags. Handles both distributed and non-
-%% distributed case.
-handle_call({tf,TraceConfList},From,LD) ->
- handle_call({tf,all,TraceConfList},From,LD);
-handle_call({tf,Nodes,TraceConfList},_From,LD) ->
- {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
- Nodes,
- TraceConfList,
- get_actstorage_ld(LD),
- get_rtstates_ld(LD)),
- {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-
-
-handle_call(get_loopdata,_From,LD) ->
- io:format("The loopdata:~n~p~n",[LD]),
- {reply,ok,LD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Clause handling an incomming state-change event from the control component.
-%% Note that it does not have to be one of our nodes since it is not possible
-%% to subscribe to certain node-events.
-%% We may very well get state-change events for state-changes we are the source
-%% to our selves. Those state-changes are already incorporated into the RTStates.
-%% There is however no harm in doing them again since we know that this event
-%% message will reach us before a reply to a potentially following state-change
-%% request will reach us. Hence we will do all state-changes in the correct order,
-%% even if sometimes done twice.
-handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case {State,Status} of
- {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
- NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
- Node,
- get_rtstates_ld(LD),
- get_trdstorage_ld(LD)),
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_trdstorage_ld(NewTracerData,
- put_rtstates_ld(NewRTStates,LD))};
- _ -> % In all other cases, just fix rtstates.
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)}
- end;
- _ ->
- {noreply,LD}
- end;
-%% If a new runtime component connects to our trace control component, and it is
-%% in our list of runtime components belonging to this session, we may update its
-%% state to now being present. Otherwise it does not belong to this session.
-%% Note that we avoid updating an already connected runtime component. This
-%% can happend if it connected by itself after we started the session handler,
-%% but before we managed to initiate tracing. Doing so or not will not result in
-%% any error in the long run, but during a short period of time we might be
-%% prevented from doing things with the runtime though it actually is tracing.
-handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
- {ok,unavailable} -> % This is the situation when we update!
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)};
- _ -> % In all other cases, let it be.
- {noreply,LD}
- end;
- _ -> % Not from our control component.
- {noreply,LD}
- end;
-%% If a runtime component disconnects we mark it as unavailable. We must also
-%% remove all saved trace-flags in order for them to not be accidently reactivated
-%% should the runtime component reconnect and then suspend.
-handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
- NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
- {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
- _ ->
- {noreply,LD}
- end;
-handle_info(_,LD) ->
- {noreply,LD}.
-%% ------------------------------------------------------------------------------
-
-%% In terminate we cancel our subscription to event from the trace control component.
-%% That should actually not be necessary, but lets do it the correct way!
-terminate(_,LD) ->
- case get_ctrlnode_ld(LD) of
- void -> % Non-distributed.
- inviso:unsubscribe();
- Node ->
- inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% First level help functions to call-backs.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% Help functions to init.
-%% ------------------------------------------------------------------------------
-
-%% Help function which find out the state/status of the runtime components.
-%% Note that since we have just started subscribe to state changes we must
-%% check our inqueue to see that we have no waiting messages for the nodes
-%% we learned the state/status of. If there is a waiting message we don't
-%% know whether that was a state change received before or after the state
-%% check was done. We will then redo the state-check.
-%% Returns {ok,States} or {error,Reason}.
-%% Where States is [{Node,{State,Status},Opts},...].
-%% Note that {error,Reason} can not occur in the non-distributed case.
-init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
- ok=inviso:subscribe(),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
-init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
- ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
-
-init_rtcomponent_states_2(_,_,_,[],States) ->
- {ok,States};
-init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
- case inviso:get_status() of
- {ok,StateStatus} -> % Got its state/status, now...
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
- [],States),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
- {error,_Reason} -> % The runtime is not available!?
- {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
- end;
-init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
- {ok,NodeResult} ->
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
- {error,Reason} -> % Severe problem, abort the session.
- {error,{get_status,Reason}}
- end.
-
-%% Traverses the list of returnvalues and checks that we do not have an event
-%% waiting in the message queue. If we do have, it is a problem. That node will
-%% be asked about its state again.
-%% Note that it is here we construct the RTStatesList.
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
- receive
- {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
- after
- 0 -> % Not in msg queue, then we're safe!
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,{State,Status},Opts};
- _ -> % No option available, use [].
- {Node,{State,Status},[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
- end;
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,unavailable,Opts};
- _ -> % No option available, use [].
- {Node,unavailable,[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
-init_rtcomponent_states_3(_,_,[],Problems,States) ->
- {Problems,States}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing nodes from NodeParams. The reason for this can either
-%% be that we are using a tool internal tracerdata that shall not be forwarded to
-%% the trace control component, or that the node is actually already part of
-%% another session.
-%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
-remove_nodeparams(Nodes,NodesParams) ->
- remove_nodeparams_2(Nodes,NodesParams,[],[]).
-
-remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
- (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
- Node=element(1,NodeParam),
- Params=element(2,NodeParam), % This is tracerdata!
- case lists:member(Node,Nodes) of
- true -> % Remove this one, in another session.
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
- false -> % Ok so far...
- case is_tool_internal_tracerdata(Params) of
- false -> % Then keep it and use it later!
- remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
- true -> % Since it is, remove it from the list.
- remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
- end
- end;
-remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
-remove_nodeparams_2(_,[],NPAcc,NAcc) ->
- {lists:reverse(NPAcc),NAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds both the nodes which were already part of another
-%% session and the nodes that we actually did not issue any init_tracing for.
-%% Returns a new Result list of [{Node,NodeResult},...].
-init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
- NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
- init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
-
-init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
- [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
-init_fix_resultnodes_2([],_,Result) ->
- Result. % Append Result to the end of the list.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to reactivate.
-%% ------------------------------------------------------------------------------
-
-h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
- {ok,CSuspResults} ->
- {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
- lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
- {[Node|GoodNs],BadNs};
- ({Node,{error,Reason}},{GoodNs,BadNs})->
- {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
- end,
- {[],[]},
- CSuspResults),
- Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
- {ok,BadResults++Results};
- {error,Reason} -> % General failure cancelling suspend.
- {error,{cancel_suspension,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses the list of nodes known to be ours and have
-%% cancelled their suspend. If we fail redoing one of the activities associated
-%% with a node, the node will be reported in the return value as failed. From
-%% that point on its state must be considered unknown since we do not know how
-%% many of the activities were successfully redone.
-h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
- case get_activities_actstorage(Node,ACTstorage) of
- {ok,Activities} -> % The node existed in activity storage.
- {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
- false -> % Node not present in activity storage.
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
- end;
-h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
- lists:reverse(Acc).
-
-%% Help function actually redoing the activity. Note that there must be one
-%% clause here for every type of activity.
-%% Returns {NrGoodCmds,NrBadCmds}.
-%% The number of good or bad commands refers to inviso commands done. If any
-%% of the subparts of such a command returned an error, the command is concidered
-%% no good.
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
- {ok,[{_Node,{ok,Answers}}]} ->
- case h_reactivate_redo_activity_check_tf(Answers) of
- ok ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- error -> % At least oneReports the first encountered error.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
- {ok,[{_Node,ok}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
- {Good,Bad}.
-
-%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
-%% to see if there were any errors.
-h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
- h_reactivate_redo_activity_check_tf(Rest);
-h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
- error;
-h_reactivate_redo_activity_check_tf([]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to tp (setting trace patterns, both local and global).
-%% ------------------------------------------------------------------------------
-
-%% Help function which handles both tpl and tp. Note that the non-distributed case
-%% handled with Nodes='all'.
-%% Returns what shall be the reply to the client.
-h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
- Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
- h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
-h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- SafetyCatches=get_safetycatches_ld(LD),
- case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
- {multinode_expansion,NodeMods} ->
- NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
- {singlenode_expansion,Modules} ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- module ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- wildcard -> % Means do for all modules, no safety.
- h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Note that this function can never be called in the non-distributed case.
-h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
- case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
- {ok,[{Node,Result}]} ->
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
- {error,Reason} -> % Failure, but don't stop.
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
- end;
-h_tp_node_by_node(_,_,_,[],Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function which does the actual call to the trace control component.
-%% Note that Nodes can be a list of nodes (including a single one) or
-%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
-%% detected by the 'void' CtrlNode.
-%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
-%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
-%% {error,Reason}.
-h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
- inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
- case inviso:PatternFunc(TPs) of
- {ok,Result} -> % A list of [Nr1,Nr2,error,...].
- {ok,
- lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
- {AccNr+N,AccErr};
- (error,{AccNr,AccErr}) ->
- {AccNr,AccErr+1}
- end,
- {0,0},
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end;
-h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
- inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
- case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
- {ok,Result} -> % Result is [{Node,Result},...].
- {ok,
- lists:map(fun({Node,{ok,Res}})->
- {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
- {ok,{AccNr+N,AccErr}};
- (error,{AccNr,AccErr}) ->
- {ok,{AccNr,AccErr+1}}
- end,
- {ok,{0,0}},
- Res)};
- ({_Node,{error,Reason}})->
- {error,Reason}
- end,
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions for removing trace-patterns.
-%% ------------------------------------------------------------------------------
-
-%% NOT IMPLEMENTED YET.
-h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
- tbd.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for calling the trace information facility.
-%% ------------------------------------------------------------------------------
-
-
-%% Function handling the meta trace pattern for capturing registration of local
-%% process names.
-h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Functions calling meta trace functions for specified nodes. This function is
-%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
-%% ctpm.
-%% Note that we must store called meta trace functions and their parameters in the
-%% activity storage in order to be able to redo them in case of a reactivate.
-h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for set trace flags.
-%% ------------------------------------------------------------------------------
-
-%% Help function which sets the tracepatterns in TraceConfList for all nodes
-%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
-%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
-%% and NewACTstorage is traceflag storage modified with the flags added to the
-%% corresponding nodes.
-h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
- Reply=inviso:tf(TraceConfList),
- NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
- {Reply,NewACTstorage};
-h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
- AllNodes=get_all_session_nodes_rtstates(RTStates),
- h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
-h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
- {ok,Result} -> % That good we want to modify actstorage!
- NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
- {{ok,Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify actstorage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions to stop_session.
-%% ------------------------------------------------------------------------------
-
-%% This function fetches all local log-files using our stored tracerdata. Note
-%% that there are two major ways of tranfering logfiles. Either via distributed
-%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
-%% But there may be info in the RTStates structure about a common file-system.
-%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
-%% nodenames where problems occurred. Note that problems does not necessarily
-%% mean that no files were copied.
-%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
-%% {ti_log,Files}, listing all files successfully fetched. Note that the
-%% list of fetched files contains sublists of filenames. One for each node and
-%% tracerdata.
-%% In the non-distributed system we always use copy (since the files always
-%% resides locally).
-transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
- if
- CtrlNode==void -> % When non-distributed, always copy!
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
- true -> % The distributed case.
- {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
- {FailedFetchNodes,FetchedFiles}=
- case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
- {ok,Failed,Files} -> % So far no disasters.
- {Failed,Files};
- {error,Reason} -> % Means all fetch-nodes failed!
- inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
- {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
- end,
- {FailedCopyNodes,CopiedFiles}=
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
- {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
- end.
-
-%% Help function which finds out which node we have a common file system with
-%% and from which we must make distributed erlang tranfere.
-%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
-find_logfile_transfer_methods(Nodes,RTStates) ->
- find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
-
-find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
- {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
- case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
- {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
- find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
- {value,_} -> % Can't understand dir option.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
- false -> % Then we want to use fetch instead.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
- end;
-find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
- {FetchAcc,CopyAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transferes all local logfiles according to the tracerdata
-%% stored for the nodes in Nodes.
-%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
-%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
-%% or not at all.
-%% FileNames is a list of list of actually fetched files (the name as it is here, including
-%% Dir). The sublists are files which belong together.
-fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
- LogSpecList=build_logspeclist(Nodes,TRDstorage),
- case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
- fetch_log,
- [LogSpecList,Dir,Prefix])) of
- {ok,Result} ->
- Files=get_all_filenames_fetchlog_result(Result,Dbg),
- FailedNodes=get_all_failednodes_fetchlog_result(Result),
- {ok,FailedNodes,Files};
- {error,Reason} -> % Some general failure!
- {error,{fetch_log,Reason}}
- end.
-
-%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
-%% Note that there may be more than one tracerdata for a node, resulting in multiple
-%% tuples for that node.
-build_logspeclist(Nodes,TRDstorage) ->
- build_logspeclist_2(Nodes,TRDstorage,[]).
-
-build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
- build_logspeclist_2(Rest,
- TRDstorage,
- [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
-build_logspeclist_2([],_,Acc) ->
- lists:flatten(Acc).
-
-%% Help function which translates inviso:fetch_log return values to what I
-%% want!
-inviso_fetch_log({error,Reason}) ->
- {error,Reason};
-inviso_fetch_log({_Success,ResultList}) ->
- {ok,ResultList}.
-
-%% Help function which collects all filenames mentioned in a noderesult structure.
-%% The files may or may not be complete.
-%% Returns a list of list of filenames. Each sublist contains files which belong
-%% together, i.e because they are a wrap-set.
-get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
- get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
-
-get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
- when Success=/=error, list(FileInfo) ->
- SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
-get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
- inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
-get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
- Accum.
-
-get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
- FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
- get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
-get_all_filenames_fetchlog_result_3([],SubAccum) ->
- SubAccum.
-
-%% Help function which traverses a noderesult and builds a list as return
-%% value containing the nodenames of all nodes not being complete.
-%% Note that a node may occur multiple times since may have fetched logfiles
-%% for several tracerdata from the same node. Makes sure the list contains
-%% unique node names.
-%% Returns a list nodes.
-get_all_failednodes_fetchlog_result(NodeResult) ->
- get_all_failednodes_fetchlog_result_2(NodeResult,[]).
-
-get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
-get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
- case lists:member(Node,Acc) of
- true -> % Already in the list.
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
- false -> % Not in Acc, add it!
- get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
- end;
-get_all_failednodes_fetchlog_result_2([],Acc) ->
- Acc.
-%% ------------------------------------------------------------------------------
-
-%% Help function which copies files from one location to Dir and at the same time
-%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
-%% reason the node information is still part of NodeSpecs is that otherwise we can
-%% not report faulty nodes. Note that one node may occur multiple times since there
-%% may be more than one tracerdata for a node.
-%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
-%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
-fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
- CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
-
-fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
- case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
- {0,LocalFiles} -> % Copy went ok and zero errors.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
- {_N,LocalFiles} -> % Copied files, but some went wrong.
- case lists:member(Node,FailedNodes) of
- true -> % Node already in FailedNodes.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
- [{Node,LocalFiles}|Files]);
- false -> % Node not marked as failed, yet.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
- [{Node,LocalFiles}|Files])
- end
- end;
-fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
- {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
-
-fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
- {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
- fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
-fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
- {Errors,Results}.
-
-%% For each file of one file-type (e.g. trace_log).
-fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
- DestName=Prefix++filename:basename(File),
- Destination=filename:join(Dir,DestName),
- case do_copy_file(File,Destination) of
- ok ->
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
- {error,Reason} ->
- inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
- end;
-fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
- {Errors,LocalFiles}.
-
-%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
-%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
-%% is a complete path to a file to be copied.
-build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
- build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
-
-%% For each node specified in the NodeSpecList.
-build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
- CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
- build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
-build_copylist_2(_,_,[],_,Acc) ->
- lists:flatten(Acc).
-
-%% For each tracerdata found for the node.
-build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
- case inviso:list_logs(TRD) of
- {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
- {ok,no_log} -> % This tracedata not associated with any log.
- build_copylist_3(void,Dbg,SourceDir,Node,Rest);
- {error,Reason} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(void,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
- {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
- {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {ok,[{Node,{error,Reason}}]} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(_,_,_,_,[]) ->
- [].
-
-%% Help function which makes a [{Type,Files},...] list where each file in Files
-%% is with full path as found from our file-system.
-build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
- NewFiles=
- lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
- [],
- Files),
- build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
-build_copylist_4(_,[],Accum) ->
- Accum.
-
-
-%% Help function which copies a file using os:cmd.
-%% Returns 'ok' or {error,Reason}.
-do_copy_file(Source,Destination) ->
- case os:type() of
- {win32,_} ->
- os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
- ok;
- {unix,_} ->
- os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
- ok
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function going through the Nodes list and checking that only nodes
-%% mentioned in OurNodes gets returned. It also makes the nodes in the return
-%% value unique.
-remove_nodes_not_ours(Nodes,OurNodes) ->
- remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
-
-remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
- case lists:member(Node,OurNodes) of
- true -> % Ok it is one of our nodes.
- case lists:member(Node,OurAcc) of
- true -> % Already in the list, skip.
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
- end;
- false ->
- case lists:member(Node,OtherAcc) of
- true ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
- end
- end;
-remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
- {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns 'true' or 'false' depending on if TracerData is
-%% meant to be used by the session handler (true) or if it supposed to be passed
-%% on to the trace system.
-is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that all nodes in the first list of nodes exists
-%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
-%% as one incorrect node was found.
-check_our_nodes([Node|Rest],AllNodes) ->
- case lists:member(Node,AllNodes) of
- true ->
- check_our_nodes(Rest,AllNodes);
- false -> % Then we can stop right here.
- false
- end;
-check_our_nodes([],_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that a directory actually exists. Returns 'true' or
-%% 'false'.
-check_directory_exists(Dir) ->
- case file:read_file_info(Dir) of
- {ok,#file_info{type=directory}} ->
- true;
- _ -> % In all other cases it is not valid.
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
-%% of only tracing runtime components. Not that there will actually be any difference
-%% since the return value does not reflect how stopping the nodes went.
-%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
-stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
- case inviso:stop_tracing() of
- {ok,_State} ->
- ok;
- {error,Reason} -> % We actually don't care.
- inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
- ok
- end;
-stop_all_tracing(void,_,_) -> % There is no local runtime started.
- ok;
-stop_all_tracing(CtrlNode,Dbg,Nodes) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
- {ok,Result} -> % The result is only used for debug.
- Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
- (_,Acc)->Acc
- end,
- [],
- Result),
- if
- Failed==[] ->
- ok;
- true ->
- inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
- ok
- end;
- {error,Reason} ->
- {error,{stop_tracing,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all local logs using the tracerdata to determine what
-%% logs to remove from where.
-%% There is no significant return value since it is not really clear what to do
-%% if removal went wrong. The function can make debug-reports thought.
-remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
- LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
- case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
- {ok,Results} ->
- case look_for_errors_resultlist(Results) of
- [] -> % No errors found in the result!
- true;
- Errors ->
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
- true
- end;
- {error,Reason} -> % Some general error.
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
- true
- end.
-
-%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
-%% we must build one tuple for each tracerdata for one node.
-build_logspeclist_remove_logs(Nodes,TRDstorage) ->
- [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses a resultlist from an inviso function. Such are
-%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
-%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
-%% or {ok,FileName}.
-%% Returns a list of {Node,[{error,Reason},...]}.
-look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
- [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
-look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
- case look_for_errors_resultlist_2(NResults,[]) of
- [] ->
- look_for_errors_resultlist(Rest);
- Errors -> % A list of lists.
- [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
- end;
-look_for_errors_resultlist([_|Rest]) ->
- look_for_errors_resultlist(Rest);
-look_for_errors_resultlist([]) ->
- [].
-
-look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
- case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
- [] -> % No errors for this node.
- look_for_errors_resultlist_2(Rest,Accum);
- Errors -> % A list of at least one error.
- look_for_errors_resultlist_2(Rest,[Errors|Accum])
- end;
-look_for_errors_resultlist_2([],Accum) ->
- Accum.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the loopdata structure.
-%% Its main purpose is to store information about runtime components participating
-%% in the session and their current status.
-%% ------------------------------------------------------------------------------
-
--record(ld,{parent,
- ctrlnode,
- ctrlpid, % To where to send inviso cmd.
- rtstates,
- tracerdata,
- safetycatches,
- dbg,
- actstorage % Activity storage, for reactivate.
- }).
-
-%% Function creating the initial datastructure.
-%% The datastructure is [{Node,State},...].
-%%
-%% The tracerdata table is a bag simply for the reason that if we try to insert
-%% the same tracerdata for a node twice, we will end up with one tracerdata after
-%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
-%% come as a state-change too.
-mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
- TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
- TRDtid=ets:new(TRDtableName,[bag]),
- ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
- ACTtid=ets:new(ACTtableName,[bag]),
- mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
- #ld{parent=Parent, % The tool main process.
- ctrlnode=CtrlNode, % Node name where the control component is.
- ctrlpid=CtrlPid, % The process id of the control component.
- rtstates=RTStates, % All nodes and their state/status.
- tracerdata=TRDtid,
- safetycatches=SafetyCatches,
- dbg=Dbg,
- actstorage=ACTtid
- }.
-
-%% Help function which inserts tracer data for the nodes. Note that we can get
-%% tracer data either from the return value from init_tracing or by asking the
-%% node for it. The latter is necessary for the nodes which were marked not to
-%% be initiated by the session handler. This maybe because those nodes have
-%% autostarted.
-mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
- mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
- mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
-
-mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_nodeparams(TId,Rest);
-mk_ld_fill_tracerdata_nodeparams(_,[]) ->
- ok.
-
-mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
- ok;
-mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
- case inviso:get_tracerdata() of
- {error,_Reason} -> % Perhaps in state new or disconnected.
- ok; % Do nothing.
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData})
- end;
-mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
- case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
- {ok,Results} ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Results);
- {error,_Reason} -> % Strange, we will probably crash later.
- ok
- end.
-
-mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
-mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
-mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
- CtrlNode.
-%% ------------------------------------------------------------------------------
-
-
-get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
- CtrlPid.
-%% ------------------------------------------------------------------------------
-
-get_rtstates_ld(#ld{rtstates=RTStates}) ->
- RTStates.
-
-put_rtstates_ld(NewRTStates,LD) ->
- LD#ld{rtstates=NewRTStates}.
-%% ------------------------------------------------------------------------------
-
-get_trdstorage_ld(#ld{tracerdata=TId}) ->
- TId.
-
-put_trdstorage_ld(_NewTId,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds the current tracerdata of node Node to the tracerdata
-%% storage. We only want to add tracerdata we have not seen before. We therefore
-%% avoid adding it if the node already is in state ?TRACING.
-%% Returns a new tracerdata (what ever it is)!
-add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
- case get_statestatus_rtstates(Node,RTStates) of
- {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
- TId; % Then do nothing.
- {ok,_} -> % Since we were not tracing before.
- case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData});
- no_tracerdata -> % Strange, how could we become tracing
- ok;
- {error,_Reason} -> % The node perhaps disconnected!?
- ok
- end;
- false -> % Very strange, not our node!
- ok % Do nothing.
- end.
-
-add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
- case inviso:get_tracerdata() of
- {ok,TracerData} ->
- {ok,TracerData};
- {error,no_tracerdata} ->
- no_tracerdata;
- {error,Reason} ->
- {error,Reason}
- end;
-add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
- {ok,[{Node,{ok,TracerData}}]} ->
- {ok,TracerData};
- {ok,[{Node,{error,no_tracerdata}}]} ->
- no_tracerdata;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-get_safetycatches_ld(#ld{safetycatches=SCs}) ->
- SCs.
-%% ------------------------------------------------------------------------------
-
-get_dbg_ld(#ld{dbg=Dbg}) ->
- Dbg.
-%% ------------------------------------------------------------------------------
-
-get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
- ACTstorage.
-
-put_actstorage_ld(_NewACTstorage,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the rtstates structure (which is a substructure of loopdata).
-%% It is either:
-%% [{Node,StateStatus,Opts},...]
-%% Node is either the node name of the runtime component erlang node or
-%% ?LOCAL_RUNTIME as returned from the trace control component.
-%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
-%% Status is the returnvalue from trace control component.
-%% i.e: running | {suspended,Reason}
-%% ------------------------------------------------------------------------------
-
-%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
-to_rtstates(ListOfStates) when list(ListOfStates) ->
- ListOfStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
-from_rtstates(RTStates) ->
- RTStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes an rtstates structure and a result as returned from
-%% init_tracing. The RTStates is modified for the nodes that changed state as a
-%% result of successful init_tracing.
-%% Returns a new RTStates.
-set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
- case lists:keysearch(Node,1,Result) of
- {value,{_,ok}} -> % Means state-change to tracing!
- [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
- _ -> % Otherwise, leave it as is.
- [E|set_tracing_rtstates(Rest,Result)]
- end;
-set_tracing_rtstates([],_Result) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component.
-%% Returns a new RTStates structure. Note that Node must not necessarily be one
-%% of the nodes in the session. Meaning that Node shall not be added to RTStates
-%% should it not already be in there.
-statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component. The
-%% state/status is set to 'unavailable'.
-%% Returns a new RTStates structure.
-set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function finding the statestatus associated with Node in the RTStates structure.
-%% Returns {ok,StateStatus} or 'false'.
-get_statestatus_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,StateStatus,_}} ->
- {ok,StateStatus};
- false ->
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns a list of all nodes that are currently marked
-%% as available to us in the runtime state structure.
-get_all_available_nodes_rtstates(RTStates) ->
- get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
- (_)->true
- end,
- RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Help function returning a list of all nodes belonging to this session.
-get_all_session_nodes_rtstates(RTStates) ->
- lists:map(fun({Node,_,_})->Node end,RTStates).
-%% ------------------------------------------------------------------------------
-
-%% Function which returns a list of nodes that are indicated as tracing in the
-%% RTStates structure.
-get_all_tracing_nodes_rtstates(RTStates) ->
- lists:map(fun({N,_,_})->N end,
- lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Returns the options associated with Node in the RTStates structure.
-get_opts_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- {ok,Opts};
- false ->
- false
- end.
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the tracerdata structure, which is a part of the loopdata.
-%% The tracerdata structure is an ETS-table of type bag storing:
-%% {Node,TracerData}.
-%% Note that there can of course be multiple entries for a node.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a tracerdata loopdata structure and returns a list
-%% of all stored tracerdata for a certain Node.
-find_tracerdata_for_node_trd(Node,TRD) ->
- case ets:lookup(TRD,Node) of
- Result when list(Result) ->
- lists:map(fun({_Node,TracerData})->TracerData end,Result);
- _ -> % Should probably never happend.
- []
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the activity storage structure, which is part of the
-%% loopdata. It stores entries about things that needs to be "redone" in case
-%% of a reactivation of the node. The time order is also important.
-%% Note that for every ActivityType there must be a "handler" in the reactivation
-%% functionality.
-%%
-%% The structure is a bag of {Node,ActivityType,What}.
-%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
-%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
-%% /{Op,[]}
-%% TraceConfList=[{Proc,Flags},...]
-%% How=true|false
-%% ------------------------------------------------------------------------------
-
-%% Function that adds meta-pattern activities to the activity storage. Note
-%% that one of the parameters to the function is a return value from an
-%% inviso call. In that way we do not enter activities that were unsuccessful.
-%% Op can be either the setting or clearing of a meta pattern.
-%% Returns a new ACTstorage.
-add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-
-%% Function that adds process trace-flags to the activity storage. Note that one
-%% of the parameters is the return value from an inviso function. Meaning that
-%% if the flags failed in their entirety, no activity will be saved. If only
-%% some of the flags failed, we will not go through the effort of trying to find
-%% out exactly which.
-%% Returns a new activity storage structure.
-add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
-%% Finds all activities associated with Node. Returns a list of them in the
-%% same order as they were inserted.
-get_activities_actstorage(Node,ACTstorage) ->
- case ets:lookup(ACTstorage,Node) of
- [] ->
- false;
- Result when list(Result) ->
- {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function removing all activity entries associated with Node. This is useful
-%% if the Node disconnects for instance.
-del_node_actstorage(Node,ACTstorage) ->
- ets:delete(ACTstorage,Node),
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
diff --git a/lib/inviso/test/Makefile b/lib/inviso/test/Makefile
deleted file mode 100644
index 2650faa392..0000000000
--- a/lib/inviso/test/Makefile
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-MODULES = \
- inviso_tool_SUITE
-
-ERL_FILES= $(MODULES:%=%.erl)
-
-TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-INSTALL_PROGS= $(TARGET_FILES)
-
-EMAKEFILE=Emakefile
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/inviso_test
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
-
-EBIN = .
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-make_emakefile:
- $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\
- > $(EMAKEFILE)
-
-tests debug opt: make_emakefile
- erl $(ERL_MAKE_FLAGS) -make
-
-clean:
- rm -f $(EMAKEFILE)
- rm -f $(TARGET_FILES)
- rm -f core
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
-
-release_tests_spec: make_emakefile
- $(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) "$(RELSYSDIR)"
- chmod -R u+w "$(RELSYSDIR)"
- @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
-
-release_docs_spec:
-
-
diff --git a/lib/inviso/test/inviso.cover b/lib/inviso/test/inviso.cover
deleted file mode 100644
index e23b9fa59b..0000000000
--- a/lib/inviso/test/inviso.cover
+++ /dev/null
@@ -1,2 +0,0 @@
-{incl_app,inviso,details}.
-
diff --git a/lib/inviso/test/inviso.spec b/lib/inviso/test/inviso.spec
deleted file mode 100644
index 49f9b0b460..0000000000
--- a/lib/inviso/test/inviso.spec
+++ /dev/null
@@ -1 +0,0 @@
-{suites,"../inviso_test",all}.
diff --git a/lib/inviso/test/inviso_tool_SUITE.erl b/lib/inviso/test/inviso_tool_SUITE.erl
deleted file mode 100644
index e14f32de44..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE.erl
+++ /dev/null
@@ -1,1166 +0,0 @@
-%%
-%% %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%
-%%
-%%
-%% Description:
-%% Test suite for the inviso_tool. It is here assumed that inviso works
-%% properly.
-%%
-%% Authors:
-%% Lennart Öhman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool_SUITE).
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("kernel/include/file.hrl").
-
--define(l,?line).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [dist_basic_1, dist_rtc, dist_reconnect, dist_adopt,
- dist_history, dist_start_session_special].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% -----------------------------------------------------------------------------
-
-init_per_suite(Config) ->
- case test_server:is_native(lists) of
- true ->
- {skip,"Native libs -- tracing doesn't work"};
- false ->
- Config
- end.
-%% -----------------------------------------------------------------------------
-
-end_per_suite(_Config) ->
- ok.
-%% -----------------------------------------------------------------------------
-
-%% For each distributed testcase, we need two other distributed nodes to run the
-%% runtime components on. Since they are freshly started every time there is no
-%% need to clean them up first.
-init_per_testcase(_Case,Config) ->
- ?l TH=test_server:timetrap(100000),
- ?l {ok,Node1}=test_server:start_node(inviso1,peer,[]),
- ?l {ok,Node2}=test_server:start_node(inviso2,peer,[]),
- ?l SuiteDir=filename:dirname(code:which(?MODULE)),
-
- %% Otherwise peer nodes will not find this module!
- ?l true=rpc:call(Node1,code,add_patha,[SuiteDir]),
- ?l true=rpc:call(Node2,code,add_patha,[SuiteDir]),
-
- %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-
-% %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT, windows.
-% ?l rpc:call(Node1,code,add_patha,["C:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["C:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["C:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["C:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-
- ?l ok=rpc:call(Node1,application,start,[runtime_tools]),
- ?l ok=rpc:call(Node2,application,start,[runtime_tools]),
- ?l timer:sleep(100), % Problem with autostarted runtime.
- %% The following is a test that the inviso_rt processes which are autostarted
- %% are now gone.
-
- ?l ok=poll(rpc,call,[Node1,erlang,whereis,[inviso_rt]],undefined,20),
- ?l ok=poll(rpc,call,[Node2,erlang,whereis,[inviso_rt]],undefined,20),
-
- NewConfig1=insert_remotenode_config(inviso1,Node1,Config),
- NewConfig2=insert_remotenode_config(inviso2,Node2,NewConfig1),
- insert_timetraphandle_config(TH,NewConfig2).
-%% -----------------------------------------------------------------------------
-
-end_per_testcase(_Case,Config) ->
- ?l test_server:stop_node(get_remotenode_config(inviso1,Config)),
- ?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
- ?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
- ?l case whereis(inviso_tool) of % In case inviso_tool did not stop.
- Pid when is_pid(Pid) ->
- ?l io:format("Had to kill inviso_tool!~n",[]),
- ?l exit(Pid,kill);
- _ ->
- true
- end,
- ?l case whereis(inviso_rt) of % In case we ran a runtime here.
- Pid2 when is_pid(Pid2) ->
- ?l io:format("Had to kill inviso_rt!~n",[]),
- ?l exit(Pid2,kill);
- _ ->
- true
- end,
- ?l case whereis(inviso_c) of % In case we ran the controll component here.
- Pid3 when is_pid(Pid3) ->
- ?l io:format("Had to kill inviso_c!~n",[]),
- ?l exit(Pid3,kill);
- _ ->
- true
- end,
- NewConfig1=remove_remotenode_config(inviso1,Config),
- NewConfig2=remove_remotenode_config(inviso2,NewConfig1),
- remove_timetraphandle_config(NewConfig2).
-%% -----------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Testcases.
-%% ==============================================================================
-
-%% -----------------------------------------------------------------------------
-%% Functional tests:
-%% API:
-%% start/0 dist_basic_1
-%% stop/0 dist_basic_1
-%% reconnect_nodes/1 dist_reconnect
-%% start_session/0 dist_basic_1
-%% reinitiate_session/1 dist_reconnect
-%% stop_session/0 dist_basic_1
-%% atc/3 dist_basic_1
-%% sync_atc/3 dist_basic_1
-%% sync_rtc/2, dist_rtc
-%% dtc/2 dist_basic_1
-%% sync_dtc/2 dist_basic_1
-%% inviso/2 dist_basic_1
-%% reactivate/1 dist_basic_1
-%% get_autostart_data/2 dist_basic_1
-%% get_activities/0 dist_basic_1
-%% save_history/1 dist_history
-%% restore_session/1 dist_history
-%% get_node_status/1 dist_basic_1
-%% get_session_data/0 dist_basic_1
-%% flush/0 dist_basic_1
-%% -----------------------------------------------------------------------------
-
-%% Non functional tests:
-%% Run the control component on both the dist_history
-%% same node as the tool and on a
-%% different node.
-%% Let a trace case crash in its execution dist_basic_1
-%% and check that it does not become
-%% part of the history.
-%% Check that tracer data becomes what the NOT IMPLEMENTED
-%% tdg function generates.
-%% Check that all inviso runtime stop_inviso_tool/2
-%% components terminate if the tool is
-%% killed.
-%% Check that activation/deactivation dist_basic_1
-%% cancels each other out in the history.
-%% Check that regexp expansion is done on dist_reconnect
-%% another node if regexp_node is down.
-%% Test that tool-commands activating dist_basic_1
-%% something done during a reactivation
-%% are actually done a bit later at the
-%% reactivated node (this since the the
-%% command being reactivated at the momen
-%% at the reactivating node may not
-%% be finished at the time the new tool
-%% command is issued).
-%% Check that deactivating tracecases are dist_basic_1
-%% not redone at a reactivating node.
-%% (to prevent it from being redone and
-%% then just deactivated).
-%% Check that on-going reactivators and NOT IMPLEMENTED
-%% tracecases are killed when stop_session.
-%% Check that inviso_tool can and will adopt
-%% a running runtime component. dist_adopt
-%% -----------------------------------------------------------------------------
-
--define(TC_DEF_FILE,filename:join(DataDir,"tracecase_def.txt")).
-
-%% TEST CASE: Basic, distributed, start of inviso_tool with simple tracing.
-dist_basic_1(doc) -> ["Simple test"];
-dist_basic_1(suite) -> [];
-dist_basic_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- %% Now we know that all inviso runtimes are running and are not tracing.
- ?l {error,no_session}=inviso_tool:inviso(tpl,[lists,module_info,0,[]]),
- ?l {error,no_session}=inviso_tool:get_session_data(),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- ?l {ok,{tracing,1,TDGargs}}=inviso_tool:get_session_data(),
- ?l true=is_list(TDGargs),
- %% Check that the initial tracecase has been executed at all tracing nodes.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{lists,module_info,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Let the processes start
- timer:sleep(100),
-
- %% Find the pids of the test processes.
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l true=(1=<length(TestProcs)),
-
- %% Activate a trace case.
- ?l {error,unknown_tracecase}=inviso_tool:atc(nonexistant,id,[]),
- ?l {error,{missing_variable,'ProcessName'}}=
- inviso_tool:atc(tracecase1,id1,[]),
- ?l ok=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l {error,activating}=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l {ok,[{tracecases,[{{tracecase1,id1},activating}]}]}=inviso_tool:get_activities(),
- ?l timer:sleep(1700), % There is a 500 ms delay in the tracecase.
- ?l {error,already_started}=
- inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
-
- %% Now check that the trace case was executed at Nodes.
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[call]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
- ?l lists:foreach(fun(P) ->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,0},traced]],
- {traced,false},
- 1)
- end,
- TestProcs),
-
- %% Test inviso_tool:inviso/2.
- ?l {ok,NodeResults1}=inviso_tool:inviso(tpl,[math,module_info,0,[]]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults1),
- ?l lists:foreach(fun(P) ->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,0},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
-
- %% Test inviso_tool:sync_atc/3.
- ?l a_return_value=inviso_tool:sync_atc(tracecase2,id2,[]), % This will take 3000 ms.
- ?l lists:foreach(fun(P) ->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,pi,0},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
-
- %% Test the reactivation mechanism.
- ?l [ANode|OtherNodes]=Nodes, % Get a node to suspend.
- ?l {ok,{tracing,running}}=inviso_tool:get_node_status(ANode),
- ?l {ok,[{ANode,ok}]}=rpc:call(CNode,inviso,suspend,[[ANode],test]),
- ?l [APid|_]=TestProcs, % The first process is at ANode.
- %% Now check that trace flags were removed at ANode. This is actually testing inviso.
- ?l ok=poll(rpc,call,[node(APid),erlang,trace_info,[APid,flags]],{flags,[]},10),
- ?l {ok,{tracing,suspended}}=inviso_tool:get_node_status(ANode),
- %% Now reactivate it and expect the history to be redone. But it will take
- %% 3000 ms since there is a delay in tracecase2. Use that delay to issue a new
- %% tool command.
- ?l ok=inviso_tool:reactivate(ANode),
- ?l {ok,reactivating}=inviso_tool:get_node_status(ANode),
- ?l {ok,NodeResults2}=inviso_tool:inviso(tpl,[math,sin,1,[]]),
- ?l true=check_noderesults(OtherNodes,{ok,[1]},NodeResults2),
- %% Verify that the inviso command was not done (yet) at ANode.
- ?l {traced,false}=rpc:call(ANode,erlang,trace_info,[{math,sin,1},traced]),
- ?l {ok,[{reactivating_nodes,[ANode]}]}=inviso_tool:get_activities(),
- ?l timer:sleep(3600),
- %% Now the history shall have been redone including the new inviso command.
- ?l ok=poll(rpc,call,[ANode,erlang,trace_info,[{math,sin,1},traced]],{traced,local},10),
- ?l {flags,[call]}=rpc:call(ANode,erlang,trace_info,[APid,flags]),
- ?l {ok,[]}=inviso_tool:get_activities(),
-
- %% Check the get_autostart function. We know that we use the standard options
- %% generator and the tracecases activated above.
- ?l {ok,{AutostartData1,NodeResults3}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l true=check_noderesults(Nodes,
- fun({_N,{ok,{[{dependency,infinity}],{tdg,{_M,_F,TDlist}}}}})
- when is_list(TDlist)->
- true;
- (_) ->
- false
- end,
- NodeResults3),
- %% Check the tracecase that shall be activated and their order.
- ?l TraceCaseFileNameInit=filename:join(DataDir,"./tracecase_init.trc"),
- ?l TraceCaseFileName1=filename:join(DataDir,"./tracecase1_on.trc"),
- ?l TraceCaseFileName2=filename:join(DataDir,"./tracecase2_on.trc"),
- ?l [{file,{TraceCaseFileNameInit,[]}},
- {file,{TraceCaseFileName1,[{'ProcessName',inviso_tool_test_proc}]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}},
- {file,{TraceCaseFileName2,[]}},
- {mfa,{inviso,tpl,[math,sin,1,[]]}}]=AutostartData1,
-
- %% Try to activate a faulty tracecase. We shall get the same history as before.
- ?l ok=inviso_tool:atc(tracecase3,id3,[]),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- ?l {ok,{AutostartData1,NodeResults3}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
-
- %% Now deactivate a trace case.
- ?l inviso_tool:dtc(tracecase1,id1),
- %% Check that the now deactivated trace case is not part of autostart data
- %% if requested from the tool.
- ?l {ok,{AutostartData2,_NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l [{file,{TraceCaseFileNameInit,[]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}},
- {file,{TraceCaseFileName2,[]}},
- {mfa,{inviso,tpl,[math,sin,1,[]]}}]=AutostartData2,
- %% Now tracing shall be removed since we deactivated tracecase1.
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,false},
- 10)
- end,
- TestProcs),
-
- %% Suspend the ANode again and check that when it is reactivated that
- %% tracecase1 is not redone at all. We use an ets table with a counter that is
- %% incremented every time the tracecase1 is executed.
- ?l {ok,[{ANode,ok}]}=rpc:call(CNode,inviso,suspend,[[ANode],testagain]),
- ?l [{counter,SideEffectCounter1}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l true=(SideEffectCounter1>0),
- ?l ok=inviso_tool:reactivate(ANode),
- ?l timer:sleep(3000), % The delay in tracecase2.
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- %% Now the reactivation is done, check that tracecase1 was not redone at ANode.
- ?l [{counter,SideEffectCounter1}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
-
- %% Deactivate tracecase2.
- ?l another_return_value=inviso_tool:sync_dtc(tracecase2,id2),
- %% Immediately check the autostart data (again!). This time we want to see
- %% that the two inviso commands have been joined since there is no tracecase
- %% in between.
- ?l {ok,{AutostartData3,NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l [{file,{TraceCaseFileNameInit,[]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}},
- {mfa,{inviso,tpl,[math,sin,1,[]]}}]=AutostartData3,
-
- %% Check that a deactivating tracecase is not redone at a reactivating node.
- ?l inviso_tool:sync_atc(tracecase5,id5,[]), % Updates the counter.
- %% Yet again suspend the node when we know that tracecase5 has been executed.
- ?l {ok,[{ANode,ok}]}=rpc:call(CNode,inviso,suspend,[[ANode],testagain2]),
- ?l timer:sleep(100), % Subscription reaches the tool.
- ?l [{counter,SideEffectCounter2A}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l [BNode|_]=OtherNodes,
- ?l [{counter,SideEffectCounter2B}]=rpc:call(BNode,ets,lookup,[test_proc_tab,counter]),
- ?l ok=inviso_tool:dtc(tracecase5,id5), % In here there is a 2000 ms delay!
- ?l ok=inviso_tool:reactivate(ANode),
- %% Check that the reactivator is done, but that the tracecase remains. The
- %% reactivator should be done pretty quickly since there are no delays in the
- %% still active tracecases.
- ?l ok=poll(inviso_tool,
- get_activities,
- [],
- {ok,[{tracecases,[{{tracecase5,id5},deactivating}]}]},
- 10),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},30),
- ?l [{counter,SideEffectCounter2A}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l SideEffectCounter2B1=SideEffectCounter2B+1,
- ?l [{counter,SideEffectCounter2B1}]=rpc:call(BNode,ets,lookup,[test_proc_tab,counter]),
-
- %% Check the flush function. It is difficult to find out if it really flushed.
- ?l {ok,NodeResults4}=inviso_tool:flush(),
- ?l true=check_noderesults(Nodes,ok,NodeResults4),
-
- %% Check that this function still has a trace pattern. We are going to stop session
- %% and check that it is still there.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{math,sin,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
-
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- ?l {ok,{not_tracing,1,TDGargs}}=inviso_tool:get_session_data(),
-
- ?l {ok,NodeResults5}=inviso_tool:flush(Nodes),
- ?l true=check_noderesults(Nodes,fun({_,{error,_}})->true;(_)->false end,NodeResults5),
- ?l {ok,[]}=inviso_tool:flush(),
-
- %% Check that you can not start trace cases when the session is stopped.
- ?l {error,no_session}=inviso_tool:atc(tracecase2,id3,[]),
- %% But the history shall be there to retrieve.
- ?l {ok,{AutostartData3,NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l {ok,{inactive,running}}=inviso_tool:get_node_status(ANode),
-
- %% Check that the trace pattern is still there.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{math,sin,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
-
- %% Now start a session and check that the trace patterns is gone.
- ?l start_inviso_tool_session(CNode,[],2,Nodes),
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{math,sin,1},traced]],
- {traced,false},
- 20)
- end,
- Nodes),
- ?l stop_inviso_tool_session(CNode,2,Nodes),
-
- ?l stop_inviso_tool(CNode,Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test case tests the rtc trace case mechanism.
-dist_rtc(doc) -> [""];
-dist_rtc(suite) -> [];
-dist_rtc(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- %% Check that the initial tracecase has been executed at all tracing nodes.
- ?l lists:foreach(fun(N)->
- ok=poll(rpc,
- call,
- [N,
- erlang,
- trace_info,
- [{lists,module_info,1},traced]],
- {traced,local},
- 20)
- end,
- Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Let the processes start
- timer:sleep(100),
-
- %% Find the pids of the test processes.
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l true=(1=<length(TestProcs)),
- ?l [ANode|_]=Nodes,
- ?l [{counter,Val}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- %% Call the tracecase as an rtc.
- ?l inviso_tool:sync_rtc(tracecase5,[]), % Updates the counter.
- ?l [{counter,Val2}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l true=(Val2==Val+1),
- ?l inviso_tool:sync_rtc(tracecase5,[]), % Updates the counter.
- ?l [{counter,Val3}]=rpc:call(ANode,ets,lookup,[test_proc_tab,counter]),
- ?l true=(Val3==Val2+1),
-
- %% Now we stop the session and restore it again.
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- ?l {ok,{2,_InvisoReturn}}=inviso_tool:restore_session(),
- %% The tracecase shall be done twice then.
- ?l ok=poll(rpc,call,[ANode,ets,lookup,[test_proc_tab,counter]],
- fun([{counter,V}]) when V==Val3+2 -> true;
- (_) -> false
- end,
- 20),
- ?l stop_inviso_tool_session(CNode,2,Nodes),
-
- ?l {ok,{AutostartData,_NodeResults}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l [{file,{_FileNameInit,_}},{file,{FileName,Bindings}},{file,{FileName,Bindings}}]=
- AutostartData,
- ?l stop_inviso_tool(CNode,Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% This test case tests mainly that reconnect and reinitiations of a node works.
-dist_reconnect(doc) -> [""];
-dist_reconnect(suite) -> [];
-dist_reconnect(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|OtherNodes]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Let the processes start
- timer:sleep(100),
-
- %% Find the pids of the test processes.
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l true=(1=<length(TestProcs)),
- ?l ok=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},20),
-
- %% Now we want to crash a node. Lets choose the RegExp node, then we can test
- %% that regexp expansion is done elsewhere too.
- ?l test_server:stop_node(RegExpNode),
- ?l ok=poll(net_adm,ping,[RegExpNode],pang,10),
- %% Make time for the monitoring signal to reach inviso_c and for inviso_c to
- %% inform its subscribers e.g inviso_tool.
- ?l timer:sleep(100),
- ?l {_,[{_Node,Modules}]}=
- inviso_tool_lib:expand_module_names([node()],"application.*",[]),
- ?l NrOfModules=length(Modules),
- %% This also checks that regexp expansion can be made at another node
- %% than RexExpNode.
- ?l {ok,NodeResults1}=inviso_tool:inviso(tp,["application.*",module_info,0,[]]),
- ?l true=check_noderesults(OtherNodes,
- fun({_N,{ok,Ints}}) when is_list(Ints) ->
- NrOfModules=lists:sum(Ints),
- true;
- (_) ->
- false
- end,
- NodeResults1),
-
- %% Do some faulty tests on the dead node.
- ?l {ok,{ok,[{RegExpNode,{error,down}}]}}=
- inviso_tool:reinitiate_session([RegExpNode]),
- ?l {ok,down}=inviso_tool:get_node_status(RegExpNode),
-
- %% Now it is time to restart the crashed node and reconnect it and then
- %% finally reinitiate it.
- ?l RegExpNodeString=atom_to_list(RegExpNode),
- ?l [NodeNameString,_HostNameString] = string:tokens(RegExpNodeString,[$@]),
- ?l RegExpNodeName=list_to_atom(NodeNameString),
- ?l test_server:start_node(RegExpNodeName,peer,[]),
- ?l ok=poll(net_adm,ping,[RegExpNode],pong,20),
- ?l SuiteDir=filename:dirname(code:which(?MODULE)),
- ?l true=rpc:call(RegExpNode,code,add_patha,[SuiteDir]),
- ?l ok=rpc:call(RegExpNode,application,start,[runtime_tools]),
- ?l ok=poll(rpc,call,[RegExpNode,erlang,whereis,[inviso_rt]],undefined,20),
- ?l {ok,down}=inviso_tool:get_node_status(RegExpNode),
-
- %% Restart the test process.
- ?l spawn(RegExpNode,?MODULE,test_proc_init,[]),
- ?l ok=poll(rpc,
- call,
- [RegExpNode,erlang,whereis,[inviso_tool_test_proc]],
- fun(P) when is_pid(P) -> true;
- (undefined) -> false
- end,
- 10),
- ?l TPid=rpc:call(RegExpNode,erlang,whereis,[inviso_tool_test_proc]),
- ?l {ok,[{RegExpNode,{ok,{inactive,running}}}]}=inviso_tool:reconnect_nodes([RegExpNode]),
- %% Try to reconnect the node again and an unknown node.
- ?l UnknownNode='unknown@nonexistant',
- ?l {ok,[{RegExpNode,{error,already_connected}},{UnknownNode,{error,unknown_node}}]}=
- inviso_tool:reconnect_nodes([RegExpNode,UnknownNode]),
- ?l {ok,{ok,[{RegExpNode,{ok,_}}]}}=inviso_tool:reinitiate_session([RegExpNode]),
- ?l ok=poll(rpc,
- call,
- [RegExpNode,erlang,trace_info,[TPid,flags]],
- {flags,[call]},
- 10),
- ?l {ok,{ok,[{RegExpNode,{error,already_in_session}},{UnknownNode,{error,unknown_node}}]}}=
- inviso_tool:reinitiate_session([RegExpNode,UnknownNode]),
-
- %% Suspend RegExpNode and test that it can not be reinitiated.
- ?l {ok,[{RegExpNode,ok}]}=rpc:call(CNode,inviso,suspend,[[RegExpNode],yetatest]),
- ?l {ok,[{RegExpNode,{error,already_connected}}]}=inviso_tool:reconnect_nodes([RegExpNode]),
- ?l {ok,{ok,[{RegExpNode,{error,suspended}}]}}=inviso_tool:reinitiate_session([RegExpNode]),
-
- %% Now we start a tracecase that will never terminate. We then reactivate the
- %% suspended node. Then there will be a running reactivator and a running
- %% tracecase to kill.
- ?l ok=inviso_tool:atc(tracecase4,id4,[]), % This one will not terminate.
- ?l ok=inviso_tool:reactivate(RegExpNode),
- ?l ok=poll(inviso_tool,
- get_activities,
- [],
- fun({ok,L}) when length(L)==2 -> true;
- (_) -> false
- end,
- 20),
- %% Now the reactivator and the tracecase shall be stuck(!)
- ?l {links,ToolLinks}=process_info(whereis(inviso_tool),links),
- ?l [P1,P2]=lists:foldl(fun(P,Acc)->case process_info(P,initial_call) of
- {initial_call,{inviso_tool,_,_}} ->
- [P|Acc];
- _ ->
- Acc
- end
- end,
- [],
- ToolLinks),
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- %% Check that the processes are killed.
- ?l ok=poll(erlang,process_info,[P1],undefined,10),
- ?l ok=poll(erlang,process_info,[P2],undefined,10),
- ?l stop_inviso_tool(CNode,Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test tests that we can adopt a running inviso runtime component and
-%% mark it as tracing-running.
-dist_adopt(doc) -> [""];
-dist_adopt(suite) -> [];
-dist_adopt(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=node(),
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- %% Then first start runtime components at different nodes for us to
- %% later adopt.
- ?l {ok,_IPid}=inviso:start(),
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_tag,[{dependency,infinity}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l [ANode|OtherNodes]=Nodes,
- ?l {ok,[{ANode,_LogResult}]}=
- inviso:init_tracing([ANode],
- [{trace,{file,filename:join(PrivDir,"dist_adopt_adoptednode.log")}},
- {ti,{file,filename:join(PrivDir,"dist_adopt_adoptednode.ti")}}]),
- ?l inviso:stop(),
- ?l ok=poll(erlang,whereis,[inviso_c],undefined,10),
- ?l lists:foreach(fun(N)->true=(is_pid(rpc:call(N,erlang,whereis,[inviso_rt]))) end,
- Nodes),
-
- %% Now start the tool and watch it adopt the runtimes.
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l Options=[{nodes,Nodes},{c_node,CNode}|Opts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- ?l io:format("LoopData:~p~n",[inviso_tool:get_loopdata()]),
- ?l {ok,{1,InvisoReturn}}=inviso_tool:start_session([]),
- ?l io:format("Invisoreturn:~p~n",[InvisoReturn]),
- %% Now check that all nodes are tracing.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{tracing,running}}}]})->true;
- (_) ->false
- end,
- 10)
- end,
- Nodes),
-
- %% At this point all nodes shall be tracing. However the initial tracecase
- %% shall not have been executed at ANode since it was adopted by the tool.
- ?l {traced,false}=rpc:call(ANode,erlang,trace_info,[{lists,module_info,1},traced]),
- ?l lists:foreach(fun(N)->
- {traced,local}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- OtherNodes),
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- ?l [BNode|_]=OtherNodes,
- %% Since nodes are not cleared the pattern still be there.
- ?l {traced,local}=rpc:call(BNode,erlang,trace_info,[{lists,module_info,1},traced]),
- ?l start_inviso_tool_session(CNode,[],2,Nodes),
- ?l stop_inviso_tool_session(CNode,2,Nodes),
- ?l {ok,_NodeResults}=inviso_tool:stop(),
- ?l ok=poll(erlang,whereis,[inviso_tool],undefined,10),
- ?l ok=poll(rpc,call,[CNode,erlang,whereis,[inviso_c]],undefined,10),
-
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test tests that saving and restoring a history works.
-dist_history(doc) -> [""];
-dist_history(suite) -> [];
-dist_history(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=RegExpNode, % We use a remote control component.
- Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- %% Start up the tool and a couple of inviso runtimes.
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l start_inviso_tool(Nodes,CNode,Opts),
- ?l start_inviso_tool_session(CNode,[],1,Nodes),
- %% Start a test process at every node with a runtime component.
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
-
- %% Activate tracing of the test process.
- ?l ok=inviso_tool:atc(tracecase1,id1,[{'ProcessName',inviso_tool_test_proc}]),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
- Nodes),
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[call]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
-
- %% Create a history file.
- ?l AbsFileName=filename:join(PrivDir,"dist_history.his"),
- ?l {ok,AbsFileName}=inviso_tool:save_history(AbsFileName),
- ?l {ok,_FileInfo}=file:read_file_info(AbsFileName),
-
- %% Stop the tracing of the test process.
- ?l inviso_tool:sync_dtc(tracecase1,id1),
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,false},
- 10)
- end,
- TestProcs),
- %% Now stop the session.
- ?l stop_inviso_tool_session(CNode,1,Nodes),
- %% Restart the session using the previously saved history.
- ?l {ok,{2,_InvisoReturn}}=inviso_tool:restore_session(AbsFileName),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- %% Check that the history has been redone.
- ?l lists:foreach(fun(P)->
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[P,flags]],
- {flags,[call]},
- 10),
- ?l ok=poll(rpc,
- call,
- [node(P),erlang,trace_info,[{math,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- TestProcs),
- ?l {ok,_NodeResults1}=inviso_tool:inviso(tpl,[math,module_info,0,[]]),
- %% Also check that the restored history now is our history.
- ?l {ok,{AutostartData,_NodeResults2}}=
- inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
- ?l FNameInit=filename:join(DataDir,"tracecase_init.trc"),
- ?l FName1=filename:join(DataDir,"tracecase1_on.trc"),
- ?l [{file,{FNameInit,[]}},
- {file,{FName1,[{'ProcessName',inviso_tool_test_proc}]}},
- {mfa,{inviso,tpl,[math,module_info,0,[]]}}]=AutostartData,
- ?l stop_inviso_tool_session(CNode,2,Nodes),
- ?l NodeCounters=lists:foldl(fun(N,Acc)->[{_,X}]=rpc:call(N,ets,lookup,[test_proc_tab,counter]),
- [{N,X}|Acc]
- end,
- [],
- Nodes),
- %% Remove the patterns set by the initial tracecase.
- ?l lists:foreach(fun(N)->rpc:call(N,
- erlang,
- trace_pattern,
- [{lists,module_info,1},false,[local]])
- end,
- Nodes),
- %% Now we want to test that we can do restore on the current session.
- ?l {ok,{3,_InvisoReturn2}}=inviso_tool:restore_session(),
- ?l ok=poll(inviso_tool,get_activities,[],{ok,[]},10),
- %% Check that the history has been redone yet again.
- ?l lists:foreach(fun({N,X})->
- [{counter,Y}]=
- rpc:call(N,ets,lookup,[test_proc_tab,counter]),
- Y=X+1
- end,
- NodeCounters),
- ?l lists:foreach(fun(N)->
- {traced,local}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- Nodes),
-
- ?l {error,session_active}=inviso_tool:reset_nodes(Nodes),
- %% Now stop the session and check that we can clear the nodes.
- ?l stop_inviso_tool_session(CNode,3,Nodes),
- ?l lists:foreach(fun(N)->{traced,local}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- Nodes),
- ?l {ok,NodeResults3}=inviso_tool:reset_nodes(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults3),
- ?l lists:foreach(fun(N)->{traced,false}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,1},traced])
- end,
- Nodes),
- ?l stop_inviso_tool(CNode,Nodes),
-
- %% Now we want to test that restoring a session at no active nodes will
- %% not result in a crash. (Previous error).
- ?l FaultyNodes=[gurka@nonexistant,tomat@nonexistant],
- ?l Options=[{nodes,FaultyNodes},{c_node,CNode}|Opts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- %% Now try to restore a session.
- ?l {ok,{_,{ok,[]}}}=inviso_tool:restore_session(AbsFileName),
- ?l {ok,down}=inviso_tool:get_node_status(gurka@nonexistant),
- %% Now stop the (useless) session.
- ?l {ok,{_,[]}}=inviso_tool:stop_session(),
- ?l stop_inviso_tool(CNode,[]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% This test tests a few strange situations when activating a session and there
-%% are no nodes that can be initiated or reinitiated.
-dist_start_session_special(doc) -> [""];
-dist_start_session_special(suite) -> [];
-dist_start_session_special(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- [RegExpNode|_]=RemoteNodes,
- CNode=RegExpNode, % We use a remote control component.
-% Nodes=RemoteNodes,
- DataDir=?config(data_dir,Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- %% Start up the tool but with no exiting nodes.
- FaultyNodes=[gurka@nonexistant,tomat@nonexistant],
- Opts=[{regexp_node,RegExpNode},
- {tdg,{?MODULE,tdg,[PrivDir]}},
- {tc_def_file,?TC_DEF_FILE},
- {initial_tcs,[{tracecase_init,[]}]},
- {dir,DataDir}], % This is where we find tracecases.
- ?l Options=[{nodes,FaultyNodes},{c_node,CNode}|Opts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- %% Now try to initate a session.
- ?l {ok,{SessionNr,{ok,[]}}}=inviso_tool:start_session(),
- ?l {ok,down}=inviso_tool:get_node_status(gurka@nonexistant),
- %% Now stop the (useless) session.
- ?l {ok,{SessionNr,[]}}=inviso_tool:stop_session(),
-
- %% Now start again, still no useful nodes.
- ?l {ok,{SessionNr2,{ok,[]}}}=inviso_tool:start_session(),
- ?l {ok,{SessionNr2,[]}}=inviso_tool:stop_session(),
- ?l stop_inviso_tool(CNode,[]), % No nodes are connected.
-
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-%% Help function starting the inviso_tool with runtime components at Nodes
-%% and the inviso control component at CNode. OtherOpts shall contain all other
-%% necessary options except nodes and c_node. Returns nothing significant.
-start_inviso_tool(Nodes,CNode,OtherOpts) ->
- ?l Options=[{nodes,Nodes},{c_node,CNode}|OtherOpts],
- ?l {ok,_Pid}=inviso_tool:start(Options),
- ?l ok=poll(erlang,whereis,[inviso_tool],fun(X)->true=is_pid(X) end,10),
- %% Now the runtime components shall be started but no tracing started.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{new,running}}}]})->true;
- (_) ->false
- end,
- 10)
- end,
- Nodes),
- true.
-%% -----------------------------------------------------------------------------
-
-%% Stops the inviso_tool.
-stop_inviso_tool(CNode,Nodes) ->
- ?l {ok,NodeResults}=inviso_tool:stop(),
- ?l true=check_noderesults(Nodes,ok,NodeResults),
- ?l ok=poll(erlang,whereis,[inviso_tool],undefined,10),
- %% Check that all inviso components are gone.
- ?l ok=poll(rpc,call,[CNode,erlang,whereis,[inviso_c]],undefined,10),
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [N,erlang,whereis,[inviso_rt]],
- undefined,
- 10)
- end,
- Nodes),
- true.
-%% -----------------------------------------------------------------------------
-
-%% Starts a trace session. Returns the InvisoReturn part of the return value.
-start_inviso_tool_session(CNode,MoreTDGargs,SessionNr,Nodes) ->
- ?l {ok,{SessionNr,InvisoReturn}}=inviso_tool:start_session(MoreTDGargs),
- %% Now check that all nodes are tracing.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{tracing,running}}}]})->true;
- (_) ->false
- end,
- 10),
- %% Check that the initial trace case is executed.
- ?l ok=poll(rpc,
- call,
- [N,erlang,trace_info,[{lists,module_info,1},traced]],
- {traced,local},
- 10)
- end,
- Nodes),
- InvisoReturn.
-%% -----------------------------------------------------------------------------
-
-%% Stops a trace session.
-stop_inviso_tool_session(CNode,SessionNr,Nodes) ->
- ?l {ok,{SessionNr,NodeResults}}=inviso_tool:stop_session(),
- ?l true=check_noderesults(Nodes,ok,NodeResults),
- %% Now the runtimes shall not be tracing any longer.
- ?l lists:foreach(fun(N)->ok=poll(rpc,
- call,
- [CNode,inviso,get_status,[[N]]],
- fun({ok,[{_N,{ok,{idle,running}}}]})->true;
- (_) ->false
- end,
- 10)
- end,
- Nodes),
- true.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking that there is a Result for each node in Nodes.
-%% Returns 'true' if successful.
-check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
- case Fun({Node,Result}) of
- true ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Fun,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
- _ ->
- illegal_result
- end;
-check_noderesults(Nodes,Result,[{Node,Result}|Rest]) ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Result,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
-check_noderesults([],_,[]) ->
- true;
-check_noderesults(X,Y,Z) ->
- io:format("Bad arguments to check noderesults:~w~n~w~n~w~n",[X,Y,Z]),
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which waits for a function call to become Result. This is useful
-%% if what we are waiting for can happend independantly of indications we have
-%% access to.
-poll(_,_,_,_,0) ->
- error;
-poll(M,F,Args,Result,Times) ->
- try apply(M,F,Args) of
- What when is_function(Result) ->
- case Result(What) of
- true ->
- ok;
- X ->
- io:format("Poll: ~w:~w ~w ~w ~w~n",[M,F,Args,Result,X]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end;
- Result ->
- ok;
- X ->
- io:format("Poll: ~w:~w ~w ~w ~w~n",[M,F,Args,Result,X]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- catch
- error:Reason ->
- io:format("Apply in suite-function poll/5 failed, ~w~n",[Reason]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% The Tracer Data Generator function.
-%% ------------------------------------------------------------------------------
-
--define(I2L(Arg),integer_to_list(Arg)).
-
-tdg(Node,{{Y,Mo,D},{H,Mi,S}},PrivDir) ->
- NameStr=atom_to_list(Node)++"_"++?I2L(Y)++"-"++?I2L(Mo)++"-"++?I2L(D)++"_"++
- ?I2L(H)++"-"++?I2L(Mi)++"-"++?I2L(S),
- LogTD={file,filename:join(PrivDir,NameStr++".log")},
- TiTD={file,filename:join(PrivDir,NameStr++".ti")},
- [{trace,LogTD},{ti,TiTD}].
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Handling the test server Config.
-%% ------------------------------------------------------------------------------
-
-insert_remotenode_config(Name,Node,Config) ->
- [{remotenode,{Name,Node}}|Config].
-%% ------------------------------------------------------------------------------
-
-insert_timetraphandle_config(Handle,Config) ->
- [{timetraphandle,Handle}|Config].
-%% ------------------------------------------------------------------------------
-
-get_remotenode_config(Name, [{remotenode, {Name, Node}}| _Cs]) ->
- Node;
-get_remotenode_config(Name, [_C | Cs]) ->
- get_remotenode_config(Name, Cs);
-get_remotenode_config(Name, []) ->
- exit({no_remotenode, Name}).
-
-%% ------------------------------------------------------------------------------
-
-get_timetraphandle_config(Config) ->
- {value,{_,Handle}}=lists:keysearch(timetraphandle,1,Config),
- Handle.
-%% ------------------------------------------------------------------------------
-
-get_remotenodes_config([{remotenode,{_Name,Node}}|Config]) ->
- [Node|get_remotenodes_config(Config)];
-get_remotenodes_config([_|Config]) ->
- get_remotenodes_config(Config);
-get_remotenodes_config([]) ->
- [].
-%% ------------------------------------------------------------------------------
-
-remove_remotenode_config(Name, [{remotenode, {Name, _}} | Cs]) ->
- Cs;
-remove_remotenode_config(Name, [C | Cs]) ->
- [C | remove_remotenode_config(Name, Cs)];
-remove_remotenode_config(_Name, []) ->
- [].
-%% ------------------------------------------------------------------------------
-
-remove_timetraphandle_config(Config) ->
- lists:keydelete(timetraphandle,1,Config).
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Code for a test process which can be started.
-%% ==============================================================================
-
-%% The test proc is also responsible for owning a side effect table. The table
-%% can be updated by tracecases.
-test_proc_init() ->
- register(inviso_tool_test_proc,self()),
- ets:new(test_proc_tab,[named_table,public]),
- ets:insert(test_proc_tab,{counter,0}),
- test_proc_loop().
-
-test_proc_loop() ->
- receive
- {apply,M,F,Args} ->
- apply(M,F,Args),
- test_proc_loop();
- X ->
- io:format("Got ~w~n",[X]),
- test_proc_loop()
- end.
-%% ------------------------------------------------------------------------------
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc
deleted file mode 100644
index 426c1ed9f9..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_off.trc
+++ /dev/null
@@ -1,12 +0,0 @@
-%% TRACECASE1_OFF.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Trace case deactivating the trace started by the activation case tracecase1_on.
-%%
-%% ProcessName=atom(), variable set in the test environment.
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:ctf(Nodes,ProcessName,[call]).
-inviso:ctpl(Nodes,math,module_info,1).
-%% END-OF-TRACE-CASE
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc
deleted file mode 100644
index a9106dbc78..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase1_on.trc
+++ /dev/null
@@ -1,17 +0,0 @@
-%% TRACECASE1_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Trace case setting a local pattern on math:module_info/1 and process flags
-%% on a test process which is supposed to be started by the test environment.
-%%
-%% ProcessName=atom(), variable set in the test environment.
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:tpl(Nodes,math,module_info,1,[]).
-inviso:tf(Nodes,ProcessName,[call]).
-lists:foreach(fun(N)->rpc:call(N,ets,update_counter,[test_proc_tab,counter,1]) end,
- Nodes).
-timer:sleep(500).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc
deleted file mode 100644
index cc89c3aa03..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_off.trc
+++ /dev/null
@@ -1,12 +0,0 @@
-%% TRACECASE2_OFF.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% The tracecase is mainly used for testing that synchronous tracecases return
-%% values.
-%%
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:ctpl(Nodes,math,pi,0).
-another_return_value.
-%% END-OF-TRACE-CASE
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc
deleted file mode 100644
index a3ab5fcfc7..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase2_on.trc
+++ /dev/null
@@ -1,16 +0,0 @@
-%% TRACECASE2_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% The tracecase is mainly used for testing that synchronous tracecases return
-%% values.
-%% We also use this tracecase to check that reactivation works when it comes to
-%% handling simulataneously issued tool commands (issued during reactivation).
-%%
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:tpl(Nodes,math,pi,0,[]).
-timer:sleep(3000).
-a_return_value.
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc
deleted file mode 100644
index e6c5ff78b1..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase3_on.trc
+++ /dev/null
@@ -1,9 +0,0 @@
-%% TRACECASE3_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% It is faulty and meant to cause a crash!
-%% -----------------------------------------------------------------------------
-
-1=2.
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc
deleted file mode 100644
index d14c11f78c..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase4_on.trc
+++ /dev/null
@@ -1,9 +0,0 @@
-%% TRACECASE4_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% It contains an infinity timer in order for the tracecase executer to hang.
-%% -----------------------------------------------------------------------------
-
-timer:sleep(infinity).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc
deleted file mode 100644
index feb67acb11..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_off.trc
+++ /dev/null
@@ -1,11 +0,0 @@
-%% TRACECASE5_OFF.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Contains a 2 second sleep.
-%% -----------------------------------------------------------------------------
-
-lists:foreach(fun(N)->rpc:call(N,ets,update_counter,[test_proc_tab,counter,1]) end,
- Nodes).
-timer:sleep(2000).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc
deleted file mode 100644
index 724c617c5a..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase5_on.trc
+++ /dev/null
@@ -1,11 +0,0 @@
-%% TRACECASE5_ON.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% This tracecase updates an ETS table. Can be used to verify that it has been
-%% done (or not done!).
-%% -----------------------------------------------------------------------------
-
-lists:foreach(fun(N)->rpc:call(N,ets,update_counter,[test_proc_tab,counter,1]) end,
- Nodes).
-%% END-OF-TRACE-CASE
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt b/lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt
deleted file mode 100644
index 5b08fa32a5..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_def.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-{tracecase_init,on,[],"./tracecase_init.trc"}.
-{tracecase1,on_off,['ProcessName'],"./tracecase1_on.trc","./tracecase1_off.trc"}.
-{tracecase2,on_off,[],"./tracecase2_on.trc","./tracecase2_off.trc"}.
-{tracecase3,on,[],"./tracecase3_on.trc"}.
-{tracecase4,on,[],"./tracecase4_on.trc"}.
-{tracecase5,on_off,[],"./tracecase5_on.trc","./tracecase5_off.trc"}.
-
-
diff --git a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc b/lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc
deleted file mode 100644
index 49a79cd3a5..0000000000
--- a/lib/inviso/test/inviso_tool_SUITE_data/tracecase_init.trc
+++ /dev/null
@@ -1,10 +0,0 @@
-%% TRACECASE_INIT.TRC
-%% -----------------------------------------------------------------------------
-%% This is a test trace case to be used by the inviso_tool_SUITE.
-%% Initial trace case executed at session start.
-%%
-%% Nodes=list(), inviso_tool variable - all traced nodes.
-%% -----------------------------------------------------------------------------
-
-inviso:tpl(Nodes,lists,module_info,1,[]).
-%% END-OF-TRACE-CASE
diff --git a/lib/inviso/vsn.mk b/lib/inviso/vsn.mk
deleted file mode 100644
index c6b0398bde..0000000000
--- a/lib/inviso/vsn.mk
+++ /dev/null
@@ -1 +0,0 @@
-INVISO_VSN = 0.6.3
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 19f25e3681..25d4482b76 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>2012</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,89 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.5.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a bug in OtpErlangTuple constructor. Thanks to Vlad
+ Dumitrescu.</p>
+ <p>
+ Own Id: OTP-10819</p>
+ </item>
+ <item>
+ <p>
+ Fixed finding cookie file on windows. Thanks to Vlad
+ Dumitrescu</p>
+ <p>
+ Own Id: OTP-10821</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Don't compress external binary format if this increases
+ the size. Thanks to Nico Kruber.</p>
+ <p>
+ Own Id: OTP-10822</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Jinterface 1.5.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ fix reading compressed binary terms from Java (Thanks to
+ Nico Kruber)</p>
+ <p>
+ Own Id: OTP-10505</p>
+ </item>
+ <item>
+ <p>
+ OtpEpmd.lokupNames() no longer hangs when badly
+ configured (Thanks to Vlad Dumitrescu)</p>
+ <p>
+ Own Id: OTP-10579</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Limited support for unicode atoms in the external format
+ and in the internal representation of the vm. This is a
+ preparative feature in order to support communication
+ with future releases of Erlang/OTP that may create
+ unicode atoms.</p>
+ <p>
+ Own Id: OTP-10753</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.5.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/jinterface/java_src/Makefile b/lib/jinterface/java_src/Makefile
index 19f99831eb..dafb634eac 100644
--- a/lib/jinterface/java_src/Makefile
+++ b/lib/jinterface/java_src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+# Copyright Ericsson AB 2000-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
@@ -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..968f284bff 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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 {
@@ -116,8 +118,9 @@ public class AbstractNode {
localHost = "localhost";
}
- final String dotCookieFilename = System.getProperty("user.home")
- + File.separator + ".erlang.cookie";
+ final String homeDir = getHomeDir();
+ final String dotCookieFilename = homeDir + File.separator
+ + ".erlang.cookie";
BufferedReader br = null;
try {
@@ -249,4 +252,15 @@ public class AbstractNode {
public String toString() {
return node();
}
+
+ private static String getHomeDir() {
+ final String home = System.getProperty("user.home");
+ if (System.getProperty("os.name").toLowerCase().contains("windows")) {
+ final String drive = System.getenv("HOMEDRIVE");
+ final String path = System.getenv("HOMEPATH");
+ return (drive != null && path != null) ? drive + path : home;
+ } else {
+ return home;
+ }
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
index 8ae63a1561..f476d4594d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
@@ -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..1868dc7740 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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..0371740b26 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2012. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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/OtpErlangTuple.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
index b3a1021992..bffce7f14d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -84,13 +84,13 @@ public class OtpErlangTuple extends OtpErlangObject implements Serializable,
* if the array is empty (null) or contains null
* elements.
*/
- public OtpErlangTuple(OtpErlangObject[] elems, final int start,
+ public OtpErlangTuple(final OtpErlangObject[] elems, final int start,
final int count) {
if (elems == null) {
throw new java.lang.IllegalArgumentException(
"Tuple content can't be null");
} else if (count < 1) {
- elems = NO_ELEMENTS;
+ this.elems = NO_ELEMENTS;
} else {
this.elems = new OtpErlangObject[count];
for (int i = 0; i < count; i++) {
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..45a82d6c94 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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..9dc1728346 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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
@@ -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..78f47aa32f 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2000-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,6 +26,7 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.Arrays;
+import java.util.zip.Deflater;
/**
* Provides a stream for encoding Erlang terms to external format, for
@@ -48,6 +49,8 @@ public class OtpOutputStream extends ByteArrayOutputStream {
private static final BigDecimal ten = new BigDecimal(10.0);
private static final BigDecimal one = new BigDecimal(1.0);
+ private int fixedSize = Integer.MAX_VALUE;
+
/**
* Create a stream with the default initial size (2048 bytes).
*/
@@ -101,10 +104,16 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* the storage of an <tt>OtpOutputStream</tt> instance.
*/
public void trimToSize() {
- if (super.count < super.buf.length) {
- final byte[] tmp = new byte[super.count];
- System.arraycopy(super.buf, 0, tmp, 0, super.count);
+ resize(super.count);
+ }
+
+ private void resize(int size) {
+ if (size < super.buf.length) {
+ final byte[] tmp = new byte[size];
+ System.arraycopy(super.buf, 0, tmp, 0, size);
super.buf = tmp;
+ } else if (size > super.buf.length) {
+ ensureCapacity(size);
}
}
@@ -116,6 +125,9 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
+ if (minCapacity > fixedSize) {
+ throw new IllegalArgumentException("Trying to increase fixed-size buffer");
+ }
int oldCapacity = super.buf.length;
if (minCapacity > oldCapacity) {
int newCapacity = (oldCapacity * 3)/2 + 1;
@@ -123,6 +135,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
newCapacity = oldCapacity + defaultIncrement;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
+ newCapacity = Math.min(fixedSize, newCapacity);
// minCapacity is usually close to size, so this is a win:
final byte[] tmp = new byte[newCapacity];
System.arraycopy(super.buf, 0, tmp, 0, super.count);
@@ -142,19 +155,37 @@ public class OtpOutputStream extends ByteArrayOutputStream {
super.buf[super.count++] = b;
}
- /**
- * Write an array of bytes to the stream.
- *
- * @param buf
- * the array of bytes to write.
- *
+ /* (non-Javadoc)
+ * @see java.io.ByteArrayOutputStream#write(byte[])
*/
-
@Override
public void write(final byte[] buf) {
- ensureCapacity(super.count + buf.length);
- System.arraycopy(buf, 0, super.buf, super.count, buf.length);
- super.count += buf.length;
+ // don't assume that super.write(byte[]) calls write(buf, 0, buf.length)
+ write(buf, 0, buf.length);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.ByteArrayOutputStream#write(int)
+ */
+ @Override
+ public synchronized void write(int b) {
+ ensureCapacity(super.count + 1);
+ super.buf[super.count] = (byte) b;
+ count += 1;
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.ByteArrayOutputStream#write(byte[], int, int)
+ */
+ @Override
+ public synchronized void write(byte[] b, int off, int len) {
+ if ((off < 0) || (off > b.length) || (len < 0)
+ || ((off + len) - b.length > 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+ ensureCapacity(super.count + len);
+ System.arraycopy(b, off, super.buf, super.count, len);
+ super.count += len;
}
/**
@@ -343,9 +374,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 */
+ }
}
/**
@@ -765,24 +850,77 @@ public class OtpOutputStream extends ByteArrayOutputStream {
/**
* Write an arbitrary Erlang term to the stream in compressed format.
- *
+ *
* @param o
- * the Erlang tem to write.
+ * the Erlang term to write.
*/
public void write_compressed(final OtpErlangObject o) {
+ write_compressed(o, Deflater.DEFAULT_COMPRESSION);
+ }
+
+ /**
+ * Write an arbitrary Erlang term to the stream in compressed format.
+ *
+ * @param o
+ * the Erlang term to write.
+ * @param level
+ * the compression level (<tt>0..9</tt>)
+ */
+ public void write_compressed(final OtpErlangObject o, int level) {
final OtpOutputStream oos = new OtpOutputStream(o);
- write1(OtpExternal.compressedTag);
- write4BE(oos.size());
- final java.io.FilterOutputStream fos = new java.io.FilterOutputStream(
- this);
- final java.util.zip.DeflaterOutputStream dos = new java.util.zip.DeflaterOutputStream(
- fos);
- try {
- oos.writeTo(dos);
- dos.close();
- } catch (final IOException e) {
- throw new java.lang.IllegalArgumentException(
- "Intremediate stream failed for Erlang object " + o);
+ /*
+ * similar to erts_term_to_binary() in external.c:
+ * We don't want to compress if compression actually increases the size.
+ * Since compression uses 5 extra bytes (COMPRESSED tag + size), don't
+ * compress if the original term is smaller.
+ */
+ if (oos.size() < 5) {
+ // fast path for small terms
+ try {
+ oos.writeTo(this);
+ // if the term is written as a compressed term, the output
+ // stream is closed, so we do this here, too
+ this.close();
+ } catch (IOException e) {
+ throw new java.lang.IllegalArgumentException(
+ "Intermediate stream failed for Erlang object " + o);
+ }
+ } else {
+ int startCount = super.count;
+ // we need destCount bytes for an uncompressed term
+ // -> if compression uses more, use the uncompressed term!
+ int destCount = startCount + oos.size();
+ this.fixedSize = destCount;
+ Deflater def = new Deflater(level);
+ final java.util.zip.DeflaterOutputStream dos = new java.util.zip.DeflaterOutputStream(
+ this, def);
+ try {
+ write1(OtpExternal.compressedTag);
+ write4BE(oos.size());
+ oos.writeTo(dos);
+ dos.close(); // note: closes this, too!
+ } catch (final IllegalArgumentException e) {
+ // discard further un-compressed data
+ // -> if not called, there may be memory leaks!
+ def.end();
+ // could not make the value smaller than originally
+ // -> reset to starting count, write uncompressed
+ super.count = startCount;
+ try {
+ oos.writeTo(this);
+ // if the term is written as a compressed term, the output
+ // stream is closed, so we do this here, too
+ this.close();
+ } catch (IOException e2) {
+ throw new java.lang.IllegalArgumentException(
+ "Intermediate stream failed for Erlang object " + o);
+ }
+ } catch (final IOException e) {
+ throw new java.lang.IllegalArgumentException(
+ "Intermediate stream failed for Erlang object " + o);
+ } finally {
+ this.fixedSize = Integer.MAX_VALUE;
+ }
}
}
diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl
index 9c88400c2a..63c78ebdaa 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-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,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,21 @@ 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,
+ [{},
+ {a,b,c},
+ [],
+ 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 +232,21 @@ 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,
+ [{},
+ {a,b,c},
+ [],
+ 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 +383,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 +470,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 +733,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 +756,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 +828,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/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 15423e5658..1954040c3d 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.5.6
+JINTERFACE_VSN = 1.5.8
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index 5e04bff0c1..de3ca1e176 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -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..697de6681f 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.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>
@@ -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 08d8f49ef6..6f04741f85 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2012</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -728,19 +728,29 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]),
</desc>
</func>
<func>
- <name>is_module_native(Module) -> boolean() | undefined</name>
+ <name name="is_module_native" arity="1"/>
<fsummary>Test whether a module has native code</fsummary>
- <type>
- <v>Module = module()</v>
- </type>
<desc>
- <p>This function returns <c>true</c> if <c>Module</c> is
+ <p>This function returns <c>true</c> if <c><anno>Module</anno></c> is
name of a loaded module that has native code loaded, and
- <c>false</c> if <c>Module</c> is loaded but does not have
- native. If <c>Module</c> is not loaded, this function returns
+ <c>false</c> if <c><anno>Module</anno></c> is loaded but does not have
+ native. If <c><anno>Module</anno></c> is not loaded, this function returns
<c>undefined</c>.</p>
</desc>
</func>
+
+ <func>
+ <name name="get_mode" arity="0"/>
+ <fsummary>The code_server's mode.</fsummary>
+ <desc>
+ <p>This function returns an atom describing the code_server's mode:
+ <c>interactive</c> or <c>embedded</c>. </p>
+ <p>This information is useful when an external entity (for example,
+ an IDE) provides additional code for a running node. If in interactive
+ mode, it only needs to add to the code path. If in embedded mode,
+ the code has to be loaded with <c>load_binary/3</c></p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml
index 1911fb628e..26db11cfcd 100644
--- a/lib/kernel/doc/src/erl_ddll.xml
+++ b/lib/kernel/doc/src/erl_ddll.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>
@@ -182,11 +182,8 @@
</datatypes>
<funcs>
<func>
- <name>demonitor(MonitorRef) -> ok</name>
+ <name name="demonitor" arity="1"/>
<fsummary>Remove a monitor for a driver</fsummary>
- <type>
- <v>MonitorRef = reference()</v>
- </type>
<desc>
<p>Removes a driver monitor in much the same way as
<seealso marker="erts:erlang#erlang:demonitor/1">erlang:demonitor/1</seealso> does with process
@@ -232,24 +229,19 @@
</desc>
</func>
<func>
- <name>info(Name, Tag) -> Value</name>
+ <name name="info" arity="2"/>
<fsummary>Retrieve specific information about one driver</fsummary>
- <type>
- <v>Name = string() | atom()</v>
- <v>Tag = processes | driver_options | port_count | linked_in_driver | permanent | awaiting_load | awaiting_unload</v>
- <v>Value = term()</v>
- </type>
<desc>
<p>This function returns specific information about one aspect
- of a driver. The <c>Tag</c> parameter specifies which aspect
- to get information about. The <c>Value</c> return differs
+ of a driver. The <c><anno>Tag</anno></c> parameter specifies which aspect
+ to get information about. The <c><anno>Value</anno></c> return differs
between different tags:</p>
<taglist>
<tag><em>processes</em></tag>
<item>
<p>Return all processes containing <seealso marker="#users">users</seealso> of the specific drivers
- as a list of tuples <c>{pid(),int()}</c>, where the
- <c>int()</c> denotes the number of users in the process
+ as a list of tuples <c>{pid(),integer() >= 0}</c>, where the
+ <c>integer()</c> denotes the number of users in the process
<c>pid()</c>.</p>
</item>
<tag><em>driver_options</em></tag>
@@ -261,16 +253,16 @@
</item>
<tag><em>port_count</em></tag>
<item>
- <p>Return the number of ports (an <c>int()</c>) using the driver.</p>
+ <p>Return the number of ports (an <c>integer >= 0()</c>) using the driver.</p>
</item>
<tag><em>linked_in_driver</em></tag>
<item>
- <p>Return a <c>bool()</c>, being <c>true</c> if the driver is a
+ <p>Return a <c>boolean()</c>, being <c>true</c> if the driver is a
statically linked in one and <c>false</c> otherwise.</p>
</item>
<tag><em>permanent</em></tag>
<item>
- <p>Return a <c>bool()</c>, being <c>true</c> if the driver has made
+ <p>Return a <c>boolean()</c>, being <c>true</c> if the driver has made
itself permanent (and is <em>not</em> a statically
linked in driver). <c>false</c> otherwise.</p>
</item>
@@ -278,14 +270,14 @@
<item>
<p>Return a list of all processes having monitors for
<c>loading</c> active, each process returned as
- <c>{pid(),int()}</c>, where the <c>int()</c> is the
+ <c>{pid(),integer() >= 0}</c>, where the <c>integer()</c> is the
number of monitors held by the process <c>pid()</c>.</p>
</item>
<tag><em>awaiting_unload</em></tag>
<item>
<p>Return a list of all processes having monitors for
<c>unloading</c> active, each process returned as
- <c>{pid(),int()}</c>, where the <c>int()</c> is the
+ <c>{pid(),integer() >= 0}</c>, where the <c>integer()</c> is the
number of monitors held by the process <c>pid()</c>.</p>
</item>
</taglist>
@@ -377,41 +369,34 @@
</desc>
</func>
<func>
- <name>monitor(Tag, Item) -> MonitorRef</name>
+ <name name="monitor" arity="2"/>
<fsummary>Create a monitor for a driver</fsummary>
- <type>
- <v>Tag = driver </v>
- <v>Item = {Name, When}</v>
- <v>Name = atom() | string()</v>
- <v>When = loaded | unloaded | unloaded_only</v>
- <v>MonitorRef = reference()</v>
- </type>
<desc>
<p>This function creates a driver monitor and works in many
ways as the function <seealso marker="erts:erlang#erlang:monitor/2">erlang:monitor/2</seealso>,
does for processes. When a driver changes state, the monitor
results in a monitor-message being sent to the calling
- process. The <c>MonitorRef</c> returned by this function is
+ process. The <c><anno>MonitorRef</anno></c> returned by this function is
included in the message sent.</p>
<p>As with process monitors, each driver monitor set will only
generate <em>one single message</em>. The monitor is
"destroyed" after the message is sent and there is then no
need to call <seealso marker="#demonitor/1">demonitor/1</seealso>.</p>
- <p>The <c>MonitorRef</c> can also be used in subsequent calls
+ <p>The <c><anno>MonitorRef</anno></c> can also be used in subsequent calls
to <seealso marker="#demonitor/1">demonitor/1</seealso> to
remove a monitor.</p>
<p>The function accepts the following parameters:</p>
<taglist>
- <tag><em>Tag</em></tag>
+ <tag><em><anno>Tag</anno></em></tag>
<item>
<p>The monitor tag is always <c>driver</c> as this function
can only be used to create driver monitors. In the future,
driver monitors will be integrated with process monitors,
why this parameter has to be given for consistence.</p>
</item>
- <tag><em>Item</em></tag>
+ <tag><em><anno>Item</anno></em></tag>
<item>
- <p>The <c>Item</c> parameter specifies which driver one
+ <p>The <c><anno>Item</anno></c> parameter specifies which driver one
wants to monitor (the name of the driver) as well as
which state change one wants to monitor. The parameter
is a tuple of arity two whose first element is the
@@ -588,22 +573,8 @@
</desc>
</func>
<func>
- <name>try_load(Path, Name, OptionList) -> {ok,Status} | {ok, PendingStatus, Ref} | {error, ErrorDesc}</name>
+ <name name="try_load" arity="3"/>
<fsummary>Load a driver</fsummary>
- <type>
- <v>Path = Name = string() | atom()</v>
- <v>OptionList = [ Option ]</v>
- <v>Option = {driver_options, DriverOptionList} | {monitor, MonitorOption} | {reload, ReloadOption}</v>
- <v>DriverOptionList = [ DriverOption ]</v>
- <v>DriverOption = kill_ports</v>
- <v>MonitorOption = pending_driver | pending</v>
- <v>ReloadOption = pending_driver | pending</v>
- <v>Status = loaded | already_loaded | PendingStatus </v>
- <v>PendingStatus = pending_driver | pending_process</v>
- <v>Ref = reference()</v>
- <v>ErrorDesc = ErrorAtom | OpaqueError</v>
- <v>ErrorAtom = linked_in_driver | inconsistent | permanent | not_loaded_by_this_process | not_loaded | pending_reload | pending_process</v>
- </type>
<desc>
<p>This function provides more control than the
<c>load/2</c>/<c>reload/2</c> and
@@ -655,65 +626,65 @@
<p>When the function returns <c>{ok, pending_driver}</c> or
<c>{ok, pending_process}</c>, one might want to get information
about when the driver is <em>actually</em> loaded. This can
- be achieved by using the <c>{monitor, PendingOption}</c> option.</p>
+ be achieved by using the <c>{monitor, <anno>MonitorOption</anno>}</c> option.</p>
<p>When monitoring is requested, and a corresponding <c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c> would be
- returned, the function will instead return a tuple <c>{ok, PendingStatus, reference()}</c> and the process will, at a later
+ returned, the function will instead return a tuple <c>{ok, <anno>PendingStatus</anno>, reference()}</c> and the process will, at a later
time when the driver actually gets loaded, get a monitor
message. The monitor message one can expect is described in
the <seealso marker="#monitor/2">monitor/2</seealso>
function description. </p>
<note>
<p>Note that in case of loading, monitoring can
- <em>not</em> only get triggered by using the <c>{reload, ReloadOption}</c> option, but also in special cases where
+ <em>not</em> only get triggered by using the <c>{reload, <anno>ReloadOption</anno>}</c> option, but also in special cases where
the load-error is transient, why <c>{monitor, pending_driver}</c> should be used under basically
<em>all</em> real world circumstances!</p>
</note>
<p>The function accepts the following parameters:</p>
<taglist>
- <tag><em>Path</em></tag>
+ <tag><em><anno>Path</anno></em></tag>
<item>
<p>The filesystem path to the directory where the driver
object file is situated. The filename of the object file
(minus extension) must correspond to the driver name
(used in the name parameter) and the driver must
identify itself with the very same name. The
- <c>Path</c> might be provided as an <em>io_list</em>,
- meaning it can be a list of other io_lists, characters
+ <c><anno>Path</anno></c> might be provided as an <em>iolist()</em>,
+ meaning it can be a list of other <c>iolist()</c>s, characters
(eight bit integers) or binaries, all to be flattened
into a sequence of characters.</p>
- <p>The (possibly flattened) <c>Path</c> parameter must be
+ <p>The (possibly flattened) <c><anno>Path</anno></c> parameter must be
consistent throughout the system, a driver should, by
all <seealso marker="#users">users</seealso>, be loaded
- using the same <em>literal</em><c>Path</c>. The
+ using the same <em>literal</em><c><anno>Path</anno></c>. The
exception is when <em>reloading</em> is requested, in
- which case the <c>Path</c> may be specified
+ which case the <c><anno>Path</anno></c> may be specified
differently. Note that all <seealso marker="#users">users</seealso> trying to load the
- driver at a later time will need to use the <em>new</em><c>Path</c> if the <c>Path</c> is changed using a
+ driver at a later time will need to use the <em>new</em><c><anno>Path</anno></c> if the <c><anno>Path</anno></c> is changed using a
<c>reload</c> option. This is yet another reason
to have <em>only one loader</em> of a driver one wants to
upgrade in a running system! </p>
</item>
- <tag><em>Name</em></tag>
+ <tag><em><anno>Name</anno></em></tag>
<item>
<p>The name parameter is the name of the driver to be used
in subsequent calls to <seealso marker="erts:erlang#open_port/2">open_port</seealso>. The
- name can be specified either as an <c>io_list()</c> or
+ name can be specified either as an <c>iolist()</c> or
as an <c>atom()</c>. The name given when loading is used
to find the actual object file (with the
- help of the <c>Path</c> and the system implied
+ help of the <c><anno>Path</anno></c> and the system implied
extension suffix, i.e. <c>.so</c>). The name by which
the driver identifies itself must also be consistent
- with this <c>Name</c> parameter, much as a beam-file's
+ with this <c><anno>Name</anno></c> parameter, much as a beam-file's
module name much correspond to its filename.</p>
</item>
- <tag><em>OptionList</em></tag>
+ <tag><em><anno>OptionList</anno></em></tag>
<item>
<p>A number of options can be specified to control the
loading operation. The options are given as a list of
two-tuples, the tuples having the following values and
meanings:</p>
<taglist>
- <tag><em>{driver_options, DriverOptionsList}</em></tag>
+ <tag><em>{driver_options, <anno>DriverOptionList</anno>}</em></tag>
<item>
<p>This option is to provide options that will change
its general behavior and will "stick" to the driver
@@ -729,14 +700,14 @@
when the last <seealso marker="#users">user</seealso> calls <seealso marker="#try_unload/2">try_unload/2</seealso>, or
the last process having loaded the driver exits.</p>
</item>
- <tag><em>{monitor, MonitorOption}</em></tag>
+ <tag><em>{monitor, <anno>MonitorOption</anno>}</em></tag>
<item>
- <p>A <c>MonitorOption</c> tells <c>try_load/3</c> to
+ <p>A <c><anno>MonitorOption</anno></c> tells <c>try_load/3</c> to
trigger a driver monitor under certain
conditions. When the monitor is triggered, the
- function will return a three-tuple <c>{ok, PendingStatus, reference()}</c>, where the <c>reference()</c> is
+ function will return a three-tuple <c>{ok, <anno>PendingStatus</anno>, reference()}</c>, where the <c>reference()</c> is
the monitor ref for the driver monitor.</p>
- <p>Only one <c>MonitorOption</c> can be specified and
+ <p>Only one <c><anno>MonitorOption</anno></c> can be specified and
it is either the atom <c>pending</c>, which means
that a monitor should be created whenever a load
operation is delayed, and the atom
@@ -747,7 +718,7 @@
is present for completeness, it is very well defined
which reload-options might give rise to which
delays. It might, however, be a good idea to use the
- same <c>MonitorOption</c> as the <c>ReloadOption</c>
+ same <c><anno>MonitorOption</anno></c> as the <c><anno>ReloadOption</anno></c>
if present.</p>
<p>If reloading is not requested, it might still be
useful to specify the <c>monitor</c> option, as
@@ -760,12 +731,12 @@
<c>{monitor, pending_driver}</c> in production
code (see the monitor discussion above). </p>
</item>
- <tag><em>{reload,RealoadOption}</em></tag>
+ <tag><em>{reload,<anno>ReloadOption</anno>}</em></tag>
<item>
<p>This option is used when one wants to
<em>reload</em> a driver from disk, most often in a
code upgrade scenario. Having a <c>reload</c> option
- also implies that the <c>Path</c> parameter need
+ also implies that the <c><anno>Path</anno></c> parameter need
<em>not</em> be consistent with earlier loads of
the driver.</p>
<p>To reload a driver, the process needs to have previously
@@ -814,9 +785,9 @@
<tag><em>{error,inconsistent}</em></tag>
<item>
<p>The driver has already been loaded with either other
- <c>DriverOptions</c> or a different <em>literal</em><c>Path</c> argument.</p>
+ <c><anno>DriverOptionList</anno></c> or a different <em>literal</em><c>Path</c> argument.</p>
<p>This can happen even if a <c>reload</c> option is given,
- if the <c>DriverOptions</c> differ from the current.</p>
+ if the <c>DriverOptionList</c> differ from the current.</p>
</item>
<tag><em>{error, permanent}</em></tag>
<item>
@@ -830,19 +801,19 @@
</item>
<tag><em>{error, pending_reload}</em></tag>
<item>
- <p>Driver reload is already requested by another <seealso marker="#users">user</seealso> when the <c>{reload, ReloadOption}</c> option was given.</p>
+ <p>Driver reload is already requested by another <seealso marker="#users">user</seealso> when the <c>{reload, <anno>ReloadOption</anno>}</c> option was given.</p>
</item>
<tag><em>{error, not_loaded_by_this_process}</em></tag>
<item>
<p>Appears when the <c>reload</c> option is given. The
- driver <c>Name</c> is present in the system, but there is no
+ driver <c><anno>Name</anno></c> is present in the system, but there is no
<seealso marker="#users">user</seealso> of it in this
process.</p>
</item>
<tag><em>{error, not_loaded}</em></tag>
<item>
<p>Appears when the <c>reload</c> option is given. The
- driver <c>Name</c> is not in the system. Only drivers
+ driver <c><anno>Name</anno></c> is not in the system. Only drivers
loaded by this process can be reloaded.</p>
</item>
</taglist>
@@ -856,18 +827,8 @@
</desc>
</func>
<func>
- <name>try_unload(Name, OptionList) -> {ok,Status} | {ok, PendingStatus, Ref} | {error, ErrorAtom}</name>
+ <name name="try_unload" arity="2"/>
<fsummary>Unload a driver</fsummary>
- <type>
- <v>Name = string() | atom()</v>
- <v>OptionList = [ Option ]</v>
- <v>Option = {monitor, MonitorOption} | kill_ports</v>
- <v>MonitorOption = pending_driver | pending</v>
- <v>Status = unloaded | PendingStatus </v>
- <v>PendingStatus = pending_driver | pending_process</v>
- <v>Ref = reference()</v>
- <v>ErrorAtom = linked_in_driver | not_loaded | not_loaded_by_this_process | permanent</v>
- </type>
<desc>
<p>This is the low level function to unload (or decrement
reference counts of) a driver. It can be used to force port
@@ -948,15 +909,15 @@
</taglist>
<p>The function accepts the following parameters:</p>
<taglist>
- <tag><em>Name</em></tag>
+ <tag><em><anno>Name</anno></em></tag>
<item>
<p>The name parameter is the name of the driver to be
unloaded. The name can be specified either as an
- <c>io_list()</c> or as an <c>atom()</c>. </p>
+ <c>iolist()</c> or as an <c>atom()</c>. </p>
</item>
- <tag><em>OptionList</em></tag>
+ <tag><em><anno>OptionList</anno></em></tag>
<item>
- <p>The <c>OptionList</c> argument can be used to specify
+ <p>The <c><anno>OptionList</anno></c> argument can be used to specify
certain behavior regarding ports as well as triggering
monitors under certain conditions:</p>
<taglist>
@@ -972,10 +933,10 @@
unloads, one should use the driver option
<c>kill_ports</c> when loading the driver instead.</p>
</item>
- <tag><em>{monitor, MonitorOption}</em></tag>
+ <tag><em>{monitor, <anno>MonitorOption</anno>}</em></tag>
<item>
<p>This option creates a driver monitor if the condition
- given in <c>MonitorOptions</c> is true. The valid
+ given in <c><anno>MonitorOption</anno></c> is true. The valid
options are:</p>
<taglist>
<tag><em>pending_driver</em></tag>
@@ -989,7 +950,7 @@
<c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c>.</p>
</item>
</taglist>
- <p>The <c>pending_driver</c> <c>MonitorOption</c> is by far
+ <p>The <c>pending_driver</c> <c><anno>MonitorOption</anno></c> is by far
the most useful and it has to be used to ensure that the
driver has really been unloaded and the ports closed
whenever the <c>kill_ports</c> option is used or the
@@ -1016,11 +977,11 @@
</item>
<tag><em>{error, not_loaded}</em></tag>
<item>
- <p>The driver <c>Name</c> is not present in the system.</p>
+ <p>The driver <c><anno>Name</anno></c> is not present in the system.</p>
</item>
<tag><em>{error, not_loaded_by_this_process}</em></tag>
<item>
- <p>The driver <c>Name</c> is present in the system, but
+ <p>The driver <c><anno>Name</anno></c> is present in the system, but
there is no <seealso marker="#users">user</seealso> of
it in this process. </p>
<p>As a special case, drivers can be unloaded from
@@ -1088,12 +1049,8 @@
</desc>
</func>
<func>
- <name>loaded_drivers() -> {ok, Drivers}</name>
+ <name name="loaded_drivers" arity="0"/>
<fsummary>List loaded drivers</fsummary>
- <type>
- <v>Drivers = [Driver]</v>
- <v>Driver = string()</v>
- </type>
<desc>
<p>Returns a list of all the available drivers, both
(statically) linked-in and dynamically loaded ones.</p>
diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml
index acbf9a2c6e..84ec3927c8 100644
--- a/lib/kernel/doc/src/error_handler.xml
+++ b/lib/kernel/doc/src/error_handler.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -43,19 +43,52 @@
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>
+ <warning>
+ <p>Defining <c>'$handle_undefined_function'/2</c> in
+ ordinary application code is highly discouraged. It is very
+ easy to make subtle errors that can take a long time to
+ debug. Furthermore, none of the tools for static code
+ analysis (such as Dialyzer and Xref) supports the use of
+ <c>'$handle_undefined_function'/2</c> and no such support
+ will be added. Only use this function after having carefully
+ considered other, less dangerous, solutions. One example of
+ potential legitimate use is creating stubs for other
+ sub-systems during testing and debugging.
+ </p>
+ </warning>
+ <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/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index ec3274965a..cd86b364f6 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -127,11 +127,8 @@ ok</pre>
</desc>
</func>
<func>
- <name>warning_map() -> Tag</name>
+ <name name="warning_map" arity="0"/>
<fsummary>Return the current mapping for warning events</fsummary>
- <type>
- <v>Tag = error | warning | info</v>
- </type>
<desc>
<p>Returns the current mapping for warning events. Events sent
using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 772eff13cc..66ecba1bf2 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>2011</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -60,14 +60,6 @@
converted, why the Unicode mode for file names is not default on
systems having completely transparent file naming.</p>
- <note>
- <p>As of R14B01, the most basic file handling modules
- (<c>file</c>, <c>prim_file</c>, <c>filelib</c> and
- <c>filename</c>) accept raw file names, but the rest of OTP is not
- guaranteed to handle them, why Unicode file naming on systems
- where it is not default is still considered experimental.</p>
- </note>
-
<p>Raw file names is a new feature in OTP R14B01, which allows the
user to supply completely uninterpreted file names to the
underlying OS/filesystem. They are supplied as binaries, where it
@@ -100,17 +92,24 @@
<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"/>
</datatype>
<datatype>
+ <name name="filename_all"/>
+ </datatype>
+ <datatype>
<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>
@@ -118,6 +117,14 @@
<desc>
<p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c>
are allowed to be > 255.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="name_all"/>
+ <desc>
+ <p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c>
+ are allowed to be > 255.
<c><anno>RawFilename</anno></c> is a filename not subject to
Unicode translation,
meaning that it can contain characters not conforming to
@@ -152,9 +159,6 @@
<datatype>
<name name="file_info_option"/>
</datatype>
- <datatype>
- <name name="sendfile_option"/>
- </datatype>
</datatypes>
<funcs>
@@ -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>
@@ -412,7 +434,7 @@
</desc>
</func>
<func>
- <name>file_info(Filename) -> {ok, FileInfo} | {error, Reason}</name>
+ <name name="file_info" arity="1"/>
<fsummary>Get information about a file (deprecated)</fsummary>
<desc>
<p>This function is obsolete. Use <c>read_file_info/1,2</c>
@@ -482,7 +504,8 @@
<name name="list_dir" arity="1"/>
<fsummary>List files in a directory</fsummary>
<desc>
- <p>Lists all the files in a directory. Returns
+ <p>Lists all files in a directory, <b>except</b> files
+ with "raw" names. Returns
<c>{ok, <anno>Filenames</anno>}</c> if successful.
Otherwise, it returns <c>{error, <anno>Reason</anno>}</c>.
<c><anno>Filenames</anno></c> is a list of
@@ -499,6 +522,37 @@
<item>
<p>The directory does not exist.</p>
</item>
+ <tag><c>{no_translation, <anno>Filename</anno>}</c></tag>
+ <item>
+ <p><c><anno>Filename</anno></c> is a <c>binary()</c> with
+ characters coded in ISO-latin-1 and the VM was started
+ with the parameter <c>+fnue</c>.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name name="list_dir_all" arity="1"/>
+ <fsummary>List all files in a directory</fsummary>
+ <desc>
+ <p>Lists all the files in a directory, including files with
+ "raw" names.
+ Returns <c>{ok, <anno>Filenames</anno>}</c> if successful.
+ Otherwise, it returns <c>{error, <anno>Reason</anno>}</c>.
+ <c><anno>Filenames</anno></c> is a list of
+ the names of all the files in the directory. The names are
+ not sorted.</p>
+ <p>Typical error reasons are:</p>
+ <taglist>
+ <tag><c>eacces</c></tag>
+ <item>
+ <p>Missing search or write permissions for <c><anno>Dir</anno></c>
+ or one of its parent directories.</p>
+ </item>
+ <tag><c>enoent</c></tag>
+ <item>
+ <p>The directory does not exist.</p>
+ </item>
</taglist>
</desc>
</func>
@@ -598,7 +652,7 @@
</desc>
</func>
<func>
- <name>native_name_encoding() -> latin1 | utf8</name>
+ <name name="native_name_encoding" arity="0"/>
<fsummary>Return the VM's configured filename encoding.</fsummary>
<desc>
<p>This function returns the configured default file name encoding to use for raw file names. Generally an application supplying file names raw (as binaries), should obey the character encoding returned by this function.</p>
@@ -610,7 +664,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 +698,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 +822,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 +920,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 +964,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 +1036,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 +1288,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 +1300,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 +1346,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>
@@ -1373,10 +1441,41 @@
<fsummary>See what a link is pointing to</fsummary>
<desc>
<p>This function returns <c>{ok, <anno>Filename</anno>}</c> if
+ <c><anno>Name</anno></c> refers to a symbolic link that is
+ not a "raw" file name, or <c>{error, <anno>Reason</anno>}</c>
+ otherwise.
+ On platforms that do not support symbolic links, the return
+ value will be <c>{error,enotsup}</c>.</p>
+ <p>Typical error reasons:</p>
+ <taglist>
+ <tag><c>einval</c></tag>
+ <item>
+ <p><c><anno>Name</anno></c> does not refer to a symbolic link
+ or the name of the file that it refers to does not conform
+ to the expected encoding.</p>
+ </item>
+ <tag><c>enoent</c></tag>
+ <item>
+ <p>The file does not exist.</p>
+ </item>
+ <tag><c>enotsup</c></tag>
+ <item>
+ <p>Symbolic links are not supported on this platform.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name name="read_link_all" arity="1"/>
+ <fsummary>See what a link is pointing to</fsummary>
+ <desc>
+ <p>This function returns <c>{ok, <anno>Filename</anno>}</c> if
<c><anno>Name</anno></c> refers to a symbolic link or
<c>{error, <anno>Reason</anno>}</c> otherwise.
On platforms that do not support symbolic links, the return
value will be <c>{error,enotsup}</c>.</p>
+ <p>Note that <c><anno>Filename</anno></c> can be either a list
+ or a binary.</p>
<p>Typical error reasons:</p>
<taglist>
<tag><c>einval</c></tag>
@@ -1502,6 +1601,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>
@@ -1541,6 +1643,12 @@
<p><c><anno>Dir</anno></c> had an improper type,
such as tuple.</p>
</item>
+ <tag><c>no_translation</c></tag>
+ <item>
+ <p><c><anno>Dir</anno></c> is a <c>binary()</c> with
+ characters coded in ISO-latin-1 and the VM was started
+ with the parameter <c>+fnue</c>.</p>
+ </item>
</taglist>
<warning>
<p>In a future release, a bad type for the
@@ -1609,6 +1717,7 @@
<func>
<name name="sendfile" arity="5"/>
<fsummary>send a file to a socket</fsummary>
+ <type name="sendfile_option"/>
<desc>
<p>Sends <c>Bytes</c> from the file
referenced by <c>RawFile</c> beginning at <c>Offset</c> to
@@ -1732,22 +1841,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 +1887,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/global.xml b/lib/kernel/doc/src/global.xml
index 304a9b1d88..53958c47c2 100644
--- a/lib/kernel/doc/src/global.xml
+++ b/lib/kernel/doc/src/global.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>
@@ -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/inet.xml b/lib/kernel/doc/src/inet.xml
index b727960d96..541500a300 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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>
@@ -446,16 +493,6 @@ fe80::204:acff:fe17:bf38
faster than the receiver can read.</p>
</item>
- <tag><c>{bit8, clear | set | on | off}</c></tag>
- <item>
- <p>
- Scans every byte in received data-packets and checks if the 8 bit
- is set in any of them. Information is retrieved with
- <c>inet:getopts/2</c>.
- </p>
- <p>Note that the <c>bit8</c> option is deprecated and will be removed in Erlang/OTP R16.</p>
- </item>
-
<tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag>
<item>
<p>Enable/disable permission to send broadcasts.</p>
@@ -522,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>
@@ -550,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 78bc533464..c18ae897b4 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,235 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.16.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A bug that could cause a crash with wrong reason has
+ been corrected in the <c>application_controller</c>
+ module. </p>
+ <p>
+ Own Id: OTP-10754</p>
+ </item>
+ <item>
+ <p>
+ Fix <c>code:is_module_native/1</c> that sometimes in R16A
+ returned false for hipe compiled modules containing BIFs
+ such as <c>lists</c>.</p>
+ <p>
+ Own Id: OTP-10870</p>
+ </item>
+ <item>
+ <p>
+ Respect <c>{exit_on_close,false}</c> option on tcp socket
+ in non-passive mode when receiving fails (due to an
+ ill-formed packet for example) by only doing a half close
+ and still allow sending on the socket. (Thanks to Anthony
+ Molinaro and Steve Vinoski for reporting the problem)</p>
+ <p>
+ Own Id: OTP-10879</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Slightly nicer error message when node start fails due to
+ duplicate name. Thanks to Magnus Henoch.</p>
+ <p>
+ Own Id: OTP-10797</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to Unicode support. </p>
+ <p>
+ Own Id: OTP-10820</p>
+ </item>
+ <item>
+ <p>
+ Add a new function code:get_mode() can be used to detect
+ how the code servers behaves. Thanks to Vlad Dumitrescu</p>
+ <p>
+ Own Id: OTP-10823</p>
+ </item>
+ <item>
+ <p>
+ Fix type of error Reason on gen_tcp:send/2. Thanks to
+ Sean Cribbs.</p>
+ <p>
+ Own Id: OTP-10839</p>
+ </item>
+ <item>
+ <p><c>file:list_dir_all/1</c> and
+ <c>file:read_link_all/1</c> that can handle raw file
+ names have been added. See the User Guide for STDLIB for
+ information about raw file names.</p>
+ <p>
+ Own Id: OTP-10852</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ It is no longer possible to have <c>{Mod,Vsn}</c> in the
+ 'modules' list in a .app file.</p>
+ <p>
+ This was earlier possible, although never documented in
+ the .app file reference manual. It was however visible in
+ the documentation of <c>application:load/[1,2]</c>, where
+ the same term as in a .app file can be used as the first
+ argument.</p>
+ <p>
+ The possibility has been removed since the <c>Vsn</c>
+ part was never used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10417</p>
+ </item>
+ <item>
+ <p> The contract of <c>erl_ddll:format_error/1</c> has
+ been corrected. (Thanks to Joseph Wayne Norton.) </p>
+ <p>
+ Own Id: OTP-10473</p>
+ </item>
+ <item>
+ <p>
+ Change printout of application crash message on startup
+ to formated strings (Thanks to Serge Aleynikov)</p>
+ <p>
+ Own Id: OTP-10620</p>
+ </item>
+ <item>
+ <p> The type <c>ascii_string()</c> in the <c>base64</c>
+ module has been corrected. The type
+ <c>file:file_info()</c> has been cleaned up. The type
+ <c>file:fd()</c> has been made opaque in the
+ documentation. </p>
+ <p>
+ Own Id: OTP-10624 Aux Id: kunagi-352 [263] </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Inet exported functionality</p>
+ <p>
+ inet:parse_ipv4_address/1,
+ inet:parse_ipv4strict_address/1,
+ inet:parse_ipv6_address/1,
+ inet:parse_ipv6strict_address/1, inet:parse_address/1 and
+ inet:parse_strict_address is now exported from the inet
+ module.</p>
+ <p>
+ Own Id: OTP-8067 Aux Id: kunagi-274 [185] </p>
+ </item>
+ <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> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>
+ The documentation for <c>global:register_name/3</c> has
+ been updated to mention that the use of
+ <c>{Module,Function}</c> as the method argument (resolve
+ function) is deprecated.</p>
+ <p>
+ Own Id: OTP-10419</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug where sendfile on oracle solaris would return
+ an error when a partial send was done.</p>
+ <p>
+ Own Id: OTP-10549</p>
+ </item>
+ <item>
+ <p>
+ The <c>error_handler</c> module will now call
+ <c>'$handle_undefined_function'/2</c> if an attempt is
+ made to call a non-existing function in a module that
+ exists. See the documentation for <c>error_handler</c>
+ module for details.</p>
+ <p>
+ Own Id: OTP-10617 Aux Id: kunagi-340 [251] </p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Do not return wrong terms unnecessarily. (Thanks to
+ Kostis Sagonas.)</p>
+ <p>
+ Own Id: OTP-10662</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>Add file:allocate/3 operation</p>
+ <p>This operation allows pre-allocation of space for
+ files. It succeeds only on systems that support such
+ operation. (Thanks to Filipe David Manana)</p>
+ <p>
+ Own Id: OTP-10680</p>
+ </item>
+ <item>
+ <p>
+ Add application:get_key/3. The new function provides a
+ default value for a configuration parameter. Thanks to
+ Serge Aleynikov.</p>
+ <p>
+ Own Id: OTP-10694</p>
+ </item>
+ <item>
+ <p>
+ Add search to Erlang shell's history. Thanks to Fred
+ Herbert.</p>
+ <p>
+ Own Id: OTP-10739</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.15.3</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 f3a051c989..5e182de41d 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -73,7 +73,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
</desc>
</func>
<func>
- <name>getenv() -> [string()]</name>
+ <name name="getenv" arity="0"/>
<fsummary>List all environment variables</fsummary>
<desc>
<p>Returns a list of all environment variables.
@@ -87,66 +87,51 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
</desc>
</func>
<func>
- <name>getenv(VarName) -> Value | false</name>
+ <name name="getenv" arity="1"/>
<fsummary>Get the value of an environment variable</fsummary>
- <type>
- <v>VarName = string() </v>
- <v>Value = string()</v>
- </type>
<desc>
- <p>Returns the <c>Value</c> of the environment variable
- <c>VarName</c>, or <c>false</c> if the environment variable
+ <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>VarName</c> and
- <c>Value</c>) may contain characters with codepoints > 255.</p>
+ 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>
- <name>getpid() -> Value </name>
+ <name name="getpid" arity="0"/>
<fsummary>Return the process identifier of the emulator process</fsummary>
- <type>
- <v>Value = string()</v>
- </type>
<desc>
<p>Returns the process identifier of the current Erlang emulator
in the format most commonly used by the operating system
- environment. <c>Value</c> is returned as a string containing
+ 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>Value</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>
</func>
<func>
- <name>putenv(VarName, Value) -> true</name>
+ <name name="putenv" arity="2"/>
<fsummary>Set a new value for an environment variable</fsummary>
- <type>
- <v>VarName = string() </v>
- <v>Value = string()</v>
- </type>
<desc>
- <p>Sets a new <c>Value</c> for the environment variable
- <c>VarName</c>.</p>
+ <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>VarName</c> and
- <c>Value</c>) may contain characters with codepoints > 255.</p>
+ 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>
- <name>timestamp() -> Timestamp</name>
+ <name name="timestamp" arity="0"/>
+ <type_desc variable="Timestamp">Timestamp = {MegaSecs, Secs, MicroSecs}</type_desc>
<fsummary>Returna a timestamp from the OS in the erlang:now/0 format</fsummary>
- <type>
- <v>Timestamp = {MegaSecs, Secs, MicroSecs} = <seealso marker="erts:erlang#type-timestamp">erlang:timestamp()</seealso></v>
- <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v>
- </type>
<desc>
<p>Returns a tuple in the same format as <seealso marker="erts:erlang#now/0">erlang:now/0</seealso>. The difference is that this function returns what the operating system thinks (a.k.a. the wall clock time) without any attempts at time correction. The result of two different calls to this function is <em>not</em> guaranteed to be different.</p>
<p>The most obvious use for this function is logging. The tuple can be used together with the function <seealso marker="stdlib:calendar#now_to_universal_time/1">calendar:now_to_universal_time/1</seealso>
@@ -183,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 8a82b91a90..0000000000
--- a/lib/kernel/doc/src/packages.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>2004</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>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>
-</erlref>
-
diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml
index 9ef0959271..96604a2fa2 100644
--- a/lib/kernel/doc/src/ref_man.xml
+++ b/lib/kernel/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml
index b41addaa0c..813bb06e1f 100644
--- a/lib/kernel/doc/src/specs.xml
+++ b/lib/kernel/doc/src/specs.xml
@@ -29,5 +29,4 @@
<xi:include href="../specs/specs_user.xml"/>
<xi:include href="../specs/specs_wrap_log_reader.xml"/>
<xi:include href="../specs/specs_zlib_stub.xml"/>
- <xi:include href="../specs/specs_packages.xml"/>
</specs>
diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl
index 5b52f6f294..e32c112e63 100644
--- a/lib/kernel/include/dist.hrl
+++ b/lib/kernel/include/dist.hrl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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 c76ff9e2f0..cb3c0a49f4 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -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
@@ -107,7 +107,6 @@ MODULES = \
net_adm \
net_kernel \
os \
- packages \
pg2 \
ram_file \
rpc \
@@ -149,7 +148,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
-ERL_COMPILE_FLAGS += -I../include
+ERL_COMPILE_FLAGS += -I../include -Werror
# ----------------------------------------------------
# Targets
@@ -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..6efc1b4499 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-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
@@ -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..9ed2c7a7d9 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-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
@@ -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
@@ -248,7 +247,7 @@ start_boot_application(Application, RestartType) ->
{{error, {already_loaded, AppName}}, _} ->
gen_server:call(?AC, {start_application, AppName, RestartType}, infinity);
{{error,{bad_environment_value,Env}}, permanent} ->
- Txt = io_lib:format("Bad environment variable: ~p Application: ~p",
+ Txt = io_lib:format("Bad environment variable: ~tp Application: ~p",
[Env, Application]),
exit({error, list_to_atom(lists:flatten(Txt))});
{Error, _} ->
@@ -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},
@@ -506,19 +495,21 @@ init(Init, Kernel) ->
{'EXIT', LoadError} ->
Reason = {'load error', LoadError},
Init ! {ack, self(), {error, to_string(Reason)}};
+ {error, Error} ->
+ Init ! {ack, self(), {error, to_string(Error)}};
{ok, NewS} ->
Init ! {ack, self(), ok},
gen_server:enter_loop(?MODULE, [], NewS,
{local, ?AC})
end;
{error, ErrorStr} ->
- Str = lists:flatten(io_lib:format("invalid config data: ~s", [ErrorStr])),
+ Str = lists:flatten(io_lib:format("invalid config data: ~ts", [ErrorStr])),
Init ! {ack, self(), {error, to_string(Str)}}
end;
{error, {File, Line, Str}} ->
ReasonStr =
lists:flatten(io_lib:format("error in config file "
- "~p (~w): ~s",
+ "~tp (~w): ~ts",
[File, Line, Str])),
Init ! {ack, self(), {error, to_string(ReasonStr)}}
end.
@@ -548,17 +539,17 @@ check_conf_data(ConfData) when is_list(ConfData) ->
end;
{AppName, List} when is_list(List) ->
ErrMsg = "application: "
- ++ lists:flatten(io_lib:format("~p",[AppName]))
+ ++ lists:flatten(io_lib:format("~tp",[AppName]))
++ "; application name must be an atom",
{error, ErrMsg};
{AppName, _List} ->
ErrMsg = "application: "
- ++ lists:flatten(io_lib:format("~p",[AppName]))
+ ++ lists:flatten(io_lib:format("~tp",[AppName]))
++ "; parameters must be a list",
{error, ErrMsg};
Else ->
ErrMsg = "invalid application name: " ++
- lists:flatten(io_lib:format(" ~p",[Else])),
+ lists:flatten(io_lib:format(" ~tp",[Else])),
{error, ErrMsg}
end;
check_conf_data(_ConfData) ->
@@ -581,10 +572,10 @@ check_para_kernel([{Para, _Val} | ParaList]) when is_atom(Para) ->
check_para_kernel(ParaList);
check_para_kernel([{Para, _Val} | _ParaList]) ->
{error, "application: kernel; invalid parameter: " ++
- lists:flatten(io_lib:format("~p",[Para]))};
+ lists:flatten(io_lib:format("~tp",[Para]))};
check_para_kernel(Else) ->
{error, "application: kernel; invalid parameter list: " ++
- lists:flatten(io_lib:format("~p",[Else]))}.
+ lists:flatten(io_lib:format("~tp",[Else]))}.
check_distributed([]) ->
@@ -605,10 +596,10 @@ check_para([{Para, _Val} | ParaList], AppName) when is_atom(Para) ->
check_para(ParaList, AppName);
check_para([{Para, _Val} | _ParaList], AppName) ->
{error, "application: " ++ AppName ++ "; invalid parameter: " ++
- lists:flatten(io_lib:format("~p",[Para]))};
+ lists:flatten(io_lib:format("~tp",[Para]))};
check_para([Else | _ParaList], AppName) ->
{error, "application: " ++ AppName ++ "; invalid parameter: " ++
- lists:flatten(io_lib:format("~p",[Else]))}.
+ lists:flatten(io_lib:format("~tp",[Else]))}.
-type calls() :: 'info' | 'prep_config_change' | 'which_applications'
@@ -1445,7 +1436,9 @@ make_appl(Name) when is_atom(Name) ->
{ok, [Application]} ->
{ok, make_appl_i(Application)};
{error, Reason} ->
- {error, {file:format_error(Reason), FName}}
+ {error, {file:format_error(Reason), FName}};
+ error ->
+ {error, "bad encoding"}
end
end;
make_appl(Application) ->
@@ -1454,12 +1447,17 @@ make_appl(Application) ->
prim_consult(FullName) ->
case erl_prim_loader:get_file(FullName) of
{ok, Bin, _} ->
- case erl_scan:string(binary_to_list(Bin)) of
- {ok, Tokens, _EndLine} ->
- prim_parse(Tokens, []);
- {error, Reason, _EndLine} ->
- {error, Reason}
- end;
+ case file_binary_to_list(Bin) of
+ {ok, String} ->
+ case erl_scan:string(String) of
+ {ok, Tokens, _EndLine} ->
+ prim_parse(Tokens, []);
+ {error, Reason, _EndLine} ->
+ {error, Reason}
+ end;
+ error ->
+ error
+ end;
error ->
{error, enoent}
end.
@@ -1532,7 +1530,7 @@ do_change_apps(Applications, Config, OldAppls) ->
%% Report errors, but do not terminate
%% (backwards compatible behaviour)
lists:foreach(fun({error, {SysFName, Line, Str}}) ->
- Str2 = lists:flatten(io_lib:format("~p: ~w: ~s~n",
+ Str2 = lists:flatten(io_lib:format("~tp: ~w: ~ts~n",
[SysFName, Line, Str])),
error_logger:format(Str2, [])
end,
@@ -1610,12 +1608,12 @@ make_term(Str) ->
{ok, Term} ->
Term;
{error, {_,M,Reason}} ->
- error_logger:format("application_controller: ~s: ~s~n",
+ error_logger:format("application_controller: ~ts: ~ts~n",
[M:format_error(Reason), Str]),
throw({error, {bad_environment_value, Str}})
end;
{error, {_,M,Reason}, _} ->
- error_logger:format("application_controller: ~s: ~s~n",
+ error_logger:format("application_controller: ~ts: ~ts~n",
[M:format_error(Reason), Str]),
throw({error, {bad_environment_value, Str}})
end.
@@ -1839,8 +1837,12 @@ load_file(File) ->
{ok, Bin, _FileName} ->
%% Make sure that there is some whitespace at the end of the string
%% (so that reading a file with no NL following the "." will work).
- Str = binary_to_list(Bin) ++ " ",
- scan_file(Str);
+ case file_binary_to_list(Bin) of
+ {ok, String} ->
+ scan_file(String ++ " ");
+ error ->
+ {error, {none, scan_file, "bad encoding"}}
+ end;
error ->
{error, {none, open_file, "configuration file not found"}}
end.
@@ -1958,6 +1960,18 @@ test_make_apps([], Res) ->
test_make_apps([A|Apps], Res) ->
test_make_apps(Apps, [make_appl(A) | Res]).
+file_binary_to_list(Bin) ->
+ Enc = case epp:read_encoding_from_binary(Bin) of
+ none -> epp:default_encoding();
+ Encoding -> Encoding
+ end,
+ case catch unicode:characters_to_list(Bin, Enc) of
+ String when is_list(String) ->
+ {ok, String};
+ _ ->
+ error
+ end.
+
%%-----------------------------------------------------------------
%% String conversion
%% Exit reason needs to be a printable string
@@ -1971,5 +1985,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/auth.erl b/lib/kernel/src/auth.erl
index 6ae786ebd9..7d463103e3 100644
--- a/lib/kernel/src/auth.erl
+++ b/lib/kernel/src/auth.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
@@ -384,14 +384,14 @@ create_cookie(Name) ->
{{error,Reason}, _} ->
{error,
lists:flatten(
- io_lib:format("Failed to write to cookie file '~s': ~p", [Name, Reason]))};
+ io_lib:format("Failed to write to cookie file '~ts': ~p", [Name, Reason]))};
{ok, {error, Reason}} ->
{error, "Failed to change mode: " ++ atom_to_list(Reason)}
end;
{error,Reason} ->
{error,
lists:flatten(
- io_lib:format("Failed to create cookie file '~s': ~p", [Name, Reason]))}
+ io_lib:format("Failed to create cookie file '~ts': ~p", [Name, Reason]))}
end.
random_cookie(0, _, Result) ->
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index 363072951e..03fba96d4b 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.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
@@ -64,52 +64,13 @@
where_is_file/1,
where_is_file/2,
set_primary_archive/4,
- clash/0]).
+ clash/0,
+ get_mode/0]).
-export_type([load_error_rsn/0, load_ret/0]).
-include_lib("kernel/include/file.hrl").
-%% User interface.
-%%
-%% objfile_extension() -> ".beam"
-%% get_path() -> [Dir]
-%% set_path([Dir]) -> true | {error, bad_directory | bad_path}
-%% add_path(Dir) -> true | {error, bad_directory}
-%% add_patha(Dir) -> true | {error, bad_directory}
-%% add_pathz(Dir) -> true | {error, bad_directory}
-%% add_paths([Dir]) -> ok
-%% add_pathsa([Dir]) -> ok
-%% add_pathsz([Dir]) -> ok
-%% del_path(Dir) -> boolean() | {error, bad_name}
-%% replace_path(Name, Dir) -> true | {error, bad_directory | bad_name
-%% | {badarg,_}}
-%% load_file(Module) -> {module, Module} | {error, What :: atom()}
-%% load_abs(File) -> {module, Module} | {error, What :: atom()}
-%% load_abs(File, Module) -> {module, Module} | {error, What :: atom()}
-%% load_binary(Module, File, Bin)-> {module, Module} | {error, What :: atom()}
-%% ensure_loaded(Module) -> {module, Module} | {error, What :: atom()}
-%% delete(Module) -> boolean()
-%% purge(Module) -> boolean() kills all procs running old code
-%% soft_purge(Module) -> boolean()
-%% is_loaded(Module) -> {file, loaded_filename()} | false
-%% all_loaded() -> [{Module, loaded_filename()}]
-%% get_object_code(Module) -> {Module, Bin, Filename} | error
-%% stop() -> no_return()
-%% root_dir() -> Dir
-%% compiler_dir() -> Dir
-%% lib_dir() -> Dir
-%% lib_dir(Application) -> Dir | {error, bad_name}
-%% priv_dir(Application) -> Dir | {error, bad_name}
-%% stick_dir(Dir) -> ok | error
-%% unstick_dir(Dir) -> ok | error
-%% stick_mod(Module) -> true
-%% unstick_mod(Module) -> true
-%% is_sticky(Module) -> boolean()
-%% which(Module) -> Filename | loaded_ret_atoms() | non_existing
-%% set_primary_archive((FileName, ArchiveBin, FileInfo, ParserFun) -> ok | {error, Reason}
-%% clash() -> ok prints out number of clashes
-
%%----------------------------------------------------------------------------
%% Some types for basic exported functions of this module
%%----------------------------------------------------------------------------
@@ -125,6 +86,39 @@
-type loaded_ret_atoms() :: 'cover_compiled' | 'preloaded'.
-type loaded_filename() :: (Filename :: file:filename()) | loaded_ret_atoms().
+%%% BIFs
+
+-export([get_chunk/2, is_module_native/1, make_stub_module/3, module_md5/1]).
+
+-spec get_chunk(Bin, Chunk) ->
+ binary() | undefined when
+ Bin :: binary(),
+ Chunk :: string().
+
+get_chunk(_, _) ->
+ erlang:nif_error(undef).
+
+-spec is_module_native(Module) -> true | false | undefined when
+ Module :: module().
+
+is_module_native(_) ->
+ erlang:nif_error(undef).
+
+-spec make_stub_module(Module, Beam, Info) -> Module when
+ Module :: module(),
+ Beam :: binary(),
+ Info :: {list(), list()}.
+
+make_stub_module(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec module_md5(binary()) -> binary() | undefined.
+
+module_md5(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%%----------------------------------------------------------------------------
%% User interface
%%----------------------------------------------------------------------------
@@ -300,6 +294,9 @@ replace_path(Name, Dir) when (is_atom(Name) orelse is_list(Name)),
-spec rehash() -> 'ok'.
rehash() -> call(rehash).
+-spec get_mode() -> 'embedded' | 'interactive'.
+get_mode() -> call(get_mode).
+
%%-----------------------------------------------------------------
call(Req) ->
@@ -366,7 +363,6 @@ load_code_server_prerequisites() ->
hipe_unified_loader,
lists,
os,
- packages,
unicode],
[M = M:module_info(module) || M <- Needed],
ok.
@@ -420,7 +416,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).
@@ -519,7 +515,7 @@ search([{Dir, File} | Tail]) ->
false ->
search(Tail);
{Dir2, File} ->
- io:format("** ~s hides ~s~n",
+ io:format("** ~ts hides ~ts~n",
[filename:join(Dir, File),
filename:join(Dir2, File)]),
[clash | search(Tail)]
@@ -536,7 +532,7 @@ decorate([File|Tail], Dir) ->
[{Dir, File} | decorate(Tail, Dir)].
filter(_Ext, Dir, error) ->
- io:format("** Bad path can't read ~s~n", [Dir]), [];
+ io:format("** Bad path can't read ~ts~n", [Dir]), [];
filter(Ext, _, {ok,Files}) ->
filter2(Ext, length(Ext), Files).
@@ -554,9 +550,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 00ad923466..5d74e8620b 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2012. 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
@@ -422,6 +422,9 @@ handle_call({is_cached,File}, {_From,_Tag}, S=#state{cache=Cache}) ->
end
end;
+handle_call(get_mode, {_From,_Tag}, S=#state{mode=Mode}) ->
+ {reply, Mode, S};
+
handle_call(Other,{_From,_Tag}, S) ->
error_msg(" ** Codeserver*** ignoring ~w~n ",[Other]),
{noreply,S}.
@@ -1229,7 +1232,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 ->
@@ -1266,11 +1269,11 @@ try_load_module_1(File, Mod, Bin, Caller, #state{moddb=Db}=St) ->
{error,on_load} ->
handle_on_load(Mod, File, Caller, St);
{error,What} = Error ->
- error_msg("Loading of ~s failed: ~p\n", [File, What]),
+ error_msg("Loading of ~ts failed: ~p\n", [File, What]),
{reply,Error,St}
end;
Error ->
- error_msg("Native loading of ~s failed: ~p\n",
+ error_msg("Native loading of ~ts failed: ~p\n",
[File,Error]),
{reply,ok,St}
end
@@ -1347,7 +1350,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 +1359,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 +1573,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..0c5af2857e 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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 = [],
@@ -1525,48 +1527,48 @@ do_format_error({size_mismatch, OldSize, ArgSize}) ->
io_lib:format("The given size ~p does not match the size ~p found on "
"the disk log size file~n", [ArgSize, OldSize]);
do_format_error({read_only_mode, Log}) ->
- io_lib:format("The disk log ~p has been opened read-only, but the "
+ io_lib:format("The disk log ~tp has been opened read-only, but the "
"requested operation needs read-write access~n", [Log]);
do_format_error({format_external, Log}) ->
io_lib:format("The requested operation can only be applied on internally "
- "formatted disk logs, but ~p is externally formatted~n",
+ "formatted disk logs, but ~tp is externally formatted~n",
[Log]);
do_format_error({blocked_log, Log}) ->
- io_lib:format("The blocked disk log ~p does not queue requests, or "
+ io_lib:format("The blocked disk log ~tp does not queue requests, or "
"the log has been blocked by the calling process~n", [Log]);
do_format_error({full, Log}) ->
- io_lib:format("The halt log ~p is full~n", [Log]);
+ io_lib:format("The halt log ~tp is full~n", [Log]);
do_format_error({not_blocked, Log}) ->
- io_lib:format("The disk log ~p is not blocked~n", [Log]);
+ io_lib:format("The disk log ~tp is not blocked~n", [Log]);
do_format_error({not_owner, Pid}) ->
- io_lib:format("The pid ~p is not an owner of the disk log~n", [Pid]);
+ io_lib:format("The pid ~tp is not an owner of the disk log~n", [Pid]);
do_format_error({not_blocked_by_pid, Log}) ->
- io_lib:format("The disk log ~p is blocked, but only the blocking pid "
+ io_lib:format("The disk log ~tp is blocked, but only the blocking pid "
"can unblock a disk log~n", [Log]);
do_format_error({new_size_too_small, Log, CurrentSize}) ->
- io_lib:format("The current size ~p of the halt log ~p is greater than the "
+ io_lib:format("The current size ~p of the halt log ~tp is greater than the "
"requested new size~n", [CurrentSize, Log]);
do_format_error({halt_log, Log}) ->
- io_lib:format("The halt log ~p cannot be wrapped~n", [Log]);
+ io_lib:format("The halt log ~tp cannot be wrapped~n", [Log]);
do_format_error({same_file_name, Log}) ->
- io_lib:format("Current and new file name of the disk log ~p "
+ io_lib:format("Current and new file name of the disk log ~tp "
"are the same~n", [Log]);
do_format_error({arg_mismatch, Option, FirstValue, ArgValue}) ->
- io_lib:format("The value ~p of the disk log option ~p does not match "
- "the current value ~p~n", [ArgValue, Option, FirstValue]);
+ io_lib:format("The value ~tp of the disk log option ~p does not match "
+ "the current value ~tp~n", [ArgValue, Option, FirstValue]);
do_format_error({name_already_open, Log}) ->
- io_lib:format("The disk log ~p has already opened another file~n", [Log]);
+ io_lib:format("The disk log ~tp has already opened another file~n", [Log]);
do_format_error({node_already_open, Log}) ->
- io_lib:format("The distribution option of the disk log ~p does not match "
+ io_lib:format("The distribution option of the disk log ~tp does not match "
"already open log~n", [Log]);
do_format_error({open_read_write, Log}) ->
- io_lib:format("The disk log ~p has already been opened read-write~n",
+ io_lib:format("The disk log ~tp has already been opened read-write~n",
[Log]);
do_format_error({open_read_only, Log}) ->
- io_lib:format("The disk log ~p has already been opened read-only~n",
+ io_lib:format("The disk log ~tp has already been opened read-only~n",
[Log]);
do_format_error({not_internal_wrap, Log}) ->
- io_lib:format("The requested operation cannot be applied since ~p is not "
+ io_lib:format("The requested operation cannot be applied since ~tp is not "
"an internally formatted disk log~n", [Log]);
do_format_error(no_such_log) ->
io_lib:format("There is no disk log with the given name~n", []);
@@ -1577,13 +1579,13 @@ do_format_error(nodedown) ->
io_lib:format("There seems to be no node up that can handle "
"the request~n", []);
do_format_error({corrupt_log_file, FileName}) ->
- io_lib:format("The disk log file \"~s\" contains corrupt data~n",
+ io_lib:format("The disk log file \"~ts\" contains corrupt data~n",
[FileName]);
do_format_error({need_repair, FileName}) ->
- io_lib:format("The disk log file \"~s\" has not been closed properly and "
+ io_lib:format("The disk log file \"~ts\" has not been closed properly and "
"needs repair~n", [FileName]);
do_format_error({not_a_log_file, FileName}) ->
- io_lib:format("The file \"~s\" is not a wrap log file~n", [FileName]);
+ io_lib:format("The file \"~ts\" is not a wrap log file~n", [FileName]);
do_format_error({invalid_header, InvalidHeader}) ->
io_lib:format("The disk log header is not wellformed: ~p~n",
[InvalidHeader]);
@@ -1591,14 +1593,14 @@ do_format_error(end_of_log) ->
io_lib:format("An attempt was made to step outside a not yet "
"full wrap log~n", []);
do_format_error({invalid_index_file, FileName}) ->
- io_lib:format("The wrap log index file \"~s\" cannot be used~n",
+ io_lib:format("The wrap log index file \"~ts\" cannot be used~n",
[FileName]);
do_format_error({no_continuation, BadCont}) ->
io_lib:format("The term ~p is not a chunk continuation~n", [BadCont]);
do_format_error({file_error, FileName, Reason}) ->
- io_lib:format("\"~s\": ~p~n", [FileName, file:format_error(Reason)]);
+ io_lib:format("\"~ts\": ~tp~n", [FileName, file:format_error(Reason)]);
do_format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
do_info(L, Cnt) ->
#log{name = Name, type = Type, mode = Mode, filename = File,
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index 266df84a03..9d431bdd30 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-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
@@ -489,7 +489,7 @@ lh(H, _F) -> % cannot happen
repair(In, File) ->
FSz = file_size(File),
- error_logger:info_msg("disk_log: repairing ~p ...\n", [File]),
+ error_logger:info_msg("disk_log: repairing ~tp ...\n", [File]),
Tmp = add_ext(File, "TMP"),
{ok, {_Alloc, Out, {0, _}, _FileSize}} = new_int_file(Tmp, none),
scan_f_read(<<>>, In, Out, File, FSz, Tmp, ?MAX_CHUNK_SIZE, 0, 0).
@@ -758,7 +758,7 @@ mf_int_chunk(Handle, {FileNo, Pos}, Bin, N) ->
NFileNo = inc(FileNo, Handle#handle.maxF),
case catch int_open(FName, true, read_only, any) of
{error, _Reason} ->
- error_logger:info_msg("disk_log: chunk error. File ~p missing.\n\n",
+ error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n",
[FName]),
mf_int_chunk(Handle, {NFileNo, 0}, [], N);
{ok, {_Alloc, FdC, _HeadSize, _FileSize}} ->
@@ -786,7 +786,7 @@ mf_int_chunk_read_only(Handle, {FileNo, Pos}, Bin, N) ->
NFileNo = inc(FileNo, Handle#handle.maxF),
case catch int_open(FName, true, read_only, any) of
{error, _Reason} ->
- error_logger:info_msg("disk_log: chunk error. File ~p missing.\n\n",
+ error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n",
[FName]),
mf_int_chunk_read_only(Handle, {NFileNo, 0}, [], N);
{ok, {_Alloc, FdC, _HeadSize, _FileSize}} ->
@@ -1495,7 +1495,7 @@ fwrite_close2(Fd, FileName, B) ->
pwrite_close2(Fd, FileName, Position, B) ->
case file:pwrite(Fd, Position, B) of
ok -> ok;
- Error -> file_error(FileName, {error, Error})
+ {error,Error} -> file_error(FileName, {error, Error})
end.
position2(Fd, FileName, Pos) ->
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index f0d54a2f3e..fc50ec6717 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. 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
@@ -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 646cac99c5..e03d280cd8 100644
--- a/lib/kernel/src/erl_ddll.erl
+++ b/lib/kernel/src/erl_ddll.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
@@ -30,9 +30,97 @@
%%----------------------------------------------------------------------------
-type path() :: string() | atom().
--type driver() :: string() | atom().
+-type driver() :: iolist() | atom().
%%----------------------------------------------------------------------------
+%%% BIFs
+
+-export([demonitor/1, info/2, format_error_int/1, monitor/2,
+ try_load/3, try_unload/2, loaded_drivers/0]).
+
+-spec demonitor(MonitorRef) -> ok when
+ MonitorRef :: reference().
+
+demonitor(_) ->
+ erlang:nif_error(undef).
+
+-spec info(Name, Tag) -> Value when
+ Name :: driver(),
+ Tag :: processes | driver_options | port_count | linked_in_driver
+ | permanent | awaiting_load | awaiting_unload,
+ Value :: term().
+
+info(_, _) ->
+ erlang:nif_error(undef).
+
+-spec format_error_int(ErrSpec) -> string() when
+ ErrSpec :: term().
+
+format_error_int(_) ->
+ erlang:nif_error(undef).
+
+-spec monitor(Tag, Item) -> MonitorRef when
+ Tag :: driver,
+ Item :: {Name, When},
+ Name :: driver(),
+ When :: loaded | unloaded | unloaded_only,
+ MonitorRef :: reference().
+
+monitor(_, _) ->
+ erlang:nif_error(undef).
+
+-spec try_load(Path, Name, OptionList) ->
+ {ok,Status} |
+ {ok, PendingStatus, Ref} |
+ {error, ErrorDesc} when
+ Path :: path(),
+ Name :: driver(),
+ OptionList :: [Option],
+ Option :: {driver_options, DriverOptionList}
+ | {monitor, MonitorOption}
+ | {reload, ReloadOption},
+ DriverOptionList :: [DriverOption],
+ DriverOption :: kill_ports,
+ MonitorOption :: pending_driver | pending,
+ ReloadOption :: pending_driver | pending,
+ Status :: loaded | already_loaded | PendingStatus,
+ PendingStatus :: pending_driver | pending_process,
+ Ref :: reference(),
+ ErrorDesc :: ErrorAtom | OpaqueError,
+ ErrorAtom :: linked_in_driver | inconsistent | permanent
+ | not_loaded_by_this_process | not_loaded
+ | pending_reload | pending_process,
+ OpaqueError :: term().
+
+try_load(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec try_unload(Name, OptionList) ->
+ {ok, Status} |
+ {ok, PendingStatus, Ref} |
+ {error, ErrorAtom} when
+ Name :: driver(),
+ OptionList :: [Option],
+ Option :: {monitor, MonitorOption} | kill_ports,
+ MonitorOption :: pending_driver | pending,
+ Status :: unloaded | PendingStatus,
+ PendingStatus :: pending_driver | pending_process,
+ Ref :: reference(),
+ ErrorAtom :: linked_in_driver | not_loaded |
+ not_loaded_by_this_process | permanent.
+
+try_unload(_, _) ->
+ erlang:nif_error(undef).
+
+-spec loaded_drivers() -> {ok, Drivers} when
+ Drivers :: [Driver],
+ Driver :: string().
+
+loaded_drivers() ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
-spec start() -> {'error', {'already_started', 'undefined'}}.
diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl
index a67b11a888..40aabba803 100644
--- a/lib/kernel/src/error_handler.erl
+++ b/lib/kernel/src/error_handler.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
@@ -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.
@@ -90,7 +95,7 @@ int() -> int.
crash(Fun, Args) ->
crash({Fun,Args,[]}).
--spec crash(atom(), atom(), arity()) -> no_return().
+-spec crash(atom(), atom(), arity() | [term()]) -> no_return().
crash(M, F, A) ->
crash({M,F,A,[]}).
@@ -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/error_logger.erl b/lib/kernel/src/error_logger.erl
index f94cca000f..92c1eb80dc 100644
--- a/lib/kernel/src/error_logger.erl
+++ b/lib/kernel/src/error_logger.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,18 @@
-type state() :: {non_neg_integer(), non_neg_integer(), [term()]}.
+%%% BIF
+
+-export([warning_map/0]).
+
+-spec warning_map() -> Tag when
+ Tag :: error | warning | info.
+
+warning_map() ->
+ erlang:nif_error(undef).
+
+%%% End of BIF
+
%%-----------------------------------------------------------------
-spec start() -> {'ok', pid()} | {'error', any()}.
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index b8871e0d45..6654cd9ee7 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -28,6 +28,135 @@
%% same/2
%% flat_size/1
+%%% BIFs
+
+-export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2,
+ dump_monitors/1, dump_links/1, flat_size/1,
+ get_internal_state/1, instructions/0, lock_counters/1,
+ same/2, set_internal_state/2]).
+
+-spec breakpoint(MFA, Flag) -> non_neg_integer() when
+ MFA :: {Module :: module(),
+ Function :: atom(),
+ Arity :: arity() | '_'},
+ Flag :: boolean().
+
+breakpoint(_, _) ->
+ erlang:nif_error(undef).
+
+-spec disassemble(What) -> false | undef | Result when
+ What :: MFA | Address,
+ Result :: {Address, Code, MFA},
+ MFA :: mfa(),
+ Address :: non_neg_integer(),
+ Code :: binary().
+
+disassemble(_) ->
+ erlang:nif_error(undef).
+
+-spec display(Term) -> string() when
+ Term :: term().
+
+display(_) ->
+ erlang:nif_error(undef).
+
+-spec dist_ext_to_term(Tuple, Binary) -> term() when
+ Tuple :: tuple(),
+ Binary :: binary().
+
+dist_ext_to_term(_, _) ->
+ erlang:nif_error(undef).
+
+-spec dump_monitors(Id) -> true when
+ Id :: pid() | atom().
+
+dump_monitors(_) ->
+ erlang:nif_error(undef).
+
+-spec dump_links(Id) -> true when
+ Id :: pid() | port() | atom().
+
+dump_links(_) ->
+ erlang:nif_error(undef).
+
+-spec flat_size(Term) -> non_neg_integer() when
+ Term :: term().
+
+flat_size(_) ->
+ erlang:nif_error(undef).
+
+-spec get_internal_state(W) -> term() when
+ W :: reds_left | node_and_dist_references | monitoring_nodes
+ | next_pid | 'DbTable_words' | check_io_debug
+ | process_info_args | processes | processes_bif_info
+ | max_atom_out_cache_index | nbalance | available_internal_state
+ | force_heap_frags | memory
+ | {process_status, pid()}
+ | {link_list, pid() | port() | node()}
+ | {monitor_list, pid() | node()}
+ | {channel_number, non_neg_integer()}
+ | {have_pending_exit, pid() | port() | atom()}
+ | {binary_info, binary()}
+ | {term_to_binary_no_funs, term()}
+ | {dist_port, port()}
+ | {atom_out_cache_index, atom()}
+ | {fake_scheduler_bindings,
+ default_bind | spread | processor_spread | thread_spread
+ | thread_no_node_processor_spread | no_node_processor_spread
+ | no_node_thread_spread | no_spread | unbound}
+ | {reader_groups_map, non_neg_integer()}.
+
+get_internal_state(_) ->
+ erlang:nif_error(undef).
+
+-spec instructions() -> [string()].
+
+instructions() ->
+ erlang:nif_error(undef).
+
+-spec lock_counters(info) -> term();
+ (clear) -> ok;
+ ({copy_save, boolean()}) -> boolean();
+ ({process_locks, boolean()}) -> boolean().
+
+lock_counters(_) ->
+ erlang:nif_error(undef).
+
+-spec same(Term1, Term2) -> boolean() when
+ Term1 :: term(),
+ Term2 :: term().
+
+same(_, _) ->
+ erlang:nif_error(undef).
+
+-spec set_internal_state(available_internal_state, boolean()) -> boolean();
+ (reds_left, non_neg_integer()) -> true;
+ (block, non_neg_integer()) -> true;
+ (sleep, non_neg_integer()) -> true;
+ (block_scheduler, non_neg_integer()) -> true;
+ (next_pid, non_neg_integer()) -> false | integer();
+ (force_gc, pid() | atom()) -> boolean();
+ (send_fake_exit_signal, {pid() | port(), pid(), term()}) -> dead | message | unaffected | exit;
+ (colliding_names, {atom(), non_neg_integer()}) ->
+ [atom()];
+ (binary_loop_limit, default) -> -1;
+ (binary_loop_limit, non_neg_integer()) -> non_neg_integer();
+ (re_loop_limit, default) -> -1;
+ (re_loop_limit, non_neg_integer()) -> non_neg_integer();
+ (unicode_loop_limit, default) -> -1;
+ (unicode_loop_limit, non_neg_integer()) -> non_neg_integer();
+ (hipe_test_reschedule_suspend, term()) -> nil();
+ (hipe_test_reschedule_resume, pid() | port()) -> boolean();
+ (test_long_gc_sleep, non_neg_integer()) -> true;
+ (kill_dist_connection, port()) -> boolean();
+ (not_running_optimization, boolean()) -> boolean();
+ (wait, deallocations) -> ok.
+
+set_internal_state(_, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%% size(Term)
%% Returns the size of Term in actual heap words. Shared subterms are
%% counted once. Example: If A = [a,b], B =[A,A] then size(B) returns 8,
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index cdb984c333..a4c56b346f 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.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
@@ -27,18 +27,18 @@
-export([format_error/1]).
%% File system and metadata.
-export([get_cwd/0, get_cwd/1, set_cwd/1, delete/1, rename/2,
- make_dir/1, del_dir/1, list_dir/1,
+ make_dir/1, del_dir/1, list_dir/1, list_dir_all/1,
read_file_info/1, read_file_info/2,
write_file_info/2, write_file_info/3,
altname/1,
read_link_info/1, read_link_info/2,
- read_link/1,
+ read_link/1, read_link_all/1,
make_link/2, make_symlink/2,
read_file/1, write_file/2, write_file/3]).
%% 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,
@@ -67,8 +67,8 @@
-export([ipread_s32bu_p32bu_int/3]).
%% Types that can be used from other modules -- alphabetically ordered.
--export_type([date_time/0, fd/0, file_info/0, filename/0, io_device/0,
- name/0, posix/0]).
+-export_type([date_time/0, fd/0, file_info/0, filename/0, filename_all/0,
+ io_device/0, name/0, name_all/0, posix/0]).
%%% Includes and defines
-include("file.hrl").
@@ -80,7 +80,8 @@
-define(RAM_FILE, ram_file). % Module
%% data types
--type filename() :: string() | binary().
+-type filename() :: string().
+-type filename_all() :: string() | binary().
-type file_info() :: #file_info{}.
-type fd() :: #file_descriptor{}.
-type io_device() :: pid() | fd().
@@ -96,7 +97,8 @@
| 'read_ahead' | 'compressed'
| {'encoding', unicode:encoding()}.
-type deep_list() :: [char() | atom() | deep_list()].
--type name() :: string() | atom() | deep_list() | (RawFilename :: binary()).
+-type name() :: string() | atom() | deep_list().
+-type name_all() :: string() | atom() | deep_list() | (RawFilename :: binary()).
-type posix() :: 'eacces' | 'eagain' | 'ebadf' | 'ebusy' | 'edquot'
| 'eexist' | 'efault' | 'efbig' | 'eintr' | 'einval'
| 'eio' | 'eisdir' | 'eloop' | 'emfile' | 'emlink'
@@ -111,6 +113,24 @@
-type sendfile_option() :: {chunk_size, non_neg_integer()}.
-type file_info_option() :: {'time', 'local'} | {'time', 'universal'}
| {'time', 'posix'}.
+%%% BIFs
+
+-export([file_info/1, native_name_encoding/0]).
+
+-spec file_info(Filename) -> {ok, FileInfo} | {error, Reason} when
+ Filename :: name_all(),
+ FileInfo :: file_info(),
+ Reason :: posix() | badarg.
+
+file_info(_) ->
+ erlang:nif_error(undef).
+
+-spec native_name_encoding() -> latin1 | utf8.
+
+native_name_encoding() ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
%%%-----------------------------------------------------------------
@@ -130,7 +150,7 @@ format_error({Line, ?MODULE, {Reason, Stacktrace}}) ->
io_lib:format("~w: evaluation failed with reason ~w and stacktrace ~w",
[Line, Reason, Stacktrace]);
format_error({Line, Mod, Reason}) ->
- io_lib:format("~w: ~s", [Line, Mod:format_error(Reason)]);
+ io_lib:format("~w: ~ts", [Line, Mod:format_error(Reason)]);
format_error(badarg) ->
"bad argument";
format_error(system_limit) ->
@@ -141,7 +161,7 @@ format_error(ErrorId) ->
erl_posix_msg:message(ErrorId).
-spec pid2name(Pid) -> {ok, Filename} | undefined when
- Filename :: filename(),
+ Filename :: filename_all(),
Pid :: pid().
pid2name(Pid) when is_pid(Pid) ->
@@ -178,42 +198,42 @@ get_cwd(Drive) ->
-spec set_cwd(Dir) -> ok | {error, Reason} when
Dir :: name(),
- Reason :: posix() | badarg.
+ Reason :: posix() | badarg | no_translation.
set_cwd(Dirname) ->
check_and_call(set_cwd, [file_name(Dirname)]).
-spec delete(Filename) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Reason :: posix() | badarg.
delete(Name) ->
check_and_call(delete, [file_name(Name)]).
-spec rename(Source, Destination) -> ok | {error, Reason} when
- Source :: name(),
- Destination :: name(),
+ Source :: name_all(),
+ Destination :: name_all(),
Reason :: posix() | badarg.
rename(From, To) ->
check_and_call(rename, [file_name(From), file_name(To)]).
-spec make_dir(Dir) -> ok | {error, Reason} when
- Dir :: name(),
+ Dir :: name_all(),
Reason :: posix() | badarg.
make_dir(Name) ->
check_and_call(make_dir, [file_name(Name)]).
-spec del_dir(Dir) -> ok | {error, Reason} when
- Dir :: name(),
+ Dir :: name_all(),
Reason :: posix() | badarg.
del_dir(Name) ->
check_and_call(del_dir, [file_name(Name)]).
-spec read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
FileInfo :: file_info(),
Reason :: posix() | badarg.
@@ -221,7 +241,7 @@ read_file_info(Name) ->
check_and_call(read_file_info, [file_name(Name)]).
-spec read_file_info(Filename, Opts) -> {ok, FileInfo} | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Opts :: [file_info_option()],
FileInfo :: file_info(),
Reason :: posix() | badarg.
@@ -229,13 +249,13 @@ read_file_info(Name) ->
read_file_info(Name, Opts) when is_list(Opts) ->
check_and_call(read_file_info, [file_name(Name), Opts]).
--spec altname(Name :: name()) -> any().
+-spec altname(Name :: name_all()) -> any().
altname(Name) ->
check_and_call(altname, [file_name(Name)]).
-spec read_link_info(Name) -> {ok, FileInfo} | {error, Reason} when
- Name :: name(),
+ Name :: name_all(),
FileInfo :: file_info(),
Reason :: posix() | badarg.
@@ -243,7 +263,7 @@ read_link_info(Name) ->
check_and_call(read_link_info, [file_name(Name)]).
-spec read_link_info(Name, Opts) -> {ok, FileInfo} | {error, Reason} when
- Name :: name(),
+ Name :: name_all(),
Opts :: [file_info_option()],
FileInfo :: file_info(),
Reason :: posix() | badarg.
@@ -253,15 +273,23 @@ read_link_info(Name, Opts) when is_list(Opts) ->
-spec read_link(Name) -> {ok, Filename} | {error, Reason} when
- Name :: name(),
+ Name :: name_all(),
Filename :: filename(),
Reason :: posix() | badarg.
read_link(Name) ->
check_and_call(read_link, [file_name(Name)]).
+-spec read_link_all(Name) -> {ok, Filename} | {error, Reason} when
+ Name :: name_all(),
+ Filename :: filename_all(),
+ Reason :: posix() | badarg.
+
+read_link_all(Name) ->
+ check_and_call(read_link_all, [file_name(Name)]).
+
-spec write_file_info(Filename, FileInfo) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
FileInfo :: file_info(),
Reason :: posix() | badarg.
@@ -269,7 +297,7 @@ write_file_info(Name, Info = #file_info{}) ->
check_and_call(write_file_info, [file_name(Name), Info]).
-spec write_file_info(Filename, FileInfo, Opts) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Opts :: [file_info_option()],
FileInfo :: file_info(),
Reason :: posix() | badarg.
@@ -278,15 +306,25 @@ write_file_info(Name, Info = #file_info{}, Opts) when is_list(Opts) ->
check_and_call(write_file_info, [file_name(Name), Info, Opts]).
-spec list_dir(Dir) -> {ok, Filenames} | {error, Reason} when
- Dir :: name(),
+ Dir :: name_all(),
Filenames :: [filename()],
- Reason :: posix() | badarg.
+ Reason :: posix()
+ | badarg
+ | {no_translation, Filename :: unicode:latin1_binary()}.
list_dir(Name) ->
check_and_call(list_dir, [file_name(Name)]).
+-spec list_dir_all(Dir) -> {ok, Filenames} | {error, Reason} when
+ Dir :: name_all(),
+ Filenames :: [filename_all()],
+ Reason :: posix() | badarg.
+
+list_dir_all(Name) ->
+ check_and_call(list_dir_all, [file_name(Name)]).
+
-spec read_file(Filename) -> {ok, Binary} | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Binary :: binary(),
Reason :: posix() | badarg | terminated | system_limit.
@@ -294,23 +332,23 @@ read_file(Name) ->
check_and_call(read_file, [file_name(Name)]).
-spec make_link(Existing, New) -> ok | {error, Reason} when
- Existing :: name(),
- New :: name(),
+ Existing :: name_all(),
+ New :: name_all(),
Reason :: posix() | badarg.
make_link(Old, New) ->
check_and_call(make_link, [file_name(Old), file_name(New)]).
-spec make_symlink(Existing, New) -> ok | {error, Reason} when
- Existing :: name(),
- New :: name(),
+ Existing :: name_all(),
+ New :: name_all(),
Reason :: posix() | badarg.
make_symlink(Old, New) ->
check_and_call(make_symlink, [file_name(Old), file_name(New)]).
-spec write_file(Filename, Bytes) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Bytes :: iodata(),
Reason :: posix() | badarg | terminated | system_limit.
@@ -322,7 +360,7 @@ write_file(Name, Bin) ->
%% Meanwhile, it is implemented here, slightly less efficient.
-spec write_file(Filename, Bytes, Modes) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Bytes :: iodata(),
Modes :: [mode()],
Reason :: posix() | badarg | terminated | system_limit.
@@ -379,9 +417,10 @@ raw_write_file_info(Name, #file_info{} = Info) ->
%% Contemporary mode specification - list of options
--spec open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} when
- Filename :: name(),
- Modes :: [mode()],
+-spec open(File, Modes) -> {ok, IoDevice} | {error, Reason} when
+ File :: Filename | iodata(),
+ Filename :: name_all(),
+ Modes :: [mode() | ram],
IoDevice :: io_device(),
Reason :: posix() | badarg | system_limit.
@@ -471,11 +510,26 @@ 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(),
Data :: string() | binary(),
- Reason :: posix() | badarg | terminated.
+ Reason :: posix()
+ | badarg
+ | terminated
+ | {no_translation, unicode, latin1}.
read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 ->
case io:request(File, {get_chars, '', Sz}) of
@@ -493,7 +547,10 @@ read(_, _) ->
-spec read_line(IoDevice) -> {ok, Data} | eof | {error, Reason} when
IoDevice :: io_device() | atom(),
Data :: string() | binary(),
- Reason :: posix() | badarg | terminated.
+ Reason :: posix()
+ | badarg
+ | terminated
+ | {no_translation, unicode, latin1}.
read_line(File) when (is_pid(File) orelse is_atom(File)) ->
case io:request(File, {get_line, ''}) of
@@ -560,7 +617,7 @@ pread(_, _, _) ->
write(File, Bytes) when (is_pid(File) orelse is_atom(File)) ->
case make_binary(Bytes) of
Bin when is_binary(Bin) ->
- io:request(File, {put_chars,Bin});
+ io:request(File, {put_chars,latin1,Bin});
Error ->
Error
end;
@@ -661,7 +718,7 @@ truncate(_) ->
-spec copy(Source, Destination) -> {ok, BytesCopied} | {error, Reason} when
Source :: io_device() | Filename | {Filename, Modes},
Destination :: io_device() | Filename | {Filename, Modes},
- Filename :: name(),
+ Filename :: name_all(),
Modes :: [mode()],
BytesCopied :: non_neg_integer(),
Reason :: posix() | badarg | terminated.
@@ -673,7 +730,7 @@ copy(Source, Dest) ->
{ok, BytesCopied} | {error, Reason} when
Source :: io_device() | Filename | {Filename, Modes},
Destination :: io_device() | Filename | {Filename, Modes},
- Filename :: name(),
+ Filename :: name_all(),
Modes :: [mode()],
ByteCount :: non_neg_integer() | infinity,
BytesCopied :: non_neg_integer(),
@@ -900,7 +957,7 @@ ipread_s32bu_p32bu_2(File,
%%% provide a higher-lever interface to files.
-spec consult(Filename) -> {ok, Terms} | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Terms :: [term()],
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -917,10 +974,10 @@ consult(File) ->
-spec path_consult(Path, Filename) -> {ok, Terms, FullName} | {error, Reason} when
Path :: [Dir],
- Dir :: name(),
- Filename :: name(),
+ Dir :: name_all(),
+ Filename :: name_all(),
Terms :: [term()],
- FullName :: filename(),
+ FullName :: filename_all(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -940,7 +997,7 @@ path_consult(Path, File) ->
end.
-spec eval(Filename) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -948,7 +1005,7 @@ eval(File) ->
eval(File, erl_eval:new_bindings()).
-spec eval(Filename, Bindings) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Bindings :: erl_eval:binding_struct(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -964,9 +1021,9 @@ eval(File, Bs) ->
end.
-spec path_eval(Path, Filename) -> {ok, FullName} | {error, Reason} when
- Path :: [Dir :: name()],
- Filename :: name(),
- FullName :: filename(),
+ Path :: [Dir :: name_all()],
+ Filename :: name_all(),
+ FullName :: filename_all(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -975,10 +1032,10 @@ path_eval(Path, File) ->
-spec path_eval(Path, Filename, Bindings) ->
{ok, FullName} | {error, Reason} when
- Path :: [Dir :: name()],
- Filename :: name(),
+ Path :: [Dir :: name_all()],
+ Filename :: name_all(),
Bindings :: erl_eval:binding_struct(),
- FullName :: filename(),
+ FullName :: filename_all(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -998,7 +1055,7 @@ path_eval(Path, File, Bs) ->
end.
-spec script(Filename) -> {ok, Value} | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Value :: term(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -1007,7 +1064,7 @@ script(File) ->
script(File, erl_eval:new_bindings()).
-spec script(Filename, Bindings) -> {ok, Value} | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Bindings :: erl_eval:binding_struct(),
Value :: term(),
Reason :: posix() | badarg | terminated | system_limit
@@ -1025,10 +1082,10 @@ script(File, Bs) ->
-spec path_script(Path, Filename) ->
{ok, Value, FullName} | {error, Reason} when
- Path :: [Dir :: name()],
- Filename :: name(),
+ Path :: [Dir :: name_all()],
+ Filename :: name_all(),
Value :: term(),
- FullName :: filename(),
+ FullName :: filename_all(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -1037,11 +1094,11 @@ path_script(Path, File) ->
-spec path_script(Path, Filename, Bindings) ->
{ok, Value, FullName} | {error, Reason} when
- Path :: [Dir :: name()],
- Filename :: name(),
+ Path :: [Dir :: name_all()],
+ Filename :: name_all(),
Bindings :: erl_eval:binding_struct(),
Value :: term(),
- FullName :: filename(),
+ FullName :: filename_all(),
Reason :: posix() | badarg | terminated | system_limit
| {Line :: integer(), Mod :: module(), Term :: term()}.
@@ -1070,11 +1127,11 @@ path_script(Path, File, Bs) ->
-spec path_open(Path, Filename, Modes) ->
{ok, IoDevice, FullName} | {error, Reason} when
- Path :: [Dir :: name()],
- Filename :: name(),
+ Path :: [Dir :: name_all()],
+ Filename :: name_all(),
Modes :: [mode()],
IoDevice :: io_device(),
- FullName :: filename(),
+ FullName :: filename_all(),
Reason :: posix() | badarg | system_limit.
path_open(PathList, Name, Mode) ->
@@ -1096,7 +1153,7 @@ path_open(PathList, Name, Mode) ->
end.
-spec change_mode(Filename, Mode) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Mode :: integer(),
Reason :: posix() | badarg.
@@ -1105,7 +1162,7 @@ change_mode(Name, Mode)
write_file_info(Name, #file_info{mode=Mode}).
-spec change_owner(Filename, Uid) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Uid :: integer(),
Reason :: posix() | badarg.
@@ -1114,7 +1171,7 @@ change_owner(Name, OwnerId)
write_file_info(Name, #file_info{uid=OwnerId}).
-spec change_owner(Filename, Uid, Gid) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Uid :: integer(),
Gid :: integer(),
Reason :: posix() | badarg.
@@ -1124,7 +1181,7 @@ change_owner(Name, OwnerId, GroupId)
write_file_info(Name, #file_info{uid=OwnerId, gid=GroupId}).
-spec change_group(Filename, Gid) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Gid :: integer(),
Reason :: posix() | badarg.
@@ -1133,7 +1190,7 @@ change_group(Name, GroupId)
write_file_info(Name, #file_info{gid=GroupId}).
-spec change_time(Filename, Mtime) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Mtime :: date_time(),
Reason :: posix() | badarg.
@@ -1143,7 +1200,7 @@ change_time(Name, {{Y, M, D}, {H, Min, Sec}}=Time)
write_file_info(Name, #file_info{mtime=Time}).
-spec change_time(Filename, Atime, Mtime) -> ok | {error, Reason} when
- Filename :: name(),
+ Filename :: name_all(),
Atime :: date_time(),
Mtime :: date_time(),
Reason :: posix() | badarg.
@@ -1165,7 +1222,7 @@ change_time(Name, {{AY, AM, AD}, {AH, AMin, ASec}}=Atime,
-spec sendfile(RawFile, Socket, Offset, Bytes, Opts) ->
{'ok', non_neg_integer()} | {'error', inet:posix() |
closed | badarg | not_owner} when
- RawFile :: file:fd(),
+ RawFile :: fd(),
Socket :: inet:socket(),
Offset :: non_neg_integer(),
Bytes :: non_neg_integer(),
@@ -1191,7 +1248,7 @@ sendfile(File, Sock, Offset, Bytes, Opts) ->
-spec sendfile(Filename, Socket) ->
{'ok', non_neg_integer()} | {'error', inet:posix() |
closed | badarg | not_owner}
- when Filename :: file:name(),
+ when Filename :: name_all(),
Socket :: inet:socket().
sendfile(Filename, Sock) ->
case file:open(Filename, [read, raw, binary]) of
@@ -1296,6 +1353,7 @@ sendfile_send(Sock, Data, Old) ->
%%% Helpers
consult_stream(Fd) ->
+ _ = epp:set_encoding(Fd),
consult_stream(Fd, 1, []).
consult_stream(Fd, Line, Acc) ->
@@ -1309,6 +1367,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/file_server.erl b/lib/kernel/src/file_server.erl
index fc6cd823c9..49ec6f96cc 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -136,6 +136,8 @@ handle_call({del_dir, Name}, _From, Handle) ->
handle_call({list_dir, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:list_dir(Handle, Name), Handle};
+handle_call({list_dir_all, Name}, _From, Handle) ->
+ {reply, ?PRIM_FILE:list_dir_all(Handle, Name), Handle};
handle_call(get_cwd, _From, Handle) ->
{reply, ?PRIM_FILE:get_cwd(Handle), Handle};
@@ -167,6 +169,8 @@ handle_call({read_link_info, Name, Opts}, _From, Handle) ->
handle_call({read_link, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:read_link(Handle, Name), Handle};
+handle_call({read_link_all, Name}, _From, Handle) ->
+ {reply, ?PRIM_FILE:read_link_all(Handle, Name), Handle};
handle_call({make_link, Old, New}, _From, Handle) ->
{reply, ?PRIM_FILE:make_link(Handle, Old, New), Handle};
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..a98ed4c238 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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()} |
@@ -250,7 +256,7 @@ close(S) ->
-spec send(Socket, Packet) -> ok | {error, Reason} when
Socket :: socket(),
Packet :: iodata(),
- Reason :: inet:posix().
+ Reason :: closed | inet:posix().
send(S, Packet) when is_port(S) ->
case inet_db:lookup_socket(S) of
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 830ca61b3c..c5a1173575 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -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..ff835e1047 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. 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
@@ -227,7 +227,7 @@ io_request({put_chars,latin1,Chars}, Drv, Buf) ->
send_drv(Drv, {put_chars, unicode,Binary}),
{ok,ok,Buf};
_ ->
- {error,{error,{put_chars,Chars}},Buf}
+ {error,{error,{put_chars,latin1,Chars}},Buf}
end;
io_request({put_chars,latin1,M,F,As}, Drv, Buf) ->
case catch apply(M, F, As) of
@@ -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) ->
@@ -446,7 +446,6 @@ get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) ->
end.
get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State0, Line, Encoding) ->
- id(M,F),
case catch M:F(State0, cast(Line,get(read_mode), Encoding), Encoding, Xa) of
{stop,Result,Rest} ->
{ok,Result,append(Rest, Buf, Encoding)};
@@ -456,8 +455,6 @@ get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State0, Line, Encoding) ->
get_chars_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding)
end.
-id(M,F) ->
- {M,F}.
%% Convert error code to make it look as before
err_func(io_lib, get_until, {_,F,_}) ->
F;
@@ -515,6 +512,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 +553,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 +626,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 +700,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 +751,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 +810,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/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index 514c002d87..e676ca997d 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -218,7 +218,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
{MFAs,Addresses} = exports(ExportMap, CodeAddress),
%% Remove references to old versions of the module.
ReferencesToPatch = get_refs_from(MFAs, []),
- remove_refs_from(MFAs),
+ ok = remove_refs_from(MFAs),
%% Patch all dynamic references in the code.
%% Function calls, Atoms, Constants, System calls
patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
@@ -337,11 +337,16 @@ exports(ExportMap, BaseAddress) ->
exports(ExportMap, BaseAddress, [], []).
exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, MFAs, Addresses) ->
- MFA = {M,F,A},
- Address = BaseAddress + Offset,
- FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure,
- is_exported=IsExported},
- exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|Addresses]);
+ case IsExported andalso erlang:is_builtin(M, F, A) of
+ true ->
+ exports(Rest, BaseAddress, MFAs, Addresses);
+ _false ->
+ MFA = {M,F,A},
+ Address = BaseAddress + Offset,
+ FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure,
+ is_exported=IsExported},
+ exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|Addresses])
+ end;
exports([], _, MFAs, Addresses) ->
{MFAs, Addresses}.
@@ -505,7 +510,7 @@ patch_offset(Type, Data, Address, ConstAndZone, Addresses) ->
Atom = Data,
patch_atom(Address, Atom);
sdesc ->
- patch_sdesc(Data, Address, ConstAndZone);
+ patch_sdesc(Data, Address, ConstAndZone, Addresses);
x86_abs_pcrel ->
patch_instr(Address, Data, x86_abs_pcrel)
%% _ ->
@@ -518,14 +523,16 @@ patch_atom(Address, Atom) ->
patch_instr(Address, hipe_bifs:atom_to_word(Atom), atom).
patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live),
- Address, {_ConstMap2,CodeAddress}) ->
+ Address, {_ConstMap2,CodeAddress}, _Addresses) ->
ExnRA =
case SymExnRA of
[] -> 0; % No catch
LabelOffset -> CodeAddress + LabelOffset
end,
?ASSERT(assert_local_patch(Address)),
- hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live}).
+ DBG_MFA = ?IF_DEBUG(address_to_mfa_lth(Address, _Addresses), {undefined,undefined,0}),
+ hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, DBG_MFA}).
+
%%----------------------------------------------------------------
%% Handle a 'load_address'-type patch.
@@ -732,7 +739,7 @@ find_const(ConstNo, []) ->
%%
add_ref(CalleeMFA, Address, Addresses, RefType, Trampoline, RemoteOrLocal) ->
- CallerMFA = address_to_mfa(Address, Addresses),
+ CallerMFA = address_to_mfa_lth(Address, Addresses),
%% just a sanity assertion below
true = case RemoteOrLocal of
local ->
@@ -745,11 +752,31 @@ add_ref(CalleeMFA, Address, Addresses, RefType, Trampoline, RemoteOrLocal) ->
%% io:format("Adding ref ~w\n",[{CallerMFA, CalleeMFA, Address, RefType}]),
hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline,RemoteOrLocal}).
-address_to_mfa(Address, [#fundef{address=Adr, mfa=MFA}|_Rest]) when Address >= Adr -> MFA;
-address_to_mfa(Address, [_ | Rest]) -> address_to_mfa(Address, Rest);
-address_to_mfa(Address, []) ->
- ?error_msg("Local adddress not found ~w\n",[Address]),
- exit({?MODULE, local_address_not_found}).
+% For FunDefs sorted from low to high addresses
+address_to_mfa_lth(Address, FunDefs) ->
+ case address_to_mfa_lth(Address, FunDefs, false) of
+ false ->
+ ?error_msg("Local adddress not found ~w\n",[Address]),
+ exit({?MODULE, local_address_not_found});
+ MFA ->
+ MFA
+ end.
+
+address_to_mfa_lth(Address, [#fundef{address=Adr, mfa=MFA}|Rest], Prev) ->
+ if Address < Adr ->
+ Prev;
+ true ->
+ address_to_mfa_lth(Address, Rest, MFA)
+ end;
+address_to_mfa_lth(_Address, [], Prev) ->
+ Prev.
+
+% For FunDefs sorted from high to low addresses
+%% address_to_mfa_htl(Address, [#fundef{address=Adr, mfa=MFA}|_Rest]) when Address >= Adr -> MFA;
+%% address_to_mfa_htl(Address, [_ | Rest]) -> address_to_mfa_htl(Address, Rest);
+%% address_to_mfa_htl(Address, []) ->
+%% ?error_msg("Local adddress not found ~w\n",[Address]),
+%% exit({?MODULE, local_address_not_found}).
%%----------------------------------------------------------------
%% Change callers of the given module to instead trap to BEAM.
@@ -775,7 +802,7 @@ patch_to_emu_step1(Mod) ->
%% Find all call sites that call these MFAs. As a side-effect,
%% create native stubs for any MFAs that are referred.
ReferencesToPatch = get_refs_from(MFAs, []),
- remove_refs_from(MFAs),
+ ok = remove_refs_from(MFAs),
ReferencesToPatch;
false ->
%% The first time we load the module, no redirection needs to be done.
@@ -819,11 +846,8 @@ get_refs_from(MFAs, []) ->
mark_referred_from(MFAs),
MFAs.
-mark_referred_from([MFA|MFAs]) ->
- hipe_bifs:mark_referred_from(MFA),
- mark_referred_from(MFAs);
-mark_referred_from([]) ->
- [].
+mark_referred_from(MFAs) ->
+ lists:foreach(fun(MFA) -> hipe_bifs:mark_referred_from(MFA) end, MFAs).
%%--------------------------------------------------------------------
%% Given a list of MFAs with referred_from references, update their
@@ -831,11 +855,8 @@ mark_referred_from([]) ->
%%
%% The {MFA,Refs} list must come from get_refs_from/2.
%%
-redirect([MFA|Rest]) ->
- hipe_bifs:redirect_referred_from(MFA),
- redirect(Rest);
-redirect([]) ->
- ok.
+redirect(MFAs) ->
+ lists:foreach(fun(MFA) -> hipe_bifs:redirect_referred_from(MFA) end, MFAs).
%%--------------------------------------------------------------------
%% Given a list of MFAs, remove all referred_from references having
@@ -847,11 +868,8 @@ redirect([]) ->
%% list. The refers_to list is used here to find the CalleeMFAs whose
%% referred_from lists should be updated.
%%
-remove_refs_from([CallerMFA|CallerMFAs]) ->
- hipe_bifs:remove_refs_from(CallerMFA),
- remove_refs_from(CallerMFAs);
-remove_refs_from([]) ->
- [].
+remove_refs_from(MFAs) ->
+ lists:foreach(fun(MFA) -> hipe_bifs:remove_refs_from(MFA) end, MFAs).
%%--------------------------------------------------------------------
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 1a03424f88..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,15 +529,58 @@ 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,
- bit8, send_timeout, send_timeout_close
+ high_msgq_watermark, low_msgq_watermark,
+ send_timeout, send_timeout_close
].
%% Return a list of statistics options
@@ -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, bit8, 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, bit8, 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):
@@ -1283,7 +1329,10 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
_ ->
case prim_inet:getopt(S, active) of
{ok, A0} ->
- prim_inet:setopt(S, active, false),
+ case A0 of
+ false -> ok;
+ _ -> prim_inet:setopt(S, active, false)
+ end,
case tcp_sync_input(S, NewOwner, false) of
true -> %% socket already closed,
ok;
@@ -1291,7 +1340,10 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S), %% unlink from port
- prim_inet:setopt(S, active, A0),
+ case A0 of
+ false -> ok;
+ _ -> prim_inet:setopt(S, active, A0)
+ end,
ok
catch
error:Reason ->
diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index b9c4fa607c..2cb0e10c87 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-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
@@ -71,8 +71,12 @@ listen(Name) ->
{ok, Socket} ->
TcpAddress = get_tcp_address(Socket),
{_,Port} = TcpAddress#net_address.address,
- {ok, Creation} = erl_epmd:register_node(Name, Port),
- {ok, {Socket, TcpAddress, Creation}};
+ case erl_epmd:register_node(Name, Port) of
+ {ok, Creation} ->
+ {ok, {Socket, TcpAddress, Creation}};
+ Error ->
+ Error
+ end;
Error ->
Error
end.
diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl
index 1ddbdcec25..2461f3ff25 100644
--- a/lib/kernel/src/inet_config.erl
+++ b/lib/kernel/src/inet_config.erl
@@ -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
@@ -104,7 +104,7 @@ init() ->
%% Add inetrc config entries
case inet_db:add_rc_list(CfgList) of
ok -> ok;
- _ -> error("syntax error in ~s~n", [RcFile])
+ _ -> error("syntax error in ~ts~n", [RcFile])
end,
%% Set up a resolver configuration file for inet_res,
@@ -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]).
@@ -276,10 +266,10 @@ load_resolv(File, Func) ->
{ok, Ls} ->
inet_db:add_rc_list(Ls);
{error, Reason} ->
- error("parse error in file ~s: ~p", [File, Reason])
+ error("parse error in file ~ts: ~p", [File, Reason])
end;
Error ->
- warning("file not found ~s: ~p~n", [File, Error])
+ warning("file not found ~ts: ~p~n", [File, Error])
end.
%%
@@ -295,12 +285,12 @@ load_hosts(File,Os) ->
inet_db:add_host(IP, [Name|Aliases]) end,
Ls);
{error, Reason} ->
- error("parse error in file ~s: ~p", [File, Reason])
+ error("parse error in file ~ts: ~p", [File, Reason])
end;
Error ->
case Os of
unix ->
- error("file not found ~s: ~p~n", [File, Error]);
+ error("file not found ~ts: ~p~n", [File, Error]);
_ ->
%% for windows or nt the hosts file is not always there
%% and we don't require it
@@ -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
@@ -521,11 +462,11 @@ get_rc(File) ->
{ok,Ls} ->
Ls;
_Error ->
- error("parse error in ~s~n", [File]),
+ error("parse error in ~ts~n", [File]),
error
end;
_Error ->
- error("file ~s not found~n", [File]),
+ error("file ~ts not found~n", [File]),
error
end.
@@ -554,8 +495,12 @@ warning(Fmt, Args) ->
%% Ignore leading whitespace before a token (due to bug in erl_scan) !
%%
parse_inetrc(Bin) ->
- Str = binary_to_list(Bin) ++ "\n",
- parse_inetrc(Str, 1, []).
+ case file_binary_to_list(Bin) of
+ {ok, String} ->
+ parse_inetrc(String ++ "\n", 1, []);
+ error ->
+ {error, 'bad_encoding'}
+ end.
parse_inetrc_skip_line([], _Line, Ack) ->
{ok, reverse(Ack)};
@@ -594,3 +539,16 @@ parse_inetrc(Str, Line, Ack) ->
{more, _} -> %% Bug in erl_scan !!
{error, {'scan_inetrc', {eof, Line}}}
end.
+
+file_binary_to_list(Bin) ->
+ Enc = case epp:read_encoding_from_binary(Bin) of
+ none -> epp:default_encoding();
+ Encoding -> Encoding
+ end,
+ case catch unicode:characters_to_list(Bin, Enc) of
+ String when is_list(String) ->
+ {ok, String};
+ _ ->
+ error
+ end.
+
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index cf893c73eb..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).
@@ -134,13 +135,14 @@
-define(INET_LOPT_EXITONCLOSE, 26).
-define(INET_LOPT_TCP_HIWTRMRK, 27).
-define(INET_LOPT_TCP_LOWTRMRK, 28).
--define(INET_LOPT_BIT8, 29).
-define(INET_LOPT_TCP_SEND_TIMEOUT, 30).
-define(INET_LOPT_TCP_DELAY_SEND, 31).
-define(INET_LOPT_PACKET_SIZE, 32).
-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).
@@ -186,12 +188,6 @@
-define(TCP_PB_HTTP_BIN,13).
-define(TCP_PB_HTTPH_BIN,14).
-%% bit options, INET_LOPT_BIT8
--define(INET_BIT8_CLEAR, 0).
--define(INET_BIT8_SET, 1).
--define(INET_BIT8_ON, 2).
--define(INET_BIT8_OFF, 3).
-
%% getstat, INET_REQ_GETSTAT
-define(INET_STAT_RECV_CNT, 1).
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index 65edddcb46..619c78a6ca 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. 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
@@ -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(_) ->
+ {error, einval}.
+
%%
%% Parse IPv4 address:
%% d1.d2.d3.d4
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index 7f935c2b36..8005eff58c 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. 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
@@ -67,8 +67,12 @@ listen(Name) ->
{ok, Socket} ->
TcpAddress = get_tcp_address(Socket),
{_,Port} = TcpAddress#net_address.address,
- {ok, Creation} = erl_epmd:register_node(Name, Port),
- {ok, {Socket, TcpAddress, Creation}};
+ case erl_epmd:register_node(Name, Port) of
+ {ok, Creation} ->
+ {ok, {Socket, TcpAddress, Creation}};
+ Error ->
+ Error
+ end;
Error ->
Error
end.
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 17ab84c177..cb8c98ab06 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. 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
@@ -28,7 +28,6 @@
application_starter,
auth,
code,
- packages,
code_server,
dist_util,
erl_boot_server,
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index bded2408a7..54628800a8 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -17,11 +17,11 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max two major revisions back
- [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
%% Down to - max two major revisions back
- [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+ [{<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
}.
diff --git a/lib/kernel/src/kernel_config.erl b/lib/kernel/src/kernel_config.erl
index b1daf655c9..48141cfa03 100644
--- a/lib/kernel/src/kernel_config.erl
+++ b/lib/kernel/src/kernel_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. 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
@@ -93,7 +93,7 @@ code_change(_OldVsn, State, _Extra) ->
sync_nodes() ->
case catch get_sync_data() of
{error, Reason} = Error ->
- error_logger:format("~p", [Reason]),
+ error_logger:format("~tp", [Reason]),
Error;
{infinity, MandatoryNodes, OptionalNodes} ->
case wait_nodes(MandatoryNodes, OptionalNodes) of
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 9e3d730cee..dd0071b914 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.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
@@ -142,6 +142,17 @@
-include("net_address.hrl").
+%%% BIF
+
+-export([dflag_unicode_io/1]).
+
+-spec dflag_unicode_io(pid()) -> boolean().
+
+dflag_unicode_io(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIF
+
%% Interface functions
kernel_apply(M,F,A) -> request({apply,M,F,A}).
@@ -1329,20 +1340,20 @@ start_protos(Name, [Proto | Ps], Node, Ls) ->
start_protos(Name, Ps, Node, Ls)
end;
{'EXIT', {undef,_}} ->
- error_logger:info_msg("Protocol: ~p: not supported~n", [Proto]),
+ error_logger:info_msg("Protocol: ~tp: not supported~n", [Proto]),
start_protos(Name,Ps, Node, Ls);
{'EXIT', Reason} ->
- error_logger:info_msg("Protocol: ~p: register error: ~p~n",
+ error_logger:info_msg("Protocol: ~tp: register error: ~tp~n",
[Proto, Reason]),
start_protos(Name,Ps, Node, Ls);
{error, duplicate_name} ->
- error_logger:info_msg("Protocol: ~p: the name " ++
+ error_logger:info_msg("Protocol: ~tp: the name " ++
atom_to_list(Node) ++
" seems to be in use by another Erlang node",
[Proto]),
start_protos(Name,Ps, Node, Ls);
{error, Reason} ->
- error_logger:info_msg("Protocol: ~p: register/listen error: ~p~n",
+ error_logger:info_msg("Protocol: ~tp: register/listen error: ~tp~n",
[Proto, Reason]),
start_protos(Name,Ps, Node, Ls)
end;
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f6769df585..742c000cc1 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -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
@@ -24,16 +24,48 @@
-include("file.hrl").
--spec type() -> vxworks | {Osfamily, Osname} when
+%%% BIFs
+
+-export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0]).
+
+-spec getenv() -> [string()].
+
+getenv() -> erlang:nif_error(undef).
+
+-spec getenv(VarName) -> Value | false when
+ VarName :: string(),
+ Value :: string().
+
+getenv(_) ->
+ erlang:nif_error(undef).
+
+-spec getpid() -> Value when
+ Value :: string().
+
+getpid() ->
+ erlang:nif_error(undef).
+
+-spec putenv(VarName, Value) -> true when
+ VarName :: string(),
+ Value :: string().
+
+putenv(_, _) ->
+ erlang:nif_error(undef).
+
+-spec timestamp() -> Timestamp when
+ Timestamp :: erlang:timestamp().
+
+timestamp() ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
+-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(),
@@ -83,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
@@ -154,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.
@@ -173,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.
@@ -319,7 +334,7 @@ mk_cmd(Cmd) when is_atom(Cmd) -> % backward comp.
mk_cmd(Cmd) ->
%% We insert a new line after the command, in case the command
%% contains a comment character.
- io_lib:format("(~s\n) </dev/null; echo \"\^D\"\n", [Cmd]).
+ io_lib:format("(~ts\n) </dev/null; echo \"\^D\"\n", [Cmd]).
validate(Atom) when is_atom(Atom) ->
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 0b1fc6e939..7c965ca384 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -62,6 +62,8 @@
%% Internals
-export([proxy_user_flush/0]).
+-export_type([key/0]).
+
%%------------------------------------------------------------------------
-type state() :: gb_tree().
diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl
index 88f32df20b..c897d46bc2 100644
--- a/lib/kernel/src/user.erl
+++ b/lib/kernel/src/user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. 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
@@ -17,7 +17,7 @@
%% %CopyrightEnd%
%%
-module(user).
--compile( [ inline, { inline_size, 100 } ] ).
+-compile(inline).
%% Basic standard i/o server for user interface port.
@@ -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, [_|_]} ->
@@ -184,50 +184,52 @@ do_io_request(Req, From, ReplyAs, Port, Q0) ->
io_reply(From, ReplyAs, Reply),
Q1;
{exit,What} ->
- send_port(Port, close),
+ ok = send_port(Port, close),
exit(What)
end.
%% 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);
+ case wrap_characters_to_binary(Chars, unicode, get(encoding)) of
+ error ->
+ {error,{error,put_chars},Q};
+ Bin ->
+ put_chars(Bin, Port, Q)
+ end;
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);
- Undef ->
- Undef
- end,
- put_chars(Result, Port, Q);
+ case catch apply(Mod,Func,Args) of
+ Data when is_list(Data); is_binary(Data) ->
+ case wrap_characters_to_binary(Data, unicode, get(encoding)) of
+ Bin when is_binary(Bin) ->
+ put_chars(Bin, Port, Q);
+ error ->
+ {error,{error,put_chars},Q}
+ end;
+ Undef ->
+ put_chars(Undef, Port, Q)
+ end;
io_request({put_chars,latin1,Chars}, Port, Q) -> % Binary new in R9C
- Data = case get(unicode) of
- true ->
- unicode:characters_to_binary(Chars,latin1,unicode);
- false ->
- erlang:iolist_to_binary(Chars)
- end,
- put_chars(Data, Port, Q);
+ case catch unicode:characters_to_binary(Chars, latin1, get(encoding)) of
+ Data when is_binary(Data) ->
+ put_chars(Data, Port, Q);
+ _ ->
+ {error,{error,put_chars},Q}
+ end;
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);
- Undef ->
- Undef
- end,
- put_chars(Result, Port, Q);
+ case catch apply(Mod,Func,Args) of
+ Data when is_list(Data); is_binary(Data) ->
+ case
+ catch unicode:characters_to_binary(Data,latin1,get(encoding))
+ of
+ Bin when is_binary(Bin) ->
+ put_chars(Bin, Port, Q);
+ _ ->
+ {error,{error,put_chars},Q}
+ end;
+ Undef ->
+ put_chars(Undef, Port, Q)
+ end;
io_request({get_chars,Enc,Prompt,N}, Port, Q) -> % New in R9C
get_chars(Prompt, io_lib, collect_chars, N, Port, Q, Enc);
io_request({get_line,Enc,Prompt}, Port, Q) ->
@@ -297,7 +299,8 @@ put_port(List, Port) ->
%% send_port(Port, Command)
send_port(Port, Command) ->
- Port ! {self(),Command}.
+ Port ! {self(),Command},
+ ok.
%% io_reply(From, ReplyAs, Reply)
%% The function for sending i/o command acknowledgement.
@@ -308,7 +311,7 @@ io_reply(From, ReplyAs, Reply) ->
%% put_chars
put_chars(Chars, Port, Q) when is_binary(Chars) ->
- put_port(Chars, Port),
+ ok = put_port(Chars, Port),
{ok,ok,Q};
put_chars(Chars, Port, Q) ->
case catch list_to_binary(Chars) of
@@ -351,9 +354,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,23 +373,22 @@ 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}.
-
get_line_bin(Prompt,Port,Q, Enc) ->
- prompt(Port, Prompt),
- case {get(eof),queue:is_empty(Q)} of
- {true,true} ->
- {ok,eof,Q};
- _ ->
- get_line(Prompt,Port, Q, [], Enc)
+ case prompt(Port, Prompt) of
+ error ->
+ {error,{error,get_line},Q};
+ ok ->
+ case {get(eof),queue:is_empty(Q)} of
+ {true,true} ->
+ {ok,eof,Q};
+ _ ->
+ get_line(Prompt,Port, Q, [], Enc)
+ end
end.
+
get_line(Prompt, Port, Q, Acc, Enc) ->
case queue:is_empty(Q) of
true ->
@@ -403,8 +405,12 @@ get_line(Prompt, Port, Q, Acc, Enc) ->
get_line(Prompt, Port, Q, Acc, Enc);
{io_request,From,ReplyAs,Request} when is_pid(From) ->
do_io_request(Request, From, ReplyAs, Port, queue:new()),
- prompt(Port, Prompt),
- get_line(Prompt, Port, Q, Acc, Enc);
+ case prompt(Port, Prompt) of
+ error ->
+ {error,{error,get_line},Q};
+ ok ->
+ get_line(Prompt, Port, Q, Acc, Enc)
+ end;
{'EXIT',From,What} when node(From) =:= node() ->
{exit,What}
end;
@@ -437,6 +443,7 @@ srch(<<X:8,_/binary>>,X,N) ->
{match,[{N,1}]};
srch(<<_:8,T/binary>>,X,N) ->
srch(T,X,N+1).
+
get_line_doit(Prompt, Port, Q, Accu, Enc) ->
case queue:is_empty(Q) of
true ->
@@ -575,31 +582,36 @@ 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) ->
- 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, Enc) ->
+ case prompt(Port, Prompt) of
+ error ->
+ {error,{error,get_chars},Q};
+ ok ->
+ case {get(eof),queue:is_empty(Q)} of
+ {true,true} ->
+ {ok,eof,Q};
+ _ ->
+ get_chars(Prompt, M, F, Xa, Port, Q, start, Enc)
+ end
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 +622,45 @@ 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).
+ case prompt(Port, Prompt) of
+ error ->
+ {error,{error,get_chars},Q};
+ ok ->
+ get_chars(Prompt, M, F, XtraArg, Port, Q, State, Enc)
+ end.
%% 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 +669,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.
@@ -687,13 +703,15 @@ get_chars_more(State, M, F, Xa, Port, Q, Fmt) ->
%% common case, reduces execution time by 20%
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),
+ PromptString = io_lib:format_prompt(Prompt, Encoding),
+ case wrap_characters_to_binary(PromptString, unicode, Encoding) of
+ Bin when is_binary(Bin) ->
+ put_port(Bin, Port);
+ error ->
+ error
+ end.
%% Convert error code to make it look as before
err_func(io_lib, get_until, {_,F,_}) ->
@@ -710,72 +728,90 @@ 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(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(L, list, unicode, UniTerm) ->
- unicode:characters_to_list(erlang:iolist_to_binary(L),
- case UniTerm of
- true -> unicode;
- _ -> latin1
- end);
-cast(Other, _, _,_) ->
- Other.
-
-wrap_characters_to_binary(Chars,unicode,latin1) ->
- case unicode:characters_to_binary(Chars,unicode,latin1) of
- {error,_,_} ->
- list_to_binary(
- [ case X of
- High when High > 255 ->
- ["\\x{",erlang:integer_to_list(X, 16),$}];
- Low ->
- Low
- end || X <- unicode:characters_to_list(Chars,unicode) ]);
- Bin ->
- Bin
+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;
-
-wrap_characters_to_binary(Bin,From,From) when is_binary(Bin) ->
+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 catch unicode:characters_to_binary(Chars, unicode, latin1) of
+ Bin when is_binary(Bin) ->
+ Bin;
+ _ ->
+ case catch unicode:characters_to_list(Chars, unicode) of
+ L when is_list(L) ->
+ list_to_binary(
+ [ case X of
+ High when High > 255 ->
+ ["\\x{",erlang:integer_to_list(X, 16),$}];
+ Low ->
+ Low
+ end || X <- L ]);
+ _ ->
+ error
+ end
+ end;
+wrap_characters_to_binary(Bin, From, From) when is_binary(Bin) ->
Bin;
-wrap_characters_to_binary(Chars,From,To) ->
- unicode:characters_to_binary(Chars,From,To).
+wrap_characters_to_binary(Chars, From, To) ->
+ case catch unicode:characters_to_binary(Chars, From, To) of
+ Bin when is_binary(Bin) ->
+ Bin;
+ _ ->
+ error
+ end.
diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl
index 8f2ca28f56..bb654495d3 100644
--- a/lib/kernel/src/user_drv.erl
+++ b/lib/kernel/src/user_drv.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
@@ -129,7 +129,7 @@ server1(Iport, Oport, Shell) ->
Gr = gr_add_cur(Gr1, Curr, Shell1),
%% Print some information.
io_request({put_chars, unicode,
- flatten(io_lib:format("~s\n",
+ flatten(io_lib:format("~ts\n",
[erlang:system_info(system_version)]))},
Iport, Oport),
%% Enter the server loop.
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 8eca37029d..cb11d4e899 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -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..0452202467 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.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
@@ -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/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 827208b048..fc17db2745 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.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
@@ -25,6 +25,7 @@
replace_path/1, load_file/1, load_abs/1, ensure_loaded/1,
delete/1, purge/1, soft_purge/1, is_loaded/1, all_loaded/1,
load_binary/1, dir_req/1, object_code/1, set_path_file/1,
+ upgrade/1,
sticky_dir/1, pa_pz_option/1, add_del_path/1,
dir_disappeared/1, ext_mod_dep/1, clash/1,
load_cached/1, start_node_with_cache/1, add_and_rehash/1,
@@ -32,7 +33,7 @@
purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1,
code_archive/1, code_archive2/1, on_load/1, on_load_binary/1,
on_load_embedded/1, on_load_errors/1, big_boot_embedded/1,
- native_early_modules/1]).
+ native_early_modules/1, get_mode/1]).
-export([init_per_testcase/2, end_per_testcase/2,
init_per_suite/1, end_per_suite/1,
@@ -43,6 +44,8 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
+-export([compile_load/4]).
+
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
@@ -50,13 +53,14 @@ all() ->
replace_path, load_file, load_abs, ensure_loaded,
delete, purge, soft_purge, is_loaded, all_loaded,
load_binary, dir_req, object_code, set_path_file,
+ upgrade,
pa_pz_option, add_del_path, dir_disappeared,
ext_mod_dep, clash, load_cached, start_node_with_cache,
add_and_rehash, where_is_file_no_cache,
where_is_file_cached, purge_stacktrace, mult_lib_roots,
bad_erl_libs, code_archive, code_archive2, on_load,
on_load_binary, on_load_embedded, on_load_errors,
- big_boot_embedded, native_early_modules].
+ big_boot_embedded, native_early_modules, get_mode].
groups() ->
[].
@@ -450,6 +454,46 @@ load_binary(Config) when is_list(Config) ->
code:delete(code_b_test),
ok.
+upgrade(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ %%T = [beam, hipe],
+ T = [beam],
+
+ [upgrade_do(DataDir, Client, U1, U2, O1, O2)
+ || Client<-T, U1<-T, U2<-T, O1<-T, O2<-T],
+
+ ok.
+
+upgrade_do(DataDir, Client, U1, U2, O1, O2) ->
+ compile_load(upgrade_client, DataDir, undefined, Client),
+ upgrade_client:run(DataDir, U1, U2, O1, O2),
+ ok.
+
+compile_load(Mod, Dir, Ver, CodeType) ->
+ Version = case Ver of
+ undefined ->
+ io:format("Compiling '~p' as ~p\n", [Mod, CodeType]),
+ [];
+ _ ->
+ io:format("Compiling version ~p of '~p' as ~p\n",
+ [Ver, Mod, CodeType]),
+ [{d,list_to_atom("VERSION_" ++ integer_to_list(Ver))}]
+ end,
+ Target = case CodeType of
+ beam -> [];
+ hipe -> [native]
+ end,
+ CompOpts = [binary, report] ++ Target ++ Version,
+
+ Src = filename:join(Dir, atom_to_list(Mod) ++ ".erl"),
+ %io:format("compile:file(~p,~p)\n", [Src, CompOpts]),
+ {ok,Mod,Code} = compile:file(Src, CompOpts),
+ ObjFile = filename:basename(Src,".erl") ++ ".beam",
+ {module,Mod} = code:load_binary(Mod, ObjFile, Code),
+ %IsNative = code:is_module_native(Mod),
+ ok.
+
dir_req(suite) -> [];
dir_req(doc) -> [];
dir_req(Config) when is_list(Config) ->
@@ -535,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) ->
[];
@@ -645,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),
@@ -677,7 +716,7 @@ analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) ->
%% These modules should be loaded by code.erl before
%% the code_server is started.
OK = [erlang, os, prim_file, erl_prim_loader, init, ets,
- code_server, lists, lists_sort, unicode, binary, filename, packages,
+ code_server, lists, lists_sort, unicode, binary, filename,
gb_sets, gb_trees, hipe_unified_loader, hipe_bifs,
prim_zip, zlib],
ErrCnt1 =
@@ -783,6 +822,10 @@ check_funs({'$M_EXPR','$F_EXPR',2},
check_funs({'$M_EXPR','$F_EXPR',1},
[{lists,foreach,2},
{hipe_unified_loader,patch_consts,3} | _]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',1},
+ [{lists,foreach,2},
+ {hipe_unified_loader,mark_referred_from,1},
+ {hipe_unified_loader,get_refs_from,2}| _]) -> 0;
check_funs({'$M_EXPR',warning_msg,2},
[{code_server,finish_on_load_report,2} | _]) -> 0;
%% This is cheating! /raimo
@@ -1551,10 +1594,15 @@ native_early_modules_1(Architecture) ->
?line true = lists:all(fun code:is_module_native/1,
[ets,file,filename,gb_sets,gb_trees,
%%hipe_unified_loader, no_native as workaround
- lists,os,packages]),
+ lists,os]),
ok
end.
+get_mode(suite) -> [];
+get_mode(doc) -> ["Test that the mode of the code server is properly retrieved"];
+get_mode(Config) when is_list(Config) ->
+ interactive = code:get_mode().
+
%%-----------------------------------------------------------------
%% error_logger handler.
%% (Copied from stdlib/test/proc_lib_SUITE.erl.)
diff --git a/lib/kernel/test/code_SUITE_data/other.erl b/lib/kernel/test/code_SUITE_data/other.erl
new file mode 100644
index 0000000000..58ce87f222
--- /dev/null
+++ b/lib/kernel/test/code_SUITE_data/other.erl
@@ -0,0 +1,38 @@
+-module(other).
+
+-ifdef(VERSION_1).
+-define(VERSION,1).
+-export([exp1/0]).
+-export([exp1loc2/0]).
+-export([exp1exp2/0]).
+exp1() -> check([loc1(),exp1loc2(),exp1exp2(),loc1exp2(),loc1loc2()]).
+loc1() -> check([exp1loc2(),exp1exp2(),loc1exp2(),loc1loc2()]).
+exp1loc2() -> check([exp1exp2(),loc1exp2(),loc1loc2()]).
+exp1exp2() -> check([loc1exp2(),loc1loc2()]).
+loc1exp2() -> check([loc1loc2()]).
+-endif. % VERSION_1
+
+-ifdef(VERSION_2).
+-define(VERSION,2).
+-export([exp2/0]).
+-export([loc1exp2/0]).
+-export([exp1exp2/0]).
+loc1exp2() -> check([exp1exp2(),exp1loc2(),loc2(),exp2(),loc1loc2()]).
+exp1exp2() -> check([exp1loc2(),loc2(),exp2(),loc1loc2()]).
+exp1loc2() -> check([loc2(),exp2(),loc1loc2()]).
+loc2() -> check([exp2(),loc1loc2()]).
+exp2() -> check([loc1loc2()]).
+
+-endif. % VERSION_2
+
+loc1loc2() -> ?VERSION.
+
+
+check(VerList) ->
+ case lists:all(fun(?VERSION) -> true; (_) -> false end,
+ VerList) of
+ true ->
+ ?VERSION;
+ false ->
+ VerList
+ end.
diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl
new file mode 100644
index 0000000000..bb655e01d3
--- /dev/null
+++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl
@@ -0,0 +1,259 @@
+-module(upgrade_client).
+
+-export([run/5]).
+
+%%-define(line, io:format("~s:~p\n", [?MODULE,?LINE]),).
+-define(line,).
+
+run(Dir, Upgradee1, Upgradee2, Other1, Other2) ->
+ %% Load version 1 of upgradee
+ code_SUITE:compile_load(upgradee, Dir, 1, Upgradee1),
+
+ ?line 1 = upgradee:exp1(),
+ ?line 1 = upgradee:exp1exp2(),
+ ?line 1 = upgradee:exp1loc2(),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+
+ P = spawn_link(upgradee,dispatch_loop,[]),
+
+ ?line 1 = proxy_call(P, local, exp1),
+ ?line 1 = proxy_call(P, local, loc1),
+ ?line 1 = proxy_call(P, local, exp1exp2),
+ ?line 1 = proxy_call(P, local, exp1loc2),
+ ?line 1 = proxy_call(P, local, loc1exp2),
+ ?line 1 = proxy_call(P, local, loc1loc2),
+ ?line 1 = proxy_call(P, external, exp1),
+ ?line 1 = proxy_call(P, external, exp1exp2),
+ ?line 1 = proxy_call(P, external, exp1loc2),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+ ?line {cannot_compile,1} = proxy_call(P, local, exp2),
+ ?line {cannot_compile,1} = proxy_call(P, local, loc2),
+
+ ?line {'EXIT',{undef,_}} = (catch other:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc11exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ %%
+ %% Load version 1 of other
+ %%
+ code_SUITE:compile_load(other, Dir, 1, Other1),
+ ?line 1 = other:exp1(),
+ ?line 1 = other:exp1loc2(),
+ ?line 1 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 1 = proxy_call(P, other, exp1),
+ ?line 1 = proxy_call(P, other, exp1loc2),
+ ?line 1 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ %%
+ %% Load version 2 of upgradee
+ %%
+ code_SUITE:compile_load(upgradee, Dir, 2, Upgradee2),
+
+ ?line 2 = upgradee:exp2(),
+ ?line 2 = upgradee:exp1exp2(),
+ ?line 2 = upgradee:loc1exp2(),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+
+ ?line 1 = proxy_call(P, local, exp1),
+ ?line 1 = proxy_call(P, local, loc1),
+ ?line 1 = proxy_call(P, local, exp1exp2),
+ ?line 1 = proxy_call(P, local, exp1loc2),
+ ?line 1 = proxy_call(P, local, loc1exp2),
+ ?line 1 = proxy_call(P, local, loc1loc2),
+ ?line {cannot_compile,1} = proxy_call(P, local, exp2),
+ ?line {cannot_compile,1} = proxy_call(P, local, loc2),
+
+ ?line 2 = proxy_call(P, external, exp1exp2),
+ ?line 2 = proxy_call(P, external, loc1exp2),
+ ?line 2 = proxy_call(P, external, exp2),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+
+ ?line 1 = other:exp1(),
+ ?line 1 = other:exp1loc2(),
+ ?line 1 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 1 = proxy_call(P, other, exp1),
+ ?line 1 = proxy_call(P, other, exp1loc2),
+ ?line 1 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ %%
+ %% Load version 2 of other
+ %%
+ code_SUITE:compile_load(other, Dir, 2, Other2),
+
+ ?line 2 = upgradee:exp2(),
+ ?line 2 = upgradee:exp1exp2(),
+ ?line 2 = upgradee:loc1exp2(),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+
+ ?line 1 = proxy_call(P, local, exp1),
+ ?line 1 = proxy_call(P, local, loc1),
+ ?line 1 = proxy_call(P, local, exp1exp2),
+ ?line 1 = proxy_call(P, local, exp1loc2),
+ ?line 1 = proxy_call(P, local, loc1exp2),
+ ?line 1 = proxy_call(P, local, loc1loc2),
+ ?line {cannot_compile,1} = proxy_call(P, local, exp2),
+ ?line {cannot_compile,1} = proxy_call(P, local, loc2),
+
+ ?line 2 = proxy_call(P, external, exp1exp2),
+ ?line 2 = proxy_call(P, external, loc1exp2),
+ ?line 2 = proxy_call(P, external, exp2),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+
+ ?line 2 = other:exp2(),
+ ?line 2 = other:loc1exp2(),
+ ?line 2 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 2 = proxy_call(P, other, exp2),
+ ?line 2 = proxy_call(P, other, loc1exp2),
+ ?line 2 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+
+ %%
+ %% Upgrade proxy to version 2
+ %%
+ P ! upgrade_order,
+
+
+ %%
+ io:format("Delete version 2 of 'upgradee'\n",[]),
+ %%
+ code:purge(upgradee),
+ code:delete(upgradee),
+
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1exp2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
+
+ ?line 2 = proxy_call(P, local, exp2),
+ ?line 2 = proxy_call(P, local, loc2),
+ ?line 2 = proxy_call(P, local, exp1exp2),
+ ?line 2 = proxy_call(P, local, exp1loc2),
+ ?line 2 = proxy_call(P, local, loc1exp2),
+ ?line 2 = proxy_call(P, local, loc1loc2),
+ ?line {cannot_compile,2} = proxy_call(P, local, exp1),
+ ?line {cannot_compile,2} = proxy_call(P, local, loc1),
+
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
+
+ ?line 2 = other:exp2(),
+ ?line 2 = other:loc1exp2(),
+ ?line 2 = other:exp1exp2(),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1()),
+ ?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
+ ?line {'EXIT',{undef,_}} = (catch other:loc2()),
+
+ ?line 2 = proxy_call(P, other, exp2),
+ ?line 2 = proxy_call(P, other, loc1exp2),
+ ?line 2 = proxy_call(P, other, exp1exp2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
+ ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
+
+ unlink(P),
+ exit(P, die_please),
+
+ io:format("Purge 'upgradee'\n",[]),
+ code:purge(upgradee),
+
+ io:format("Delete and purge 'other'\n",[]),
+ code:purge(other),
+ code:delete(other),
+ code:purge(other),
+ ok.
+
+proxy_call(Pid, CallType, Func) ->
+ Pid ! {self(), CallType, Func},
+ receive
+ {Pid, call_result, Func, Ret} -> Ret
+ end.
diff --git a/lib/kernel/test/code_SUITE_data/upgradee.erl b/lib/kernel/test/code_SUITE_data/upgradee.erl
new file mode 100644
index 0000000000..62b1d95e30
--- /dev/null
+++ b/lib/kernel/test/code_SUITE_data/upgradee.erl
@@ -0,0 +1,123 @@
+-module(upgradee).
+
+-export([dispatch_loop/0]).
+
+-ifdef(VERSION_1).
+-define(VERSION,1).
+
+-export([exp1/0]). % only exported in v1
+-export([exp1loc2/0]). % exported in v1, local in v2
+-export([exp1exp2/0]). % exported in v1 and v2
+
+exp1() -> ?VERSION.
+loc1() -> ?VERSION.
+
+-endif. % VERSION_1
+
+-ifdef(VERSION_2).
+-define(VERSION,2).
+
+-export([exp2/0]).
+-export([loc1exp2/0]).
+-export([exp1exp2/0]).
+
+exp2() -> ?VERSION.
+loc2() -> ?VERSION.
+
+-endif. % VERSION_2
+
+exp1exp2() -> ?VERSION.
+exp1loc2() -> ?VERSION.
+loc1exp2() -> ?VERSION.
+loc1loc2() -> ?VERSION.
+
+dispatch_loop() ->
+ receive
+ upgrade_order ->
+ %%erlang:display({"upgradee version", ?VERSION, "got upgrade_order"}),
+ ?MODULE:dispatch_loop();
+
+ Msg ->
+ %%erlang:display({"upgradee version", ?VERSION, "got msg", Msg}),
+ {Func,Ret} = case Msg of
+ %% Local calls
+ {Pid, local, F=exp1} ->
+ {F, local_exp1()};
+ {Pid, local, F=loc1} ->
+ {F, local_loc1()};
+ {Pid, local, F=exp1exp2} ->
+ {F, catch exp1exp2()};
+ {Pid, local, F=exp1loc2} ->
+ {F, catch exp1loc2()};
+ {Pid, local, F=loc1exp2} ->
+ {F, catch loc1exp2()};
+ {Pid, local, F=loc1loc2} ->
+ {F, catch loc1loc2()};
+ {Pid, local, F=exp2} ->
+ {F, local_exp2()};
+ {Pid, local, F=loc2} ->
+ {F, local_loc2()};
+
+ %% Extern calls to own module
+ {Pid, external, F=exp1} ->
+ {F, catch ?MODULE:exp1()};
+ {Pid, external, F=loc1} ->
+ {F, catch ?MODULE:loc1()};
+ {Pid, external, F=exp1exp2} ->
+ {F, catch ?MODULE:exp1exp2()};
+ {Pid, external, F=exp1loc2} ->
+ {F, catch ?MODULE:exp1loc2()};
+ {Pid, external, F=loc1exp2} ->
+ {F, catch ?MODULE:loc1exp2()};
+ {Pid, external, F=loc1loc2} ->
+ {F, catch ?MODULE:loc1loc2()};
+ {Pid, external, F=exp2} ->
+ {F, catch ?MODULE:exp2()};
+ {Pid, external, F=loc2} ->
+ {F, catch ?MODULE:loc2()};
+
+ %% External calls to other module
+ {Pid, other, F=exp1} ->
+ {F, catch other:exp1()};
+ {Pid, other, F=loc1} ->
+ {F, catch other:loc1()};
+ {Pid, other, F=exp1exp2} ->
+ {F, catch other:exp1exp2()};
+ {Pid, other, F=exp1loc2} ->
+ {F, catch other:exp1loc2()};
+ {Pid, other, F=loc1exp2} ->
+ {F, catch other:loc1exp2()};
+ {Pid, other, F=loc1loc2} ->
+ {F, catch other:loc1loc2()};
+ {Pid, other, F=exp2} ->
+ {F, catch other:exp2()};
+ {Pid, other, F=loc2} ->
+ {F, catch other:loc2()}
+ end,
+ Pid ! {self(), call_result, Func, Ret},
+
+ dispatch_loop() % A local call, we don't want to upgrade the dispatcher
+ end.
+
+
+
+-ifdef(VERSION_1).
+local_exp1() -> catch exp1().
+local_loc1() -> catch loc1().
+-else.
+local_exp1() ->
+ %%erlang:display({"upgradee:local_exp1 in version", ?VERSION}),
+ {cannot_compile,?VERSION}.
+local_loc1() -> {cannot_compile,?VERSION}.
+-endif.
+
+-ifdef(VERSION_2).
+local_exp2() -> catch exp2().
+local_loc2() -> catch loc2().
+-else.
+local_exp2() ->
+ %%erlang:display({"upgradee:local_exp2 in version", ?VERSION}),
+ {cannot_compile,?VERSION}.
+local_loc2() ->
+ {cannot_compile,?VERSION}.
+-endif.
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 72239641e9..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"};
_ ->
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 848db06e82..c604e7073f 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.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
@@ -45,6 +45,8 @@
init_per_testcase/2, end_per_testcase/2,
read_write_file/1, names/1]).
-export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1,
+ list_dir/1,list_dir_error/1,
+ untranslatable_names/1, untranslatable_names_error/1,
pos1/1, pos2/1]).
-export([close/1, consult1/1, path_consult/1, delete/1]).
-export([ eval1/1, path_eval/1, script1/1, path_script/1,
@@ -55,12 +57,13 @@
-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
read_write/1, pread_write/1, append/1, exclusive/1]).
-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
--export([otp_5814/1]).
+-export([otp_5814/1, otp_10852/1]).
-export([ read_not_really_compressed/1,
read_compressed_cooked/1, read_compressed_cooked_binary/1,
read_cooked_tar_problem/1,
- write_compressed/1, compress_errors/1, catenated_gzips/1]).
+ 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 +87,8 @@
-export([advise/1]).
+-export([allocate/1]).
+
-export([standard_io/1,mini_server/1]).
%% Debug exports
@@ -107,16 +112,18 @@ all() ->
{group, files}, delete, rename, names, {group, errors},
{group, compression}, {group, links}, copy,
delayed_write, read_ahead, segment_read, segment_write,
- ipread, pid2name, interleaved_read_write, otp_5814,
+ ipread, pid2name, interleaved_read_write, otp_5814, otp_10852,
large_file, large_write, read_line_1, read_line_2, read_line_3,
read_line_4, standard_io].
groups() ->
- [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1]},
+ [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1,
+ list_dir, list_dir_error, untranslatable_names,
+ untranslatable_names_error]},
{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 +140,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]}].
@@ -228,7 +236,7 @@ mini_server(Parent) ->
receive
die ->
ok;
- {io_request,From,To,{put_chars,Data}} ->
+ {io_request,From,To,{put_chars,_Encoding,Data}} ->
Parent ! {io_request,From,To,{put_chars,Data}},
From ! {io_reply, To, ok},
mini_server(Parent);
@@ -492,8 +500,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,
@@ -517,6 +523,148 @@ win_cur_dir_1(_Config) ->
ok.
+
+%%%
+%%% Test list_dir() on a non-existing pathname.
+%%%
+
+list_dir_error(Config) ->
+ Priv = ?config(priv_dir, Config),
+ NonExisting = filename:join(Priv, "non-existing-dir"),
+ {error,enoent} = ?FILE_MODULE:list_dir(NonExisting),
+ ok.
+
+%%%
+%%% Test list_dir() and list_dir_all().
+%%%
+
+list_dir(Config) ->
+ RootDir = ?config(priv_dir, Config),
+ TestDir = filename:join(RootDir, ?MODULE_STRING++"_list_dir"),
+ ?FILE_MODULE:make_dir(TestDir),
+ list_dir_1(TestDir, 42, []).
+
+list_dir_1(TestDir, 0, Sorted) ->
+ [ok = ?FILE_MODULE:delete(filename:join(TestDir, F)) ||
+ F <- Sorted],
+ ok = ?FILE_MODULE:del_dir(TestDir);
+list_dir_1(TestDir, Cnt, Sorted0) ->
+ Base = "file" ++ integer_to_list(Cnt),
+ Name = filename:join(TestDir, Base),
+ ok = ?FILE_MODULE:write_file(Name, Base),
+ Sorted = lists:merge([Base], Sorted0),
+ {ok,DirList0} = ?FILE_MODULE:list_dir(TestDir),
+ {ok,DirList1} = ?FILE_MODULE:list_dir_all(TestDir),
+ Sorted = lists:sort(DirList0),
+ Sorted = lists:sort(DirList1),
+ list_dir_1(TestDir, Cnt-1, Sorted).
+
+untranslatable_names(Config) ->
+ case no_untranslatable_names() of
+ true ->
+ {skip,"Not a problem on this OS"};
+ false ->
+ untranslatable_names_1(Config)
+ end.
+
+untranslatable_names_1(Config) ->
+ {ok,OldCwd} = file:get_cwd(),
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, "untranslatable_names"),
+ ok = file:make_dir(Dir),
+ Node = start_node(untranslatable_names, "+fnu"),
+ try
+ ok = file:set_cwd(Dir),
+ [ok = file:write_file(F, F) || {_,F} <- untranslatable_names()],
+
+ ExpectedListDir0 = [unicode:characters_to_list(N, utf8) ||
+ {utf8,N} <- untranslatable_names()],
+ ExpectedListDir = lists:sort(ExpectedListDir0),
+ io:format("ExpectedListDir: ~p\n", [ExpectedListDir]),
+ ExpectedListDir = call_and_sort(Node, file, list_dir, [Dir]),
+
+ ExpectedListDirAll0 = [case Enc of
+ utf8 ->
+ unicode:characters_to_list(N, utf8);
+ latin1 ->
+ N
+ end || {Enc,N} <- untranslatable_names()],
+ ExpectedListDirAll = lists:sort(ExpectedListDirAll0),
+ io:format("ExpectedListDirAll: ~p\n", [ExpectedListDirAll]),
+ ExpectedListDirAll = call_and_sort(Node, file, list_dir_all, [Dir])
+ after
+ catch test_server:stop_node(Node),
+ file:set_cwd(OldCwd),
+ [file:delete(F) || {_,F} <- untranslatable_names()],
+ file:del_dir(Dir)
+ end,
+ ok.
+
+untranslatable_names_error(Config) ->
+ case no_untranslatable_names() of
+ true ->
+ {skip,"Not a problem on this OS"};
+ false ->
+ untranslatable_names_error_1(Config)
+ end.
+
+untranslatable_names_error_1(Config) ->
+ {ok,OldCwd} = file:get_cwd(),
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, "untranslatable_names_error"),
+ ok = file:make_dir(Dir),
+ Node = start_node(untranslatable_names, "+fnue"),
+ try
+ ok = file:set_cwd(Dir),
+ [ok = file:write_file(F, F) || {_,F} <- untranslatable_names()],
+
+ ExpectedListDir0 = [unicode:characters_to_list(N, utf8) ||
+ {utf8,N} <- untranslatable_names()],
+ ExpectedListDir = lists:sort(ExpectedListDir0),
+ io:format("ExpectedListDir: ~p\n", [ExpectedListDir]),
+ {error,{no_translation,BadFile}} =
+ rpc:call(Node, file, list_dir, [Dir]),
+ true = lists:keymember(BadFile, 2, untranslatable_names())
+
+ after
+ catch test_server:stop_node(Node),
+ file:set_cwd(OldCwd),
+ [file:delete(F) || {_,F} <- untranslatable_names()],
+ file:del_dir(Dir)
+ end,
+ ok.
+
+untranslatable_names() ->
+ [{utf8,<<"abc">>},
+ {utf8,<<"def">>},
+ {utf8,<<"Lagerl",195,182,"f">>},
+ {utf8,<<195,150,"stra Emterwik">>},
+ {latin1,<<"M",229,"rbacka">>},
+ {latin1,<<"V",228,"rmland">>}].
+
+call_and_sort(Node, M, F, A) ->
+ {ok,Res} = rpc:call(Node, M, F, A),
+ lists:sort(Res).
+
+no_untranslatable_names() ->
+ case os:type() of
+ {unix,darwin} -> true;
+ {win32,_} -> true;
+ _ -> false
+ end.
+
+start_node(Name, Args) ->
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+ ct:log("Trying to start ~w@~s~n", [Name,Host]),
+ case test_server:start_node(Name, peer, [{args,Args}]) of
+ {error,Reason} ->
+ test_server:fail(Reason);
+ {ok,Node} ->
+ ct:log("Node ~p started~n", [Node]),
+ Node
+ end.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1038,32 +1186,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 +1767,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 +1997,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 +2012,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 +2146,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 +2460,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) ->
@@ -2359,6 +2599,7 @@ 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)),
+ {error, _} = ?FILE_MODULE:read_link_all(lists:duplicate(10000,$a)),
?line RootDir = ?config(priv_dir, Config),
?line NewDir = filename:join(RootDir,
atom_to_list(?MODULE)
@@ -2382,6 +2623,7 @@ symlinks(Config) when is_list(Config) ->
?line {ok, Info2} = ?FILE_MODULE:read_link_info(Alias),
?line #file_info{links=1, type=symlink} = Info2,
?line {ok, Name} = ?FILE_MODULE:read_link(Alias),
+ {ok, Name} = ?FILE_MODULE:read_link_all(Alias),
ok
end,
@@ -2564,147 +2806,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.";
@@ -3300,6 +3518,49 @@ otp_5814(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+otp_10852(suite) ->
+ [];
+otp_10852(doc) ->
+ ["OTP-10852. +fnu and latin1 filenames"];
+otp_10852(Config) when is_list(Config) ->
+ Node = start_node(erl_pp_helper, "+fnu"),
+ Dir = ?config(priv_dir, Config),
+ B = filename:join(Dir, <<"\xE4">>),
+ ok = rpc_call(Node, get_cwd, [B]),
+ {error, no_translation} = rpc_call(Node, set_cwd, [B]),
+ ok = rpc_call(Node, delete, [B]),
+ ok = rpc_call(Node, rename, [B, B]),
+ ok = rpc_call(Node, read_file_info, [B]),
+ ok = rpc_call(Node, read_link_info, [B]),
+ ok = rpc_call(Node, read_link, [B]),
+ ok = rpc_call(Node, write_file_info, [B,#file_info{}]),
+ ok = rpc_call(Node, list_dir, [B]),
+ ok = rpc_call(Node, list_dir_all, [B]),
+ ok = rpc_call(Node, read_file, [B]),
+ ok = rpc_call(Node, make_link, [B,B]),
+ ok = rpc_call(Node, make_symlink, [B,B]),
+ ok = rpc_call(Node, delete, [B]),
+ ok = rpc_call(Node, make_dir, [B]),
+ ok = rpc_call(Node, del_dir, [B]),
+ ok = rpc_call(Node, write_file, [B,B]),
+ {ok, Fd} = rpc_call(Node, open, [B,[read]]),
+ ok = rpc_call(Node, close, [Fd]),
+ {ok,0} = rpc_call(Node, copy, [B,B]),
+ {ok, Fd2, B} = rpc_call(Node, path_open, [["."], B, [read]]),
+ ok = rpc_call(Node, close, [Fd2]),
+ true = test_server:stop_node(Node),
+ ok.
+
+rpc_call(N, F, As) ->
+ case rpc:call(N, ?FILE_MODULE, F, As) of
+ {error, enotsup} -> ok;
+ {error, enoent} -> ok;
+ {error, badarg} -> ok;
+ Else -> Else
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
large_file(suite) ->
[];
large_file(doc) ->
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
index 3aa010a708..a6728564e4 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-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
@@ -336,12 +337,12 @@ 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("."),
+ {ok, L0} = Mod:list_dir_all("."),
?line L1 = lists:sort(L0),
- io:format("~p ~p~n",[L1,list(icky_dir())]),
+ io:format("~p~n~p~n~n",[L1,lists:sort(list(icky_dir()))]),
?line L1 = lists:sort(convlist(list(icky_dir()))),
?line {ok,D2} = Mod:get_cwd(),
?line true = is_list(D2),
@@ -356,66 +357,59 @@ check_icky(Mod) ->
?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,icky_dir()))}
|| {T,S,Targ} <- icky_dir(), T =:= symlink ],
?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
- ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
- ?line chk_cre_dir(Mod,[{directory,"���_dir",icky_dir()}]),
+ [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) ||
+ {SymL,Targ,_} <- Syms ],
+ ?line chk_cre_dir(Mod,[{directory,"åäö_dir",icky_dir()}]),
?line {ok,BeginAt} = Mod:get_cwd(),
?line true = is_list(BeginAt),
- ?line {error,enoent} = Mod:set_cwd("��_dir"),
- ?line ok = Mod:set_cwd("���_dir"),
+ ?line {error,enoent} = Mod:set_cwd("åä_dir"),
+ ?line ok = Mod:set_cwd("åäö_dir"),
?line {ok, NowAt} = Mod:get_cwd(),
?line true = is_list(NowAt),
?line true = BeginAt =/= NowAt,
?line ok = Mod:set_cwd(".."),
?line {ok,BeginAt} = Mod:get_cwd(),
- ?line rm_r2(Mod,"���_dir"),
- {OS,TYPE} = os:type(),
+ ?line rm_r2(Mod,"åäö_dir"),
+ {OS,_} = os:type(),
% Check that treat_icky really converts to the same as the OS
case UniMode of
true ->
- ?line chk_cre_dir(Mod,[{directory,"���_dir",[]}]),
- ?line ok = Mod:set_cwd("���_dir"),
- ?line ok = Mod:write_file(<<"���">>,<<"hello">>),
- ?line Treated = treat_icky(<<"���">>),
- ?line {ok,[Treated]} = Mod:list_dir("."),
- ?line ok = Mod:delete(<<"���">>),
+ ?line chk_cre_dir(Mod,[{directory,"åäö_dir",[]}]),
+ ?line ok = Mod:set_cwd("åäö_dir"),
+ ?line ok = Mod:write_file(<<"ååå">>,<<"hello">>),
+ ?line Treated = treat_icky(<<"ååå">>),
+ {ok,[Treated]} = Mod:list_dir_all("."),
+ ?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, NowAt2} = Mod:get_cwd(),
- io:format("~p~n",[NowAt2]),
- % Cannot create raw unicode-breaking filenames on windows or macos
- ?line true = ((((not UniMode) or (OS =:= win32) or (TYPE=:=darwin)) and is_list(NowAt2)) orelse ((UniMode) and is_binary(NowAt2))),
- ?line true = BeginAt =/= NowAt2,
- ?line ok = Mod:set_cwd(".."),
?line {ok,BeginAt} = Mod:get_cwd(),
- ?line rm_r2(Mod,conv(treat_icky(<<"���_dir">>))),
case has_links() of
true ->
- ?line ok = Mod:make_link("fil1","nisse�"),
- ?line {ok, <<"fil1">>} = Mod:read_file("nisse�"),
- ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse�"),
- ?line ok = Mod:delete("nisse�"),
- ?line ok = Mod:make_link("fil1",treat_icky(<<"nisse�">>)),
- ?line {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisse�">>)),
- ?line {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisse�">>)),
- ?line ok = Mod:delete(treat_icky(<<"nisse�">>)),
+ ?line ok = 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,
@@ -430,42 +424,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),
@@ -475,7 +469,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 ->
@@ -484,7 +478,7 @@ check_very_icky(Mod) ->
ok
end,
?line make_very_icky_dir(Mod),
- ?line {ok, L0} = Mod:list_dir("."),
+ {ok, L0} = Mod:list_dir_all("."),
?line L1 = lists:sort(L0),
?line L1 = lists:sort(convlist(list(very_icky_dir()))),
?line {ok,D2} = Mod:get_cwd(),
@@ -493,11 +487,12 @@ check_very_icky(Mod) ->
?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,very_icky_dir()))}
|| {T,S,Targ} <- very_icky_dir(), T =:= symlink ],
?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
- ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
+ ?line [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) ||
+ {SymL,Targ,_} <- Syms ],
?line chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]),
?line {ok,BeginAt} = Mod:get_cwd(),
?line true = is_list(BeginAt),
- ?line {error,enoent} = Mod:set_cwd("��_dir"),
+ ?line {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),
@@ -514,16 +509,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,
@@ -540,10 +535,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]),
@@ -558,33 +553,6 @@ check_very_icky(Mod) ->
FI#file_info{mode = NewMode2}),
?line {ok,#file_info{mode = NewMode2}} =
Mod:read_file_info([956,965,963,954,959,49]),
- ?line NumOK0 = case has_links() of
- true -> 5;
- false -> 3
- end,
- ?line NumNOK0 = case has_links() of
- true -> 4;
- false -> 3
- end,
- ?line {NumOK,NumNOK} = case is_binary(treat_icky(<<"foo">>)) of
- false ->
- {NumOK0+NumNOK0,0};
- true ->
- {NumOK0,NumNOK0}
- end,
- ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}),
- ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false),
- ?line SF3 = unicode:characters_to_binary("���subfil3",
- file:native_name_encoding()),
- ?line SF2 = case treat_icky(<<"���subfil2">>) of
- LF2 when is_list(LF2) ->
- unicode:characters_to_binary(LF2,
- file:native_name_encoding());
- BF2 ->
- BF2
- end,
- ?line Sorted = lists:sort([SF3,SF2]),
- ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)),
ok
catch
throw:need_unicode_mode ->
@@ -603,7 +571,7 @@ check_very_icky(Mod) ->
rm_rf(Mod,Dir) ->
case Mod:read_link_info(Dir) of
{ok, #file_info{type = directory}} ->
- {ok, Content} = Mod:list_dir(Dir),
+ {ok, Content} = Mod:list_dir_all(Dir),
[ rm_rf(Mod,filename:join(Dir,C)) || C <- Content ],
Mod:del_dir(Dir),
ok;
@@ -618,7 +586,7 @@ rm_r(Mod,Dir) ->
case Mod:read_link_info(Dir) of
{ok, #file_info{type = directory}} ->
{ok,#file_info{type = directory}} = Mod:read_file_info(Dir),
- {ok, Content} = Mod:list_dir(Dir),
+ {ok, Content} = Mod:list_dir_all(Dir),
[ true = is_list(Part) || Part <- Content ],
[ true = is_list(filename:join(Dir,Part)) || Part <- Content ],
[ rm_r(Mod,filename:join(Dir,C)) || C <- Content ],
@@ -636,7 +604,7 @@ rm_r2(Mod,Dir) ->
case Mod:read_link_info(Dir) of
{ok, #file_info{type = directory}} ->
{ok,#file_info{type = directory}} = Mod:read_file_info(Dir),
- {ok, Content} = Mod:list_dir(Dir),
+ {ok, Content} = Mod:list_dir_all(Dir),
UniMode = file:native_name_encoding() =/= latin1,
[ true = (is_list(Part) orelse UniMode) || Part <- Content ],
[ true = (is_list(filename:join(Dir,Part)) orelse UniMode) || Part <- Content ],
@@ -744,26 +712,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"),
@@ -774,26 +742,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 bcc2f0b840..2a886b2efc 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -31,22 +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]).
+ 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].
+ open_multihoming_ipv4_and_ipv6_socket,
+ basic_stream, xfer_stream_min, peeloff_active_once,
+ peeloff_active_true, buffers].
groups() ->
[].
@@ -923,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),
@@ -983,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),
@@ -1302,8 +1315,15 @@ recv_comm_up_eventually(S) ->
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% 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} =
@@ -1312,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..9bc66dbae0 100644
--- a/lib/kernel/test/gen_tcp_echo_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_echo_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-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
@@ -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..6b672004ec 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -42,13 +42,22 @@
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(iter_max_socks, Config) when is_list(Config) ->
+ Dog = case os:type() of
+ {win32,_} ->
+ test_server:timetrap(test_server:minutes(30));
+ _Else ->
+ test_server:timetrap(test_server:seconds(240))
+ end,
+ [{watchdog, Dog}|Config];
init_per_testcase(_Func, Config) when is_list(Config) ->
Dog = test_server:timetrap(test_server:seconds(240)),
[{watchdog, Dog}|Config].
@@ -75,6 +84,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 +350,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 +417,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 +547,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 +591,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 = case os:type() of {win32,_} -> 10; _ -> 20 end,
+ %% 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 +2591,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 1cc3eb7c79..9428a38660 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -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]) ||
@@ -1840,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]),
@@ -1860,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),
@@ -3821,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 ->
@@ -4154,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.
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 4a8033e3a3..320b23bea1 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -83,10 +83,10 @@ 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) ->
@@ -188,8 +188,20 @@ reboot(Config) when is_list(Config) ->
%% 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,
@@ -228,8 +240,19 @@ node_start_immediately_after_crash(Config) when is_list(Config) ->
%% 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,
@@ -345,13 +368,8 @@ 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(Config,N) ->
@@ -408,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,
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..d4bb02df3f
--- /dev/null
+++ b/lib/kernel/test/ignore_cores.erl
@@ -0,0 +1,158 @@
+%%
+%% %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%
+%%
+
+%%%-------------------------------------------------------------------
+%%% 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..1f7724d0dc 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -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
@@ -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 0c63a6d653..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) ->
@@ -623,7 +774,6 @@ all_listen_options() ->
{exit_on_close, true, false, true, true},
%{high_watermark,4096,8192,true,true},
%{low_watermark,2048,4096,true,true},
- {bit8,on,off,true,true},
{send_timeout,infinity,1000,true,true},
{send_timeout_close,false,true,true,true},
{delay_send,false,true,true,true},
@@ -647,7 +797,6 @@ all_connect_options() ->
{exit_on_close, true, false, true, true},
{high_watermark,4096,8192,false,true},
{low_watermark,2048,4096,false,true},
- {bit8,on,off,true,true},
{send_timeout,infinity,1000,true,true},
{send_timeout_close,false,true,true,true},
{delay_send,false,true,true,true},
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 e0b90c5214..6fe97ed04f 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -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 7549e2c83e..36e13cec26 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -29,7 +29,7 @@
-export([toerl_server/3]).
init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(test_server:seconds(60)),
+ Dog = test_server:timetrap(test_server:minutes(3)),
[{watchdog,Dog}|Config].
end_per_testcase(_Func, Config) ->
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..382fd6f6a9 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -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
@@ -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..98cff0222e 100644
--- a/lib/kernel/test/pdict_SUITE.erl
+++ b/lib/kernel/test/pdict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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..ac75037536 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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,7 +49,9 @@
make_link_a/1, make_link_b/1,
read_link_info_for_non_link/1,
symlinks_a/1, symlinks_b/1,
- list_dir_limit/1]).
+ list_dir_limit/1,
+ list_dir_error/1,
+ list_dir/1]).
-export([advise/1]).
-export([large_write/1]).
@@ -57,6 +59,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").
@@ -79,7 +83,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[read_write_file, {group, dirs}, {group, files},
delete_a, delete_b, rename_a, rename_b, {group, errors},
- {group, compression}, {group, links}, list_dir_limit].
+ {group, compression}, {group, links}, list_dir_limit, list_dir].
groups() ->
[{dirs, [],
@@ -87,7 +91,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]},
@@ -108,7 +112,7 @@ groups() ->
write_compressed, compress_errors]},
{links, [],
[make_link_a, make_link_b, read_link_info_for_non_link,
- symlinks_a, symlinks_b]}].
+ symlinks_a, symlinks_b, list_dir_error]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -406,9 +410,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 +844,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 +1363,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 +1576,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 +1590,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 +1721,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 +1775,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),
@@ -1980,6 +2037,8 @@ symlinks(Config, Handle, Suffix) ->
?line #file_info{links=1, type=symlink} = Info2,
?line {ok, Name} =
?PRIM_FILE_call(read_link, Handle, [Alias]),
+ {ok, Name} =
+ ?PRIM_FILE_call(read_link_all, Handle, [Alias]),
ok
end,
@@ -2085,6 +2144,41 @@ list_dir_limit_cleanup(Dir, Handle, N, Cnt) ->
list_dir_limit_cleanup(Dir, Handle, N, Cnt+1).
%%%
+%%% Test list_dir() on a non-existing pathname.
+%%%
+
+list_dir_error(Config) ->
+ Priv = ?config(priv_dir, Config),
+ NonExisting = filename:join(Priv, "non-existing-dir"),
+ {error,enoent} = prim_file:list_dir(NonExisting),
+ ok.
+
+%%%
+%%% Test list_dir() and list_dir_all().
+%%%
+
+list_dir(Config) ->
+ RootDir = ?config(priv_dir, Config),
+ TestDir = filename:join(RootDir, ?MODULE_STRING++"_list_dir"),
+ ?PRIM_FILE:make_dir(TestDir),
+ list_dir_1(TestDir, 42, []).
+
+list_dir_1(TestDir, 0, Sorted) ->
+ [ok = ?PRIM_FILE:delete(filename:join(TestDir, F)) ||
+ F <- Sorted],
+ ok = ?PRIM_FILE:del_dir(TestDir);
+list_dir_1(TestDir, Cnt, Sorted0) ->
+ Base = "file" ++ integer_to_list(Cnt),
+ Name = filename:join(TestDir, Base),
+ ok = ?PRIM_FILE:write_file(Name, Base),
+ Sorted = lists:merge([Base], Sorted0),
+ {ok,DirList0} = ?PRIM_FILE:list_dir(TestDir),
+ {ok,DirList1} = ?PRIM_FILE:list_dir_all(TestDir),
+ Sorted = lists:sort(DirList0),
+ Sorted = lists:sort(DirList1),
+ list_dir_1(TestDir, Cnt-1, Sorted).
+
+%%%
%%% Support for testing large files.
%%%
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/vsn.mk b/lib/kernel/vsn.mk
index 7254d714eb..b6cf4407d2 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 2.15.3
+KERNEL_VSN = 2.16.1
diff --git a/lib/megaco/aclocal.m4 b/lib/megaco/aclocal.m4
new file mode 100644
index 0000000000..25f40944e7
--- /dev/null
+++ b/lib/megaco/aclocal.m4
@@ -0,0 +1,1905 @@
+dnl
+dnl %CopyrightBegin%
+dnl
+dnl Copyright Ericsson AB 1998-2013. 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..64daa959b5 100644
--- a/lib/megaco/configure.in
+++ b/lib/megaco/configure.in
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. -*-m4-*-
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 2001-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 2001-2013. 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
@@ -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/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..4b5adab262 100644
--- a/lib/megaco/doc/src/megaco_encode.xml
+++ b/lib/megaco/doc/src/megaco_encode.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 928e3bea82..bb30ce4c2a 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2012</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,7 +36,24 @@
section is the version number of Megaco.</p>
- <section><title>Megaco 3.16.0.2</title>
+ <section><title>Megaco 3.16.0.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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>
diff --git a/lib/megaco/examples/meas/Makefile.in b/lib/megaco/examples/meas/Makefile.in
index c517fd21cf..91e342cd9a 100644
--- a/lib/megaco/examples/meas/Makefile.in
+++ b/lib/megaco/examples/meas/Makefile.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..5af40eea7d 100644
--- a/lib/megaco/examples/meas/megaco_codec_meas.erl
+++ b/lib/megaco/examples/meas/megaco_codec_meas.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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..7825e7856f 100644
--- a/lib/megaco/examples/meas/megaco_codec_transform.erl
+++ b/lib/megaco/examples/meas/megaco_codec_transform.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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/src/app/Makefile b/lib/megaco/src/app/Makefile
index 42030c5b1c..d18da5326a 100644
--- a/lib/megaco/src/app/Makefile
+++ b/lib/megaco/src/app/Makefile
@@ -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..40265166ae 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-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
@@ -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/binary/Makefile b/lib/megaco/src/binary/Makefile
index 695599b9dc..c1fd66b848 100644
--- a/lib/megaco/src/binary/Makefile
+++ b/lib/megaco/src/binary/Makefile
@@ -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..b9b86d3183 100644
--- a/lib/megaco/src/binary/depend.mk
+++ b/lib/megaco/src/binary/depend.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+# Copyright Ericsson AB 2001-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,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..abb496c173 100644
--- a/lib/megaco/src/binary/megaco_binary_encoder.erl
+++ b/lib/megaco/src/binary/megaco_binary_encoder.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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..8a4f4e7509 100644
--- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
+++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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..d0765b7a6b 100644
--- a/lib/megaco/src/binary/modules.mk
+++ b/lib/megaco/src/binary/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-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,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/flex/Makefile.in b/lib/megaco/src/flex/Makefile.in
index 69c2425d05..6111cf2304 100644
--- a/lib/megaco/src/flex/Makefile.in
+++ b/lib/megaco/src/flex/Makefile.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..3914a81b8c 100644
--- a/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src
+++ b/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -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..d9473af26e 100644
--- a/lib/megaco/src/rules.mk
+++ b/lib/megaco/src/rules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. 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
@@ -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/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..cdf11f09cc 100644
--- a/lib/megaco/test/megaco_actions_test.erl
+++ b/lib/megaco/test/megaco_actions_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-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
@@ -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..8ad1cf6445 100644
--- a/lib/megaco/test/megaco_call_flow_test.erl
+++ b/lib/megaco/test/megaco_call_flow_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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..96d7248726 100644
--- a/lib/megaco/test/megaco_mib_test.erl
+++ b/lib/megaco/test/megaco_mib_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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..cc1d2dd1b9 100644
--- a/lib/megaco/test/megaco_test_mg.erl
+++ b/lib/megaco/test/megaco_test_mg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. 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
@@ -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..d9183f847e 100644
--- a/lib/megaco/test/megaco_test_mgc.erl
+++ b/lib/megaco/test/megaco_test_mgc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. 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
@@ -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 5e72ade769..b61cf2102c 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.16.0.2
+MEGACO_VSN = 3.16.0.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 04b8c106fd..5942a40a87 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>2012</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,63 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.7.1</title>
+ <section><title>Mnesia 4.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Use chained send_after instead of send_interval, to make
+ decrease the number of messages sent after a sleep
+ (Thanks to James Wheare)</p>
+ <p>
+ Own Id: OTP-10636</p>
+ </item>
+ <item>
+ <p>
+ Fix format of mnesia overload message (Thanks to Ahmed
+ Omar)</p>
+ <p>
+ Own Id: OTP-10639</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added a general framework for executing benchmarks of
+ Erlang/OTP. Benchmarks for the Erlang VM and mnesia have
+ been incorporated in the framework. </p>
+ <p>
+ For details about how to add more benchmarks see
+ $ERL_TOP/HOWTO/BENCHMARKS.md in the source distribution.</p>
+ <p>
+ Own Id: OTP-10156</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Remove support for the query keyword and query
+ expressions. Thanks to Lo�c Hoguin.</p>
+ <p>
+ Own Id: OTP-10729</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.7.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/examples/mnesia_tpcb.erl b/lib/mnesia/examples/mnesia_tpcb.erl
index 903c53a21c..07ae73f0bd 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-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
@@ -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/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 6f289433ff..ac38fa05ef 100644
--- a/lib/mnesia/src/Makefile
+++ b/lib/mnesia/src/Makefile
@@ -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
@@ -93,10 +93,9 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# FLAGS
# ----------------------------------------------------
ERL_COMPILE_FLAGS += \
- +warn_unused_vars \
+ -Werror \
+'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,vsn,"mnesia_$(MNESIA_VSN)"}' \
- -W
+ +'{attribute,insert,vsn,"mnesia_$(MNESIA_VSN)"}'
# ----------------------------------------------------
# Targets
@@ -120,10 +119,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.erl b/lib/mnesia/src/mnesia.erl
index 3d30debc53..70466d10d7 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.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
@@ -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..78f7bfa325 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.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
@@ -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..35fe2d4035 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -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
@@ -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_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..7a788238fc 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.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
@@ -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..7aa03bda37 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -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
@@ -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..8443fefe7f 100644
--- a/lib/mnesia/src/mnesia_sup.erl
+++ b/lib/mnesia/src/mnesia_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. 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
@@ -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..e54e5c4e88 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.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
@@ -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 509dddc85d..45ce5b1983 100644
--- a/lib/mnesia/test/Makefile
+++ b/lib/mnesia/test/Makefile
@@ -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..e0004ecb51 100644
--- a/lib/mnesia/test/mnesia_SUITE.erl
+++ b/lib/mnesia/test/mnesia_SUITE.erl
@@ -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
@@ -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..f0f3053ebf 100644
--- a/lib/mnesia/test/mnesia_config_test.erl
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. 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
@@ -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_measure_test.erl b/lib/mnesia/test/mnesia_measure_test.erl
index e63689d83a..8854854570 100644
--- a/lib/mnesia/test/mnesia_measure_test.erl
+++ b/lib/mnesia/test/mnesia_measure_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. 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
@@ -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..6a51aefdf0 100644
--- a/lib/mnesia/test/mnesia_test_lib.erl
+++ b/lib/mnesia/test/mnesia_test_lib.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
@@ -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/vsn.mk b/lib/mnesia/vsn.mk
index 6d5df448c9..8cd97342af 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.7.1
+MNESIA_VSN = 4.8
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 4ec03782a7..ddf3b3fe37 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,23 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 1.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index 7135a6abd5..42f5c19935 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2012. All Rights Reserved.
+# Copyright Ericsson AB 2002-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
@@ -102,7 +102,8 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ERL_COMPILE_FLAGS += \
-I../include \
-I ../../et/include \
- -I ../../../libraries/et/include
+ -I ../../../libraries/et/include \
+ -Werror
# ----------------------------------------------------
# Targets
@@ -114,10 +115,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_wx.erl b/lib/observer/src/observer_app_wx.erl
index 380532e90c..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)
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index abf90ac612..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),
@@ -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 ee67664539..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};
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index c41f0f006a..5d1ab2e946 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -403,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}) ->
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index e433bea8c2..47740581f0 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -465,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 ->
@@ -548,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.
@@ -597,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 32e13004b6..1ff1b3ec52 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 1.2
+OBSERVER_VSN = 1.3
diff --git a/lib/odbc/aclocal.m4 b/lib/odbc/aclocal.m4
index 9578cd35c4..25f40944e7 100644
--- a/lib/odbc/aclocal.m4
+++ b/lib/odbc/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2013. 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
@@ -1849,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 026da39e6f..6572d28ee8 100644
--- a/lib/odbc/c_src/Makefile.in
+++ b/lib/odbc/c_src/Makefile.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 6d4460014f..a6b3de6e48 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -1,7 +1,7 @@
/*
* %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
@@ -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,
@@ -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);
@@ -228,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,
@@ -244,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 ---------------------------------------*/
@@ -347,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;
@@ -429,6 +431,20 @@ static db_result_msg handle_db_request(byte *reqstring, db_state *state)
<connStrIn>, returns a message indicating the outcome. */
static db_result_msg db_connect(byte *args, db_state *state)
{
+ /*
+ * Danil Onishchenko aka RubberCthulhu, [email protected]. 2013.01.09.
+ * It's a fix for Oracle ODBC driver for Linux.
+ * The issue: Oracle ODBC driver for Linux ignores setup autocommit mode
+ * during driver initialization before a connection to database has been
+ * established.
+ * Solution: set autocommit mode after a connection to database has been
+ * established.
+ *
+ * BEGIN
+ */
+ SQLLEN auto_commit_mode;
+ /* END */
+
SQLCHAR connStrOut[MAX_CONN_STR_OUT + 1] = {0};
SQLRETURN result;
SQLSMALLINT stringlength2ptr = 0, connlen;
@@ -436,14 +452,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;
@@ -463,6 +481,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);
@@ -473,10 +497,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))))
@@ -488,6 +512,42 @@ static db_result_msg db_connect(byte *args, db_state *state)
return msg;
}
+ /*
+ * Danil Onishchenko aka RubberCthulhu, [email protected]. 2013.01.09.
+ * It's a fix for Oracle ODBC driver for Linux.
+ * The issue: Oracle ODBC driver for Linux ignores setup autocommit mode
+ * during driver initialization before a connection to database has been
+ * established.
+ * Solution: set autocommit mode after a connection to database has been
+ * established.
+ *
+ * BEGIN
+ */
+ if(erl_auto_commit_mode == ON) {
+ auto_commit_mode = SQL_AUTOCOMMIT_ON;
+ } else {
+ auto_commit_mode = SQL_AUTOCOMMIT_OFF;
+ }
+
+ if(!sql_success(SQLSetConnectAttr(connection_handle(state),
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER)auto_commit_mode, 0))) {
+ diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state), extended_errors(state));
+ strcat((char *)diagnos.error_msg, " Set autocommit mode failed.");
+
+ msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
+
+ if(!sql_success(SQLFreeHandle(SQL_HANDLE_DBC,
+ connection_handle(state))))
+ DO_EXIT(EXIT_FREE);
+ if(!sql_success(SQLFreeHandle(SQL_HANDLE_ENV,
+ environment_handle(state))))
+ DO_EXIT(EXIT_FREE);
+
+ return msg;
+ }
+ /* END */
+
msg = retrive_scrollable_cursor_support_info(state);
return msg;
@@ -507,8 +567,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,
@@ -534,8 +594,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");
}
@@ -570,7 +630,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,
@@ -581,12 +641,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;
}
@@ -659,9 +719,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),
@@ -801,13 +861,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]) {
@@ -821,8 +881,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;
}
@@ -891,16 +951,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;
}
@@ -949,7 +1009,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;
@@ -958,6 +1018,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;
@@ -968,6 +1034,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;
@@ -1011,8 +1083,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;
}
@@ -1028,8 +1100,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;
}
@@ -1237,7 +1309,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 {
@@ -1782,6 +1854,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);
@@ -1797,9 +1873,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)
{
@@ -2325,7 +2416,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;
@@ -2394,10 +2485,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;
@@ -2506,7 +2597,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;
@@ -2593,10 +2684,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;
}
}
@@ -2613,7 +2704,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;
@@ -2637,17 +2728,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;
}
}
@@ -2655,7 +2745,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 a76cedf1af..916a7cb31d 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ * Copyright Ericsson AB 2002-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
@@ -145,6 +145,7 @@ typedef struct {
typedef struct {
SQLCHAR sqlState[SQL_STATE_SIZE];
+ SQLINTEGER nativeError;
byte error_msg[MAX_ERR_MSG];
} diagnos;
@@ -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..fd28830c0c 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 2005-2011. All Rights Reserved.
+dnl Copyright Ericsson AB 2005-2013. 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
@@ -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/notes.xml b/lib/odbc/doc/src/notes.xml
index 7ba0307a45..792dc3fddd 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,75 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.10.13</title>
+ <section><title>ODBC 2.10.15</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed calling odbc:param_query/3 and odbc:param_query/4
+ with unparameterized query string and empty parameters
+ list. Thanks to Danil Onishchenko.</p>
+ <p>
+ Own Id: OTP-10798</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Under Unix enable TCP_NODELAY to disable Nagel's socket
+ algorithm. Thanks to Andy Richards</p>
+ <p>
+ Impact: Performance gain on Unix systems</p>
+ <p>
+ Own Id: OTP-10506</p>
+ </item>
+ <item>
+ <p>
+ Added extended_errors option to ODBC</p>
+ <p>
+ When enabled, this option alters the return code of ODBC
+ operations that produce errors to include the ODBC error
+ code as well as the native error code, in addition to the
+ ODBC reason field which is returned by default. Thanks to
+ Bernard Duggan.</p>
+ <p>
+ Own Id: OTP-10603</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Fix aotocommit for Oracle ODBC driver in Linux. Thanks to
+ Danil Onishchenko.</p>
+ <p>
+ Own Id: OTP-10735</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.13</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 2af65cc757..bfbda8aaf4 100644
--- a/lib/odbc/src/Makefile
+++ b/lib/odbc/src/Makefile
@@ -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.erl b/lib/odbc/src/odbc.erl
index 16fdb4aabd..ebfcd20d9a 100644
--- a/lib/odbc/src/odbc.erl
+++ b/lib/odbc/src/odbc.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
@@ -810,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,
@@ -860,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}.
%%-------------------------------------------------------------------------
@@ -899,7 +902,9 @@ param_values(Params) ->
[{_, Values} | _] ->
Values;
[{_, _, Values} | _] ->
- Values
+ Values;
+ [] ->
+ []
end.
%%-------------------------------------------------------------------------
diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl
index a076c4dfff..74ae2c96e6 100644
--- a/lib/odbc/test/odbc_connect_SUITE.erl
+++ b/lib/odbc/test/odbc_connect_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -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.
@@ -277,13 +277,19 @@ port_dies(_Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
{status, _} = process_info(Ref, status),
process_flag(trap_exit, true),
- Port = lists:last(erlang:ports()),
- exit(Port, kill),
- %% Wait for exit_status from port 5000 ms (will not get a exit
- %% status in this case), then wait a little longer to make sure
- %% the port and the controlprocess has had time to terminate.
- test_server:sleep(10000),
- undefined = process_info(Ref, status).
+ NamedPorts = [{P, erlang:port_info(P, name)} || P <- erlang:ports()],
+ case [P || {P, {name, Name}} <- NamedPorts, is_odbcserver(Name)] of
+ [Port] ->
+ exit(Port, kill),
+ %% Wait for exit_status from port 5000 ms (will not get a exit
+ %% status in this case), then wait a little longer to make sure
+ %% the port and the controlprocess has had time to terminate.
+ test_server:sleep(10000),
+ undefined = process_info(Ref, status);
+ [] ->
+ ct:fail([erlang:port_info(P, name) || P <- erlang:ports()])
+ end.
+
%%-------------------------------------------------------------------------
control_process_dies(doc) ->
@@ -292,13 +298,17 @@ control_process_dies(suite) -> [];
control_process_dies(_Config) ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()),
process_flag(trap_exit, true),
- Port = lists:last(erlang:ports()),
- {connected, Ref} = erlang:port_info(Port, connected),
- exit(Ref, kill),
- test_server:sleep(500),
- undefined = erlang:port_info(Port, connected).
- %% Check for c-program still running, how?
-
+ NamedPorts = [{P, erlang:port_info(P, name)} || P <- erlang:ports()],
+ case [P || {P, {name, Name}} <- NamedPorts, is_odbcserver(Name)] of
+ [Port] ->
+ {connected, Ref} = erlang:port_info(Port, connected),
+ exit(Ref, kill),
+ test_server:sleep(500),
+ undefined = erlang:port_info(Port, connected);
+ %% Check for c-program still running, how?
+ [] ->
+ ct:fail([erlang:port_info(P, name) || P <- erlang:ports()])
+ end.
%%-------------------------------------------------------------------------
client_dies_normal(doc) ->
@@ -838,3 +848,43 @@ 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).
+
+
+is_odbcserver(Name) ->
+ case re:run(Name, "odbcserver") of
+ {match, _} ->
+ true;
+ _ ->
+ false
+ end.
+
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_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl
index 1852678b4b..062373afa0 100644
--- a/lib/odbc/test/odbc_query_SUITE.erl
+++ b/lib/odbc/test/odbc_query_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -63,7 +63,8 @@ groups() ->
param_insert_numeric, {group, param_insert_string},
param_insert_float, param_insert_real,
param_insert_double, param_insert_mix, param_update,
- param_delete, param_select]},
+ param_delete, param_select,
+ param_select_empty_params, param_delete_empty_params]},
{param_integers, [],
[param_insert_tiny_int, param_insert_small_int,
param_insert_int, param_insert_integer]},
@@ -1345,6 +1346,70 @@ param_select(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
+param_select_empty_params(doc) ->
+ ["Test parameterized select query with no parameters."];
+param_select_empty_params(suite) ->
+ [];
+param_select_empty_params(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID INTEGER, DATA CHARACTER VARYING(10),"
+ " PRIMARY KEY(ID))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2, 3]},
+ {{sql_varchar, 10},
+ ["foo", "bar", "foo"]}]),
+
+ true = odbc_test_lib:check_row_count(3, Count),
+
+ SelectResult = ?RDBMS:param_select(),
+
+ SelectResult = odbc:param_query(Ref, "SELECT * FROM " ++ Table ++
+ " WHERE DATA = \'foo\'",
+ []),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_delete_empty_params(doc) ->
+ ["Test parameterized delete query with no parameters."];
+param_delete_empty_params(suite) ->
+ [];
+param_delete_empty_params(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID INTEGER, DATA CHARACTER VARYING(10),"
+ " PRIMARY KEY(ID))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2, 3]},
+ {{sql_varchar, 10},
+ ["foo", "bar", "baz"]}]),
+ true = odbc_test_lib:check_row_count(3, Count),
+
+ {updated, NewCount} = odbc:param_query(Ref, "DELETE FROM " ++ Table ++
+ " WHERE ID = 1 OR ID = 2",
+ []),
+
+ true = odbc_test_lib:check_row_count(2, NewCount),
+
+ UpdateResult = ?RDBMS:param_delete(),
+
+ UpdateResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+ ok.
+
+%%-------------------------------------------------------------------------
describe_integer(doc) ->
["Test describe_table/[2,3] for integer columns."];
describe_integer(suite) ->
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 3bb2fe5bce..b3ffff2cf8 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.13
+ODBC_VSN = 2.10.15
diff --git a/lib/orber/COSS/CosNaming/Makefile b/lib/orber/COSS/CosNaming/Makefile
index 769c08a9e9..814062034c 100644
--- a/lib/orber/COSS/CosNaming/Makefile
+++ b/lib/orber/COSS/CosNaming/Makefile
@@ -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 3040c6443e..126ed8af21 100644
--- a/lib/orber/c_src/Makefile.in
+++ b/lib/orber/c_src/Makefile.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/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 1f7f4cb445..9e896f03c8 100644
--- a/lib/orber/doc/src/notes.xml
+++ b/lib/orber/doc/src/notes.xml
@@ -32,7 +32,8 @@
<file>notes.xml</file>
</header>
- <section><title>Orber 3.6.24.1</title>
+
+ <section><title>Orber 3.6.26</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -46,7 +47,34 @@
</section>
</section>
- <section><title>Orber 3.6.24</title>
+
+ <section><title>Orber 3.6.25</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ <p>
+ </p></p>
+ <p>
+ Own Id: OTP-10675 Aux Id: seq12154 </p>
+ </item>
+ </list>
+ </section>
+</section>
+
+<section><title>Orber 3.6.24</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/orber/examples/Stack/Makefile b/lib/orber/examples/Stack/Makefile
index f1a5106a7b..1cbb983cd6 100644
--- a/lib/orber/examples/Stack/Makefile
+++ b/lib/orber/examples/Stack/Makefile
@@ -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/src/Makefile b/lib/orber/src/Makefile
index 72610def2b..1c6781e5fd 100644
--- a/lib/orber/src/Makefile
+++ b/lib/orber/src/Makefile
@@ -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..61b3051883 100644
--- a/lib/orber/src/orber_ifr_exceptiondef.erl
+++ b/lib/orber/src/orber_ifr_exceptiondef.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. 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
@@ -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/csiv2_SUITE.erl b/lib/orber/test/csiv2_SUITE.erl
index 60ffa1eb09..da8c0d3738 100644
--- a/lib/orber/test/csiv2_SUITE.erl
+++ b/lib/orber/test/csiv2_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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 9a3d9fb4f1..6824d25aef 100644
--- a/lib/orber/test/orber_test_lib.erl
+++ b/lib/orber/test/orber_test_lib.erl
@@ -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/orber/vsn.mk b/lib/orber/vsn.mk
index 6291f9d991..4e09532f88 100644
--- a/lib/orber/vsn.mk
+++ b/lib/orber/vsn.mk
@@ -1,3 +1,2 @@
-
-ORBER_VSN = 3.6.24.1
+ORBER_VSN = 3.6.26
diff --git a/lib/os_mon/c_src/Makefile.in b/lib/os_mon/c_src/Makefile.in
index 2b16789a14..51569f6ec9 100644
--- a/lib/os_mon/c_src/Makefile.in
+++ b/lib/os_mon/c_src/Makefile.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/memsup.c b/lib/os_mon/c_src/memsup.c
index 593a066f98..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);
@@ -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);
}
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 061183e51c..61eac937e1 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,21 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.2.11</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Removed deprecated function calls to snmp</p>
+ <p>
+ Own Id: OTP-10448</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.2.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/mibs/Makefile b/lib/os_mon/mibs/Makefile
index 3e24c3d373..19f3dc8367 100644
--- a/lib/os_mon/mibs/Makefile
+++ b/lib/os_mon/mibs/Makefile
@@ -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 864d7a09d4..cd2dadec30 100644
--- a/lib/os_mon/src/Makefile
+++ b/lib/os_mon/src/Makefile
@@ -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
@@ -59,7 +59,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_obsolete_guard -I$(INCLUDE)
+ERL_COMPILE_FLAGS += +warn_obsolete_guard -I$(INCLUDE) -Werror
# ----------------------------------------------------
# Targets
@@ -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/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..b5f71f3eed 100644
--- a/lib/os_mon/src/os_mon.erl
+++ b/lib/os_mon/src/os_mon.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
@@ -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 9c5f2c1820..461bebc102 100644
--- a/lib/os_mon/test/Makefile
+++ b/lib/os_mon/test/Makefile
@@ -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 180906127c..7d6c5484a7 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.10
+OS_MON_VSN = 2.2.11
diff --git a/lib/otp_mibs/doc/src/notes.xml b/lib/otp_mibs/doc/src/notes.xml
index 3d3a1272a4..8986eab9ad 100644
--- a/lib/otp_mibs/doc/src/notes.xml
+++ b/lib/otp_mibs/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,21 @@
<p>This document describes the changes made to the OTP_Mibs
application.</p>
+<section><title>Otp_Mibs 1.0.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Otp_Mibs 1.0.7</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/otp_mibs/mibs/Makefile b/lib/otp_mibs/mibs/Makefile
index 703c4b3ed4..7f43ef31a6 100644
--- a/lib/otp_mibs/mibs/Makefile
+++ b/lib/otp_mibs/mibs/Makefile
@@ -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 03298d39d5..4f03d0228a 100644
--- a/lib/otp_mibs/src/Makefile
+++ b/lib/otp_mibs/src/Makefile
@@ -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/otp_mibs/vsn.mk b/lib/otp_mibs/vsn.mk
index f070288032..96d3088224 100644
--- a/lib/otp_mibs/vsn.mk
+++ b/lib/otp_mibs/vsn.mk
@@ -1,4 +1,4 @@
-OTP_MIBS_VSN = 1.0.7
+OTP_MIBS_VSN = 1.0.8
# Note: The branch 'otp_mibs' is defunct as of otp_mibs-1.0.4 and
# should NOT be used again.
diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index ac29cbb893..a0816dc728 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.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>
@@ -30,6 +30,39 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.0.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to Unicode support. </p>
+ <p>
+ Own Id: OTP-10820</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Parsetools 2.0.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p> The file <c>esyntax.yrl</c> has been removed. </p>
+ <p>
+ Own Id: OTP-10660</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.0.7</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/parsetools/include/yeccpre.hrl b/lib/parsetools/include/yeccpre.hrl
index 3672394fc5..855bff5fdc 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-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
@@ -166,7 +166,7 @@ yecctoken2string({integer,_,N}) -> io_lib:write(N);
yecctoken2string({float,_,F}) -> io_lib:write(F);
yecctoken2string({char,_,C}) -> io_lib:write_char(C);
yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]);
-yecctoken2string({string,_,S}) -> io_lib:write_unicode_string(S);
+yecctoken2string({string,_,S}) -> io_lib:write_string(S);
yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A);
yecctoken2string({_Cat, _, Val}) -> io_lib:format("~p",[Val]);
yecctoken2string({dot, _}) -> "'.'";
diff --git a/lib/parsetools/src/Makefile b/lib/parsetools/src/Makefile
index 4f199da8ee..2ee2285eb3 100644
--- a/lib/parsetools/src/Makefile
+++ b/lib/parsetools/src/Makefile
@@ -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
@@ -58,7 +58,8 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += +warn_obsolete_guard -I$(ERL_TOP)/lib/stdlib/include
+ERL_COMPILE_FLAGS += +warn_obsolete_guard -I$(ERL_TOP)/lib/stdlib/include \
+ -Werror
# ----------------------------------------------------
# Targets
@@ -78,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);' $< > $@
# ----------------------------------------------------
# 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..e531b78a5b 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=[]
@@ -126,7 +127,7 @@ file(File, Opts0) ->
leex_ret(St).
format_error({file_error, Reason}) ->
- io_lib:fwrite("~s",[file:format_error(Reason)]);
+ io_lib:fwrite("~ts",[file:format_error(Reason)]);
format_error(missing_defs) -> "missing Definitions";
format_error(missing_rules) -> "missing Rules";
format_error(missing_code) -> "missing Erlang code";
@@ -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("~ts: ~ts\n",
[File,Mod:format_error(E)]);
({File,{Line,Mod,E}}) ->
- io:fwrite("~s:~w: ~s\n",
+ io:fwrite("~ts:~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("~ts: ~s~ts\n",
[File,Prefix,
Mod:format_error(W)]);
({File,{Line,Mod,W}}) ->
- io:fwrite("~s:~w: ~s~s\n",
+ io:fwrite("~ts:~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 ~ts, ", [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,6 +501,8 @@ 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
{done,{ok,Toks,_},_} -> {ok,Toks,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.
@@ -1283,25 +1292,27 @@ pack_dfa([], _, Rs, PDFA) -> {PDFA,Rs}.
%% the code for the actions.
out_file(St0, DFA, DF, Actions, Code) ->
- verbose_print(St0, "Writing file ~s, ", [St0#leex.efile]),
+ verbose_print(St0, "Writing file ~ts, ", [St0#leex.efile]),
case open_inc_file(St0) of
{ok,Ifile} ->
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...
@@ -1559,7 +1582,7 @@ pp_sep(_, _, _, _) -> " ".
%% with Graphviz.
out_dfa_graph(St, DFA, DF) ->
- verbose_print(St, "Writing DFA to file ~s, ", [St#leex.gfile]),
+ verbose_print(St, "Writing DFA to file ~ts, ", [St#leex.gfile]),
case file:open(St#leex.gfile, [write]) of
{ok,Gfile} ->
try
@@ -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,8 +1633,18 @@ 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">>,
+ io:fwrite(File, <<"-file(~ts, ~w).\n">>,
[format_filename(Filename), Line]).
format_filename(Filename) ->
@@ -1636,7 +1669,7 @@ quote($\d) -> "\\\\d";
quote($\\) -> "\\\\";
quote(C) when is_integer(C) ->
%% Must remove the $ and get the \'s right.
- case io_lib:write_unicode_char(C) of
+ case io_lib:write_char(C) of
[$$,$\\|Cs] -> "\\\\" ++ Cs;
[$$|Cs] -> Cs
end;
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index b0792a6ed8..53292b037a 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-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,6 +42,7 @@
includefile,
includefile_version,
module,
+ encoding = none,
options = [],
verbose = false,
file_attrs = true,
@@ -184,7 +185,7 @@ format_error({endsymbol_is_terminal, Symbol}) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({file_error, Reason}) ->
- io_lib:fwrite("~s",[file:format_error(Reason)]);
+ io_lib:fwrite("~ts",[file:format_error(Reason)]);
format_error(illegal_empty) ->
io_lib:fwrite("illegal use of empty symbol", []);
format_error({internal_error, Error}) ->
@@ -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)]),
+ io:fwrite(<<"Generating parser from grammar in ~ts\n">>,
+ [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(<<"~ts: ~ts\n">>,
[File,Mod:format_error(E)]);
({File,{Line,Mod,E}}) ->
- io:fwrite(<<"~s:~w: ~s\n">>,
+ io:fwrite(<<"~ts:~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(<<"~ts: ~s~ts\n">>,
[File,Prefix,
Mod:format_error(W)]);
({File,{Line,Mod,W}}) ->
- io:fwrite(<<"~s:~w: ~s~s\n">>,
+ io:fwrite(<<"~ts:~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_string_as_latin1(String);
+pp_symbol({string,_,String}, _Enc) -> io_lib:write_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,8 +2507,18 @@ 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">>,
+ fwrite(St, <<"-file(~ts, ~w).\n">>,
[format_filename(Filename), Line]);
output_file_directive(St, _Filename, _Line) ->
St.
diff --git a/lib/parsetools/src/yeccparser.erl b/lib/parsetools/src/yeccparser.erl
index e4b8b06db5..54f9ba5a58 100644
--- a/lib/parsetools/src/yeccparser.erl
+++ b/lib/parsetools/src/yeccparser.erl
@@ -17,7 +17,7 @@ line_of(Token) ->
%%
%% %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
@@ -178,7 +178,7 @@ yecctoken2string({integer,_,N}) -> io_lib:write(N);
yecctoken2string({float,_,F}) -> io_lib:write(F);
yecctoken2string({char,_,C}) -> io_lib:write_char(C);
yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]);
-yecctoken2string({string,_,S}) -> io_lib:write_unicode_string(S);
+yecctoken2string({string,_,S}) -> io_lib:write_string(S);
yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A);
yecctoken2string({_Cat, _, Val}) -> io_lib:write(Val);
yecctoken2string({dot, _}) -> "'.'";
diff --git a/lib/parsetools/src/yeccscan.erl b/lib/parsetools/src/yeccscan.erl
index d7ec3ba8d3..fa3ce8c73b 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-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
@@ -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/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 1e50aedf07..afedd79a4e 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %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..3d66a2a525 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index e2594564cb..444caf44a1 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.0.7
+PARSETOOLS_VSN = 2.0.9
diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml
index dd70cfe994..82ce47c0ef 100644
--- a/lib/percept/doc/src/notes.xml
+++ b/lib/percept/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,21 @@
</header>
<p>This document describes the changes made to the Percept application.</p>
+<section><title>Percept 0.8.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Percept 0.8.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile
index 253a8c2da3..6bf0af9dc6 100644
--- a/lib/percept/src/Makefile
+++ b/lib/percept/src/Makefile
@@ -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/vsn.mk b/lib/percept/vsn.mk
index f868246f7b..07e9ac5085 100644
--- a/lib/percept/vsn.mk
+++ b/lib/percept/vsn.mk
@@ -1 +1 @@
-PERCEPT_VSN = 0.8.7
+PERCEPT_VSN = 0.8.8
diff --git a/lib/pman/doc/src/notes.xml b/lib/pman/doc/src/notes.xml
index 778f960004..26f966fd86 100644
--- a/lib/pman/doc/src/notes.xml
+++ b/lib/pman/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,38 @@
</header>
<p>This document describes the changes made to the Pman application.</p>
+<section><title>Pman 2.7.1.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Tuple funs (deprecated in R15B) are no longer supported.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10170</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Pman 2.7.1.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Pman 2.7.1.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/pman/src/Makefile b/lib/pman/src/Makefile
index fa01fe7238..eb0413bdbc 100644
--- a/lib/pman/src/Makefile
+++ b/lib/pman/src/Makefile
@@ -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/pman/src/pman_win.erl b/lib/pman/src/pman_win.erl
index 350bc2dd7f..aec7dc4412 100644
--- a/lib/pman/src/pman_win.erl
+++ b/lib/pman/src/pman_win.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
@@ -546,7 +546,7 @@ name(Pid) ->
%%
module_data(ModuleName) ->
- vformat("", catch apply({ModuleName, module_info},[])).
+ vformat("", catch apply(ModuleName, module_info, [])).
diff --git a/lib/pman/vsn.mk b/lib/pman/vsn.mk
index 13dce1a7a5..92fb9df222 100644
--- a/lib/pman/vsn.mk
+++ b/lib/pman/vsn.mk
@@ -1 +1 @@
-PMAN_VSN = 2.7.1.2
+PMAN_VSN = 2.7.1.4
diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile
index 957c332cad..a4e36c7293 100644
--- a/lib/public_key/asn1/Makefile
+++ b/lib/public_key/asn1/Makefile
@@ -66,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
# ----------------------------------------------------
# Targets
@@ -81,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
@@ -119,6 +119,6 @@ OTP-PUB-KEY.asn1db: PKIX1Algorithms88.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.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 e94a77a3e7..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
@@ -317,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 |
@@ -365,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/PKCS-10.asn1 b/lib/public_key/asn1/PKCS-10.asn1
index 333104d230..5ada81c257 100644
--- a/lib/public_key/asn1/PKCS-10.asn1
+++ b/lib/public_key/asn1/PKCS-10.asn1
@@ -20,12 +20,36 @@ IMPORTS
ATTRIBUTE
FROM InformationFramework informationFramework
- Name
+ Name, Extensions, DirectoryString
FROM PKIX1Explicit88 --InformationFramework informationFramework
ALGORITHM
FROM PKCS-7; --AuthenticationFramework authenticationFramework;
+-- start inlined from PKCS-9
+
+--pkcs-9-ub-pkcs9String INTEGER ::= 255
+--pkcs-9-ub-challengePassword INTEGER ::= pkcs-9-ub-pkcs9String
+pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7}
+
+challengePassword ATTRIBUTE ::= {
+ WITH SYNTAX DirectoryString --{pkcs-9-ub-challengePassword}
+ SINGLE VALUE TRUE
+ ID pkcs-9-at-challengePassword
+}
+
+pkcs-9-at-extensionRequest OBJECT IDENTIFIER ::= {pkcs-9 14}
+
+extensionRequest ATTRIBUTE ::= {
+ WITH SYNTAX ExtensionRequest
+ SINGLE VALUE TRUE
+ ID pkcs-9-at-extensionRequest
+}
+
+ExtensionRequest ::= Extensions
+
+-- end inlined from PKCS-9
+
-- Certificate requests
CertificationRequestInfo ::= SEQUENCE {
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index 94c7c46350..ac4b4e4489 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>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,12 +34,11 @@
<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
+ <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>. Also
- descirbed is <p>CertificationRequest</p> that is defined by
+ 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>
@@ -48,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>
@@ -62,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>
@@ -101,7 +100,7 @@
#'Certificate'{
tbsCertificate, % #'TBSCertificate'{}
signatureAlgorithm, % #'AlgorithmIdentifier'{}
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}.
#'TBSCertificate'{
@@ -119,7 +118,7 @@
#'AlgorithmIdentifier'{
algorithm, % oid()
- parameters % asn1_der_encoded()
+ parameters % der_encoded()
}.
</code>
@@ -127,7 +126,7 @@
#'OTPCertificate'{
tbsCertificate, % #'OTPTBSCertificate'{}
signatureAlgorithm, % #'SignatureAlgorithm'
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}.
#'OTPTBSCertificate'{
@@ -137,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'{}]
@@ -290,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>
@@ -372,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>
@@ -461,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'{
@@ -486,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>
@@ -530,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'{
@@ -589,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()]
@@ -641,30 +642,35 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'CertificationRequest'{
certificationRequestInfo #'CertificationRequestInfo'{},
signatureAlgorithm #'CertificationRequest_signatureAlgorithm'{}}.
- signature {0, binary()} - asn1 compact bitstring
+ signature {0, binary()} - ASN1 compact bitstring
}
#'CertificationRequestInfo'{
version atom(),
subject {rdnSequence, [#AttributeTypeAndValue'{}]} ,
subjectPKInfo #'CertificationRequestInfo_subjectPKInfo'{},
- attributes [#AttributeTypeAndValue'{}]
+ attributes [#'AttributePKCS-10' {}]
}
#'CertificationRequestInfo_subjectPKInfo'{
algorithm #'CertificationRequestInfo_subjectPKInfo_algorithm'{}
- subjectPublicKey {0, binary()} - asn1 compact bitstring
+ subjectPublicKey {0, binary()} - ASN1 compact bitstring
}
#'CertificationRequestInfo_subjectPKInfo_algorithm'{
algorithm = oid(),
- parameters = asn1_der_encoded()
+ parameters = der_encoded()
}
#'CertificationRequest_signatureAlgorithm'{
algorithm = oid(),
- parameters = asn1_der_encoded()
- }
+ parameters = der_encoded()
+ }
+
+#'AttributePKCS-10'{
+ type = oid(),
+ values = [der_encoded()]
+}
</code>
</section>
diff --git a/lib/public_key/doc/src/introduction.xml b/lib/public_key/doc/src/introduction.xml
index e0dd5603f7..4b59cc2245 100644
--- a/lib/public_key/doc/src/introduction.xml
+++ b/lib/public_key/doc/src/introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -36,11 +36,13 @@
<section>
<title>Purpose</title>
- <p> This application provides an API to public key infrastructure
- 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>
+ <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>
@@ -51,9 +53,9 @@
<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 a5e8beedf0..3a7ccbe568 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>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,6 +34,55 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 0.18</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix subjectPublicKeyInfo type comment in public_key.
+ Thanks to Ryosuke Nakai.</p>
+ <p>
+ Own Id: OTP-10670</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ public_key now supports CRL validation and documents the
+ function public_key:pkix_path_validation/3</p>
+ <p>
+ Own Id: OTP-7045</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Fixed typo's in public_key spec.</p>
+ <p>
+ Own Id: OTP-10723</p>
+ </item>
+ <item>
+ <p>
+ Corrected PKCS-10 documentation and added some PKCS-9
+ support that is fairly commonly used by PKCS-10. Full
+ support for PKCS-9 will be added later.</p>
+ <p>
+ Own Id: OTP-10767</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.17</title>
<section><title>Fixed Bugs and Malfunctions</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 2ec1fcff9d..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>2012</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 <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url>- 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' | 'CertificationRequest'</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>
@@ -86,6 +107,9 @@
<p><code> dss_digest_type() = '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>
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 90ca7256ea..4d1d510f29 100644
--- a/lib/public_key/include/public_key.hrl
+++ b/lib/public_key/include/public_key.hrl
@@ -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' | 'CertificationRequest'.
+ | '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 d5cd13d81a..09393e8e50 100644
--- a/lib/public_key/src/Makefile
+++ b/lib/public_key/src/Makefile
@@ -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
@@ -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 33fe940ea2..98004c71a3 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -119,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
@@ -161,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).
@@ -176,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 4012825f20..6bdc35fb79 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -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]).
@@ -198,7 +200,9 @@ pem_start('DHParameter') ->
pem_start('CertificationRequest') ->
<<"-----BEGIN CERTIFICATE REQUEST-----">>;
pem_start('ContentInfo') ->
- <<"-----BEGIN PKCS7-----">>.
+ <<"-----BEGIN PKCS7-----">>;
+pem_start('CertificateList') ->
+ <<"-----BEGIN X509 CRL-----">>.
pem_end(<<"-----BEGIN CERTIFICATE-----">>) ->
<<"-----END CERTIFICATE-----">>;
@@ -220,6 +224,8 @@ 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.
@@ -242,7 +248,9 @@ asn1_type(<<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>) ->
asn1_type(<<"-----BEGIN CERTIFICATE REQUEST-----">>) ->
'CertificationRequest';
asn1_type(<<"-----BEGIN PKCS7-----">>) ->
- 'ContentInfo'.
+ '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 d5df53e848..e753cf3867 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-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
@@ -42,7 +42,8 @@
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'
@@ -50,6 +51,8 @@
-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
-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>>).
@@ -428,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>.
%%--------------------------------------------------------------------
@@ -443,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().
@@ -502,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()}}.
@@ -511,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} ->
@@ -537,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()}].
%%
@@ -611,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,
@@ -630,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,
@@ -641,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 =
@@ -676,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/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 1db3b9df90..ea48479f0b 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -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() ->
@@ -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] =
@@ -252,10 +196,8 @@ dh_pem(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-pkcs10_pem(doc) ->
- [""];
-pkcs10_pem(suite) ->
- [];
+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] =
@@ -269,10 +211,8 @@ pkcs10_pem(Config) when is_list(Config) ->
Entry = public_key:pem_entry_encode('CertificationRequest', PKCS10).
%%--------------------------------------------------------------------
-pkcs7_pem(doc) ->
- [""];
-pkcs7_pem(suite) ->
- [];
+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] =
@@ -286,10 +226,8 @@ pkcs7_pem(Config) when is_list(Config) ->
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),
@@ -315,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),
@@ -343,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),
@@ -370,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),
@@ -388,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),
@@ -410,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),
@@ -432,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),
@@ -452,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),
@@ -519,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),
@@ -533,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,
@@ -553,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}]),
@@ -576,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}]),
@@ -615,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")),
@@ -656,17 +599,15 @@ 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_countryname(doc) ->
- "Test workaround for certs that code x509countryname as utf8";
-pkix_countryname(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),
@@ -676,24 +617,9 @@ pkix_countryname(Config) when is_list(Config) ->
check_countryname(Issuer),
check_countryname(Subj).
-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}|_]) ->
- test_server:fail({incorrect_cuntry_name, Value});
-do_check_countryname([_| Rest]) ->
- do_check_countryname(Rest).
-
%%--------------------------------------------------------------------
-pkix_path_validation(doc) ->
- "Misc pkix tests not covered elsewhere";
-pkix_path_validation(suite) ->
- [];
+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},
@@ -724,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], []),
@@ -761,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;
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/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 b8af89d040..bd20a5546b 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.17
+PUBLIC_KEY_VSN = 0.18
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index 66ec7e9035..a9aed2b0a3 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>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,7 +37,79 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
- <section><title>Reltool 0.6.1</title>
+ <section><title>Reltool 0.6.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Some updates are made to reltool for handling unicode.</p>
+ <p>
+ Own Id: OTP-10781</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <list> <item> If <c>incl_cond</c> was set to
+ <c>derived</c> on module level, then reltool_server would
+ crash with a <c>case_clause</c>. This has been corrected.
+ <c>incl_cond</c> on module level now overwrites
+ <c>mod_cond</c> on app or sys level as described in the
+ documentation. </item> <item> If a <c>rel</c> spec in the
+ reltool config does not contain all applications that are
+ listed as <c>{applications,Applications}</c> in a
+ <c>.app</c> file, then these applications are
+ autmatically added when creating the <c>.rel</c> file.
+ For 'included_applications', the behaviour was not the
+ same. I.e. if a <c>rel</c> spec in the reltool config did
+ not contain all applications that are listed as
+ <c>{included_applications,InclApplications}</c> in a
+ <c>.app</c> file, then reltool would fail with reason
+ "Undefined applications" when creating the <c>.rel</c>
+ file. This has been corrected, so both
+ <c>applications</c> and <c>included_applications</c> are
+ now automatically added if not already in the <c>rel</c>
+ spec. </item> <item> The <c>rel</c> specification now
+ dictates the order in which included and used
+ applications (specified in the <c>.app</c> file as
+ <c>included_applications</c> and <c>applications</c>
+ respectively) are loaded/started by the boot file. If the
+ applications are not specified in the <c>rel</c> spec,
+ then the order from the <c>.app</c> file is used. This
+ was a bug earlier reported on systools, and is now also
+ implemented in reltool. </item> <item> Instead of only
+ looking at the directory name, reltool now first looks
+ for a <c>.app</c> file in order to figure out the name of
+ an application. </item> </list></p>
+ <p>
+ Own Id: OTP-10012 Aux Id: kunagi-171 [82] </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.6.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 2567a72999..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
@@ -264,7 +274,8 @@
</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>
+ <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>
@@ -372,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>
@@ -383,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>
@@ -446,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
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/display_args b/lib/reltool/examples/display_args
index bf0994ab7c..a8882f7bda 100644
--- a/lib/reltool/examples/display_args
+++ b/lib/reltool/examples/display_args
@@ -1,8 +1,9 @@
#!/usr/bin/env escript
%% -*- erlang -*-
-%%! -smp disable
+%%! -emuarg emuvalue
main(Args) ->
+ {ok,[Emuvalue]} = init:get_argument(emuarg),
io:format("Root dir: ~s\n", [code:root_dir()]),
io:format("Script args: ~p\n", [Args]),
- io:format("Smp: ~p\n", [erlang:system_info(smp_support)]).
+ io:format("Emuarg: ~p\n", [Emuvalue]).
diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile
index a7e34053f1..b8387fff96 100644
--- a/lib/reltool/src/Makefile
+++ b/lib/reltool/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2012. All Rights Reserved.
+# Copyright Ericsson AB 2009-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
@@ -57,7 +57,8 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
ERL_COMPILE_FLAGS += +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+ +'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ -Werror
# ----------------------------------------------------
# Targets
@@ -79,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);' $< > $@
# ----------------------------------------------------
# Dependencies
diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl
index 2bdf222aa0..8ab2c2399e 100644
--- a/lib/reltool/src/reltool.erl
+++ b/lib/reltool/src/reltool.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -120,7 +120,7 @@ apply_fun(Pid, false, Fun) ->
apply_fun(Pid, true, Fun) ->
case get_status(Pid) of
{ok, Warnings} ->
- [io:format("~p: ~s\n", [?APPLICATION, W]) || W <- Warnings],
+ [io:format("~w: ~ts\n", [?APPLICATION, W]) || W <- Warnings],
apply_fun(Pid, false, Fun);
{error, _Reason} = Error ->
stop(Pid),
diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl
index 6cd0d2f90b..81ab1687fb 100644
--- a/lib/reltool/src/reltool_app_win.erl
+++ b/lib/reltool/src/reltool_app_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -173,7 +173,7 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) ->
S#state.mod_wins)},
?MODULE:loop(S2);
Msg ->
- error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ error_logger:format("~w~w got unexpected message:\n\t~p\n",
[?MODULE, self(), Msg]),
?MODULE:loop(S)
end.
@@ -181,7 +181,7 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) ->
exit_warning({'EXIT', _Pid, shutdown}) ->
ok;
exit_warning({'EXIT', _Pid, _Reason} = Msg) ->
- error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ error_logger:format("~w~w got unexpected message:\n\t~p\n",
[?MODULE, self(), Msg]).
create_window(#state{app = App} = S) ->
@@ -627,7 +627,7 @@ handle_event(#state{sys = Sys, app = App} = S, Wx) ->
Items = reltool_utils:get_items(ListCtrl),
handle_mod_button(S, Items, Action);
_ ->
- error_logger:format("~p~p got unexpected app event from "
+ error_logger:format("~w~w got unexpected app event from "
"wx:\n\t~p\n",
[?MODULE, self(), Wx]),
S
@@ -674,8 +674,8 @@ move_mod(App, {_ItemNo, ModStr}, Action) ->
blacklist_del ->
undefined;
_ ->
- error_logger:format("~p~p got unexpected mod "
- "button event: ~p\n\t ~p\n",
+ error_logger:format("~w~w got unexpected mod "
+ "button event: ~w\n\t ~p\n",
[?MODULE, self(), ModName, Action]),
M#mod.incl_cond
end,
diff --git a/lib/reltool/src/reltool_fgraph_win.erl b/lib/reltool/src/reltool_fgraph_win.erl
index b0deb1bab2..66bc2b5ab3 100644
--- a/lib/reltool/src/reltool_fgraph_win.erl
+++ b/lib/reltool/src/reltool_fgraph_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -525,7 +525,7 @@ loop(S, G) ->
exit(Reason);
Other ->
- error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ error_logger:format("~w~w got unexpected message:\n\t~p\n",
[?MODULE, self(), Other]),
loop(S, G)
end.
diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl
index 899423bb6d..b0193a2ae4 100644
--- a/lib/reltool/src/reltool_mod_win.erl
+++ b/lib/reltool/src/reltool_mod_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -130,7 +130,7 @@ do_init(Parent, WxEnv, Xref, RelPid, C, ModName) ->
loop(#state{xref_pid = Xref, common = C, mod = Mod} = S) ->
receive
Msg ->
- %% io:format("~s~p -> ~p\n", [S#state.name, self(), Msg]),
+ %% io:format("~ts~w -> ~p\n", [S#state.name, self(), Msg]),
case Msg of
{system, From, SysMsg} ->
Dbg = C#common.sys_debug,
@@ -170,7 +170,7 @@ loop(#state{xref_pid = Xref, common = C, mod = Mod} = S) ->
S2 = handle_event(S, Wx),
?MODULE:loop(S2);
_ ->
- error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ error_logger:format("~w~w got unexpected message:\n\t~p\n",
[?MODULE, self(), Msg]),
?MODULE:loop(S)
end
@@ -335,23 +335,37 @@ find_regular_bin(App, Mod) ->
SrcDir = filename:join([ActiveDir, "src"]),
ModStr = atom_to_list(Mod#mod.name),
Base = "^" ++ ModStr ++ "\\.erl$",
- Find = fun(F, _Acc) -> throw(file:read_file(F)) end,
+ Find = fun(F, _Acc) -> throw({file:read_file(F),epp:read_encoding(F)}) end,
case catch filelib:fold_files(SrcDir, Base, true, Find, {error, enoent}) of
- {ok, Bin} ->
- Bin;
+ {{ok, Bin},Encoding0} ->
+ Encoding =
+ case Encoding0 of
+ none -> epp:default_encoding();
+ _ -> Encoding0
+ end,
+ unicode:characters_to_binary(Bin,Encoding,utf8);
{error, enoent} ->
%% Reconstructing the source code from debug info if possible
BeamFile = filename:join([ActiveDir, "ebin", ModStr ++ ".beam"]),
- case beam_lib:chunks(BeamFile, [abstract_code]) of
- {ok,{_,[{abstract_code,{_,AC}}]}} ->
- IoList = erl_prettypr:format(erl_syntax:form_list(AC)),
- list_to_binary(IoList);
- _ ->
- list_to_binary(["%% Bad luck, cannot find any "
- "debug info in the file \"", BeamFile])
+ case source_from_beam(BeamFile) of
+ {ok,Source} ->
+ Source;
+ error ->
+ unicode:characters_to_binary(
+ ["%% Bad luck, cannot find any "
+ "debug info in the file \"", BeamFile])
end
end.
+source_from_beam(Beam) ->
+ case beam_lib:chunks(Beam, [abstract_code]) of
+ {ok,{_,[{abstract_code,{_,AC}}]}} ->
+ IoList = [erl_pp:form(F,[{encoding,utf8}]) || F <- AC],
+ {ok,unicode:characters_to_binary(IoList)};
+ _ ->
+ error
+ end.
+
find_escript_bin(#app{active_dir = ActiveDir}, Mod) ->
NotFound = false,
ModName = Mod#mod.name,
@@ -366,16 +380,10 @@ find_escript_bin(#app{active_dir = ActiveDir}, Mod) ->
case beam_lib:version(Bin) of
{ok,{M, _}} when M =:= ModName;
FullName =:= "." ->
- case beam_lib:chunks(Bin,
- [abstract_code]) of
- {ok,{_,[{abstract_code,{_,AC}}]}} ->
- Form =
- erl_syntax:form_list(AC),
- IoList =
- erl_prettypr:format(Form),
- {obj,
- list_to_binary(IoList)};
- _ ->
+ case source_from_beam(Bin) of
+ {ok,Source} ->
+ {obj,Source};
+ error ->
Acc
end;
_ ->
@@ -396,12 +404,9 @@ find_escript_bin(#app{active_dir = ActiveDir}, Mod) ->
case filename:split(FullName) of
[_AppName, "ebin", F]
when F =:= ObjFile, Acc =:= NotFound ->
- case beam_lib:chunks(GetBin(),
- [abstract_code]) of
- {ok,{_,[{abstract_code,{_,AC}}]}} ->
- Form = erl_syntax:form_list(AC),
- IoList = erl_prettypr:format(Form),
- {obj, list_to_binary(IoList)};
+ case source_from_beam(GetBin()) of
+ {ok,Source} ->
+ {obj,Source};
_ ->
Acc
end;
@@ -420,13 +425,15 @@ find_escript_bin(#app{active_dir = ActiveDir}, Mod) ->
{ok, {obj, Bin}} ->
Bin;
_ ->
- list_to_binary(["%% Bad luck, cannot find the "
- "code in the escript ", Escript, "."])
+ unicode:characters_to_binary(
+ ["%% Bad luck, cannot find the "
+ "code in the escript ", Escript, "."])
end
catch
throw:Reason when is_list(Reason) ->
- list_to_binary(["%% Bad luck, cannot find the code "
- "in the escript ", Escript, ": ", Reason])
+ unicode:characters_to_binary(
+ ["%% Bad luck, cannot find the code "
+ "in the escript ", Escript, ": ", Reason])
end.
create_config_page(S) ->
@@ -478,7 +485,7 @@ handle_event(#state{xref_pid = Xref} = S, Wx) ->
wxWindow:setFocus(ObjRef),
S;
_ ->
- error_logger:format("~p~p got unexpected mod event from "
+ error_logger:format("~w~w got unexpected mod event from "
"wx:\n\t~p\n",
[?MODULE, self(), Wx]),
S
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 3d1d7e54bf..5e25f22a6f 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -399,12 +399,12 @@ loop(#state{sys = Sys} = S) ->
{'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid ->
exit(Reason);
{call, ReplyTo, Ref, Msg} when is_pid(ReplyTo), is_reference(Ref) ->
- error_logger:format("~p~p got unexpected call:\n\t~p\n",
+ error_logger:format("~w~w got unexpected call:\n\t~p\n",
[?MODULE, self(), Msg]),
reltool_utils:reply(ReplyTo, Ref, {error, {invalid_call, Msg}}),
?MODULE:loop(S);
Msg ->
- error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ error_logger:format("~w~w got unexpected message:\n\t~p\n",
[?MODULE, self(), Msg]),
?MODULE:loop(S)
end.
@@ -422,7 +422,7 @@ do_set_apps(#state{sys = Sys} = S, ChangedApps) ->
%% 2) removing #app records if no configurable fields are set
%% 3) keeping #app records that are not changed
app_update_config([#app{name=Name,is_escript={inlined,Escript}}|_],_SysApps) ->
- reltool_utils:throw_error("Application ~p is inlined in ~p. Can not change "
+ reltool_utils:throw_error("Application ~w is inlined in ~w. Can not change "
"configuration for an inlined application.",
[Name,Escript]);
app_update_config([Config|Configs],SysApps) ->
@@ -580,8 +580,8 @@ apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) ->
IA;
false ->
reltool_utils:throw_error(
- "Release ~p uses non existing "
- "application ~p",
+ "Release ~tp uses non existing "
+ "application ~w",
[RelName,RA#rel_app.name])
end;
IA ->
@@ -602,7 +602,7 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) ->
more_apps_in_rels(RelApps, Apps, Acc2);
false ->
reltool_utils:throw_error(
- "Release ~p uses non existing application ~p",
+ "Release ~tp uses non existing application ~w",
[RelName,AppName])
end
end;
@@ -640,7 +640,7 @@ app_init_is_included(#state{app_tab = AppTab, mod_tab = ModTab, sys=Sys},
{undefined, false, false, Status};
{exclude, [RelName | _]} -> % App is included in at least one rel
reltool_utils:throw_error(
- "Application ~p is used in release ~p and cannot be excluded",
+ "Application ~w is used in release ~tp and cannot be excluded",
[AppName,RelName]);
{derived, []} ->
{undefined, undefined, undefined, Status};
@@ -665,7 +665,7 @@ app_init_is_included(#state{app_tab = AppTab, mod_tab = ModTab, sys=Sys},
Status3.
mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
- %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]),
+ %% print(M#mod.name, hipe, "incl_cond -> ~w\n", [AppCond]),
IsIncl =
case AppCond of
include ->
@@ -674,8 +674,10 @@ 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",
+ %% print(M#mod.name, hipe, "mod_cond -> ~w\n",
%% [ModCond]),
case ModCond of
all -> true;
@@ -693,6 +695,8 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
true;
exclude ->
false;
+ derived ->
+ undefined;
undefined ->
Default
end
@@ -707,23 +711,23 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
{false,_} ->
ets:insert(ModTab, M2),
reltool_utils:add_warning(
- "Module ~p exists in applications ~p and ~p. "
- "Using module from application ~p.",
+ "Module ~w exists in applications ~w and ~w. "
+ "Using module from application ~w.",
[M#mod.name, Existing#mod.app_name,
M#mod.app_name, M#mod.app_name],
Status);
{_,false} ->
%% Don't insert in ModTab - using Existing
reltool_utils:add_warning(
- "Module ~p exists in applications ~p and ~p. "
- "Using module from application ~p.",
+ "Module ~w exists in applications ~w and ~w. "
+ "Using module from application ~w.",
[M#mod.name, Existing#mod.app_name,
M#mod.app_name,Existing#mod.app_name],
Status);
{_,_} ->
reltool_utils:throw_error(
- "Module ~p potentially included by two different "
- "applications: ~p and ~p.",
+ "Module ~w potentially included by two different "
+ "applications: ~w and ~w.",
[M#mod.name,Existing#mod.app_name,M#mod.app_name])
end;
[] ->
@@ -731,7 +735,7 @@ mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) ->
Status
end,
- %% print(M#mod.name, hipe, "~p -> ~p\n", [M2, IsIncl]),
+ %% print(M#mod.name, hipe, "~p -> ~w\n", [M2, IsIncl]),
{M2,Status2}.
false_to_undefined(Bool) ->
@@ -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),
@@ -883,7 +888,7 @@ mod_recap_dependencies(S, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) ->
case ets:lookup(S#state.mod_tab, ModName) of
[M2] when M2#mod.app_name=:=A#app.name ->
ModStatus = do_get_status(M2),
- %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]),
+ %% print(M2#mod.name, hipe, "status -> ~w\n", [ModStatus]),
{IsIncl2, M3} =
case M2#mod.is_included of
true ->
@@ -905,8 +910,8 @@ mod_recap_dependencies(S, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) ->
%% A module is potensially included by multiple
%% applications. This is not allowed!
reltool_utils:throw_error(
- "Module ~p potentially included by two different applications: "
- "~p and ~p", [ModName,A#app.name, " and ", M2#mod.app_name, "."])
+ "Module ~w potentially included by two different applications: "
+ "~w and ~w.", [ModName,A#app.name, M2#mod.app_name])
end;
mod_recap_dependencies(_S, _A, [], Acc, IsIncl) ->
{lists:reverse(Acc), IsIncl}.
@@ -934,7 +939,7 @@ verify_config(#state{app_tab=AppTab, sys=#sys{boot_rel = BootRel, rels = Rels}},
Rels);
false ->
reltool_utils:throw_error(
- "Release ~p is mandatory (used as boot_rel)",[BootRel])
+ "Release ~tp is mandatory (used as boot_rel)",[BootRel])
end.
check_app(AppTab, {RelName, AppName}, Status) ->
@@ -944,7 +949,7 @@ check_app(AppTab, {RelName, AppName}, Status) ->
Status;
_ ->
reltool_utils:throw_error(
- "Release ~p uses non included application ~p",[RelName,AppName])
+ "Release ~tp uses non included application ~w",[RelName,AppName])
end.
check_rel(RelName, RelApps, Status) ->
@@ -955,8 +960,8 @@ check_rel(RelName, RelApps, Status) ->
Acc;
false ->
reltool_utils:throw_error(
- "Mandatory application ~p is not included in "
- "release ~p", [AppName,RelName])
+ "Mandatory application ~w is not included in "
+ "release ~tp", [AppName,RelName])
end
end,
Mandatory = [kernel, stdlib],
@@ -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,
@@ -1010,8 +1015,8 @@ refresh_app(#app{name = AppName,
lists:foldl(
fun(M,S) ->
reltool_utils:add_warning(
- "Module ~p duplicated in app file for "
- "application ~p.", [M, AppName], S)
+ "Module ~w duplicated in app file for "
+ "application ~w.", [M, AppName], S)
end,
Status3,
DuplicatedMods)
@@ -1069,18 +1074,18 @@ read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) ->
parse_app_info(AppFile, Info, AI, Status);
{ok, _BadApp} ->
{missing_app_info(DefaultVsn),
- reltool_utils:add_warning("~p: Illegal contents in app file ~p, "
+ reltool_utils:add_warning("~w: Illegal contents in app file ~tp, "
"application tuple with arity 3 expected.",
[AppName,AppFile],
Status)};
{error, Text} when Text =:= EnoentText ->
{missing_app_info(DefaultVsn),
- reltool_utils:add_warning("~p: Missing app file ~p.",
+ reltool_utils:add_warning("~w: Missing app file ~tp.",
[AppName,AppFile],
Status)};
{error, Text} ->
{missing_app_info(DefaultVsn),
- reltool_utils:add_warning("~p: Cannot parse app file ~p (~p).",
+ reltool_utils:add_warning("~w: Cannot parse app file ~tp (~tp).",
[AppName,AppFile,Text],
Status)}
end.
@@ -1117,7 +1122,7 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) ->
Status);
_ ->
Status2 =
- reltool_utils:add_warning("Unexpected item ~p in app file ~p.",
+ reltool_utils:add_warning("Unexpected item ~p in app file ~tp.",
[Key,File],
Status),
parse_app_info(File, KeyVals, AI, Status2)
@@ -1207,7 +1212,7 @@ wait_for_processto_die(Ref, Pid, File) ->
{'DOWN', Ref, _Type, _Object, _Info} ->
ok
after timer:seconds(30) ->
- error_logger:error_msg("~p(~p): Waiting for process ~p to die ~p\n",
+ error_logger:error_msg("~w(~w): Waiting for process ~w to die ~tp\n",
[?MODULE, ?LINE, Pid, File]),
wait_for_processto_die(Ref, Pid, File)
end.
@@ -1218,7 +1223,7 @@ add_missing_mods(AppName, EbinMods, AppModNames) ->
[missing_mod(ModName, AppName) || ModName <- MissingModNames].
missing_mod(ModName, AppName) ->
- %% io:format("Missing: ~p -> ~p\n", [AppName, ModName]),
+ %% io:format("Missing: ~w -> ~w\n", [AppName, ModName]),
#mod{name = ModName,
app_name = AppName,
incl_cond = undefined,
@@ -1318,7 +1323,7 @@ read_config(OldSys, Filename) when is_list(Filename) ->
{ok, Content} ->
reltool_utils:throw_error("Illegal file content: ~p",[Content]);
{error, Reason} ->
- reltool_utils:throw_error("Illegal config file ~p: ~s",
+ reltool_utils:throw_error("Illegal config file ~tp: ~ts",
[Filename,file:format_error(Reason)])
end;
read_config(OldSys, {sys, KeyVals}) ->
@@ -1336,7 +1341,7 @@ read_config(OldSys, {sys, KeyVals}) ->
NewSys2;
false ->
reltool_utils:throw_error(
- "Release ~p is mandatory (used as boot_rel)",
+ "Release ~tp is mandatory (used as boot_rel)",
[NewSys2#sys.boot_rel])
end;
read_config(_OldSys, BadConfig) ->
@@ -1505,7 +1510,7 @@ decode(#app{} = App, [{Key, Val} | KeyVals]) ->
active_dir = Dir,
sorted_dirs = [Dir]};
false ->
- reltool_utils:throw_error("Illegal lib dir for ~p: ~p",
+ reltool_utils:throw_error("Illegal lib dir for ~w: ~p",
[App#app.name, Val])
end;
SelectVsn when SelectVsn=:=vsn; SelectVsn=:=lib_dir ->
@@ -1637,7 +1642,7 @@ patch_erts_version(RootDir, Apps, Status) ->
end;
false ->
reltool_utils:throw_error(
- "erts cannot be found in the root directory ~p", [RootDir])
+ "erts cannot be found in the root directory ~tp", [RootDir])
end.
libs_to_dirs(RootDir, LibDirs) ->
@@ -1664,10 +1669,10 @@ libs_to_dirs(RootDir, LibDirs) ->
lists:prefix("erts", F)],
app_dirs2(AllLibDirs, [ErtsFiles]);
[Duplicate | _] ->
- reltool_utils:throw_error("Duplicate library: ~p",[Duplicate])
+ reltool_utils:throw_error("Duplicate library: ~tp",[Duplicate])
end;
{error, Reason} ->
- reltool_utils:throw_error("Missing root library ~p: ~s",
+ reltool_utils:throw_error("Missing root library ~tp: ~ts",
[RootDir,file:format_error(Reason)])
end.
@@ -1680,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}}
@@ -1693,23 +1697,80 @@ app_dirs2([Lib | Libs], Acc) ->
Files2 = lists:zf(Filter, Files),
app_dirs2(Libs, [Files2 | Acc]);
{error, Reason} ->
- reltool_utils:throw_error("Illegal library ~p: ~s",
+ reltool_utils:throw_error("Illegal library ~tp: ~ts",
[Lib, file:format_error(Reason)])
end;
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 ~tp: ~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} =
@@ -1722,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()},
@@ -1760,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 =
@@ -1774,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 ~tp: ~p",
+ [Escript,Reason2])
end;
escripts_to_apps([], Apps, Status) ->
{Apps, Status}.
@@ -1837,7 +1901,7 @@ init_escript_app(AppName, EscriptAppName, Dir, Info, Mods, Apps, Status) ->
case lists:keymember(AppName, #app.name, Apps) of
true ->
reltool_utils:throw_error(
- "~p: Application name clash. Escript ~p contains application ~p.",
+ "~w: Application name clash. Escript ~tp contains application ~tp.",
[AppName,Dir,AppName]);
false ->
{App2, Status}
@@ -1922,7 +1986,7 @@ ensure_app_info(#app{is_escript = IsEscript, active_dir = Dir, info = Info},
%% Escript or application which is inlined in an escript
{Info, Dir, Status};
ensure_app_info(#app{name = Name, sorted_dirs = []}, _Status) ->
- reltool_utils:throw_error("~p: : Missing application directory.",[Name]);
+ reltool_utils:throw_error("~w: : Missing application directory.",[Name]);
ensure_app_info(#app{name = Name,
vsn = Vsn,
use_selected_vsn = UseSelectedVsn,
@@ -1934,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,
@@ -1947,8 +2011,8 @@ ensure_app_info(#app{name = Name,
Status2;
[BadVsn | _] ->
reltool_utils:throw_error(
- "~p: Application version clash. "
- "Multiple directories contains version ~p.",
+ "~w: Application version clash. "
+ "Multiple directories contains version ~tp.",
[Name,BadVsn])
end,
FirstInfo = hd(AllInfo),
@@ -1970,8 +2034,8 @@ ensure_app_info(#app{name = Name,
{Info, VsnDir, Status3};
false ->
reltool_utils:throw_error(
- "~p: No application directory contains "
- "selected version ~p", [Name,Vsn])
+ "~w: No application directory contains "
+ "selected version ~tp", [Name,Vsn])
end
end;
true ->
diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl
index 0c0b295db1..b5d54e6d3e 100644
--- a/lib/reltool/src/reltool_sys_win.erl
+++ b/lib/reltool/src/reltool_sys_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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,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.
@@ -181,7 +181,7 @@ do_init([{safe_config, Safe}, {parent, Parent} | Options]) ->
end.
restart_server_safe_config(true,Parent,Reason) ->
- io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
+ io:format("~w(~w): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
proc_lib:init_ack(Parent, {error,Reason});
restart_server_safe_config(false,Parent,Reason) ->
wx:new(),
@@ -198,7 +198,7 @@ restart_server_safe_config(false,Parent,Reason) ->
?wxID_OK ->
do_init([{safe_config,true},{parent,Parent},?safe_config]);
?wxID_CANCEL ->
- io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
+ io:format("~w(~w): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
proc_lib:init_ack(Parent,{error,Reason})
end.
@@ -211,7 +211,7 @@ exit_dialog(Warnings) ->
?wxID_OK ->
ok;
?wxID_CANCEL ->
- io:format("~p(~p): <ERROR> ~s\n", [?MODULE, ?LINE, Details]),
+ io:format("~w(~w): <ERROR> ~ts\n", [?MODULE, ?LINE, Details]),
exit(Details)
end.
@@ -249,7 +249,7 @@ loop(S) ->
WWs2 = lists:delete(ObjRef, WWs),
?MODULE:loop(S#state{warning_wins = WWs2});
false ->
- error_logger:format("~p~p got unexpected "
+ error_logger:format("~w~w got unexpected "
"message:\n\t~p\n",
[?MODULE, self(), Msg]),
?MODULE:loop(S)
@@ -291,7 +291,7 @@ loop(S) ->
S#state.app_wins),
?MODULE:loop(S#state{fgraph_wins = FWs, app_wins = AWs});
Msg ->
- error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ error_logger:format("~w~w got unexpected message:\n\t~p\n",
[?MODULE, self(), Msg]),
?MODULE:loop(S)
end.
@@ -315,7 +315,7 @@ handle_child_exit({'EXIT', Pid, _Reason} = Exit, FWs, AWs) ->
msg_warning({'EXIT', _Pid, shutdown}, Type) when Type =/= unknown ->
ok;
msg_warning(Exit, Type) ->
- error_logger:format("~p~p got unexpected message (~p):\n\t~p\n",
+ error_logger:format("~w~w got unexpected message (~w):\n\t~p\n",
[?MODULE, self(), Type, Exit]).
create_window(S) ->
@@ -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,
@@ -1179,7 +1161,7 @@ handle_system_event(#state{sys = Sys} = S,
Sys2 = Sys#sys{incl_cond = AppCond},
do_set_sys(S#state{sys = Sys2});
handle_system_event(S, Event, ObjRef, UserData) ->
- error_logger:format("~p~p got unexpected wx sys event to ~p "
+ error_logger:format("~w~w got unexpected wx sys event to ~p "
"with user data: ~p\n\t ~p\n",
[?MODULE, self(), ObjRef, UserData, Event]),
S.
@@ -1195,13 +1177,13 @@ handle_source_event(S,
_UserData) ->
case wxTreeCtrl:getItemData(ObjRef, Item) of
#root_data{dir = _Dir} ->
- %% io:format("Root dialog: ~p\n", [Dir]),
+ %% io:format("Root dialog: ~tp\n", [Dir]),
S;
#lib_data{dir = _Dir} ->
- %% io:format("Lib dialog: ~p\n", [Dir]),
+ %% io:format("Lib dialog: ~tp\n", [Dir]),
S;
#escript_data{file = _File} ->
- %% io:format("Escript dialog: ~p\n", [File]),
+ %% io:format("Escript dialog: ~tp\n", [File]),
S;
#app_data{name = Name} ->
do_open_app(S, Name);
@@ -1221,7 +1203,7 @@ handle_source_event(S,
#escript_data{file = File} ->
wx:batch(fun() -> escript_popup(S, File, Tree, Item) end);
#app_data{name = Name} ->
- io:format("App menu: ~p\n", [Name]),
+ io:format("App menu: ~tp\n", [Name]),
S;
undefined ->
S
@@ -1241,7 +1223,7 @@ handle_app_event(S,
Items = reltool_utils:get_items(ListCtrl),
handle_app_button(S, Items, Action);
handle_app_event(S, Event, ObjRef, UserData) ->
- error_logger:format("~p~p got unexpected wx app event to "
+ error_logger:format("~w~w got unexpected wx app event to "
"~p with user data: ~p\n\t ~p\n",
[?MODULE, self(), ObjRef, UserData, Event]),
S.
@@ -1285,7 +1267,7 @@ move_app(S, {_ItemNo, AppBase}, Action) ->
blacklist_del ->
undefined;
_ ->
- error_logger:format("~p~p got unexpected app "
+ error_logger:format("~w~w got unexpected app "
"button event: ~p ~p\n",
[?MODULE, self(), Action, AppBase]),
OldApp#app.incl_cond
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index c39ed0ecd5..9cda5dabd3 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -296,7 +296,7 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps},
{ErtsName, Erts#app.vsn},
[strip_rel_info(App, RelApps) || App <- MergedApps]};
false ->
- reltool_utils:throw_error("Mandatory application ~p is "
+ reltool_utils:throw_error("Mandatory application ~w is "
"not included",
[ErtsName])
end.
@@ -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
@@ -379,8 +383,8 @@ merge_app(RelName,
[] ->
App#app{app_type = Type2, info = Info#app_info{incl_apps = InclApps}};
BadIncl ->
- reltool_utils:throw_error("~p: These applications are "
- "used by release ~s but are "
+ reltool_utils:throw_error("~w: These applications are "
+ "used by release ~ts but are "
"missing as included_applications "
"in the app file: ~p",
[Name, RelName, BadIncl])
@@ -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})
@@ -832,7 +865,7 @@ strip_sys_files(Relocatable, SysFiles, Apps, ExclRegexps) ->
case File of
"erts" ->
reltool_utils:throw_error("This system is not installed. "
- "The directory ~s is missing.",
+ "The directory ~ts is missing.",
[Erts#app.label]);
_ when File =:= Erts#app.label ->
replace_dyn_erl(Relocatable, Spec);
@@ -954,7 +987,7 @@ check_sys(Mandatory, SysFiles) ->
do_check_sys(Prefix, Specs) ->
case lookup_spec(Prefix, Specs) of
[] ->
- reltool_utils:throw_error("Mandatory system directory ~s "
+ reltool_utils:throw_error("Mandatory system directory ~ts "
"is not included",
[Prefix]);
_ ->
@@ -975,8 +1008,8 @@ lookup_spec(Prefix, Specs) ->
safe_lookup_spec(Prefix, Specs) ->
case lookup_spec(Prefix, Specs) of
[] ->
- %% io:format("lookup fail ~s:\n\t~p\n", [Prefix, Specs]),
- reltool_utils:throw_error("Mandatory system file ~s is "
+ %% io:format("lookup fail ~ts:\n\t~p\n", [Prefix, Specs]),
+ reltool_utils:throw_error("Mandatory system file ~ts is "
"not included", [Prefix]);
Match ->
Match
@@ -1020,7 +1053,7 @@ spec_lib_files(#sys{root_dir = RootDir,
check_apps([Mandatory | Names], Apps) ->
case lists:keymember(Mandatory, #app.name, Apps) of
false ->
- reltool_utils:throw_error("Mandatory application ~p is "
+ reltool_utils:throw_error("Mandatory application ~w is "
"not included in ~p",
[Mandatory, Apps]);
true ->
@@ -1111,13 +1144,13 @@ spec_dir(Dir) ->
Base,
[spec_dir(filename:join([Dir, F])) || F <- Files]};
error ->
- reltool_utils:throw_error("list dir ~s failed", [Dir])
+ reltool_utils:throw_error("list dir ~ts failed", [Dir])
end;
{ok, #file_info{type = regular}} ->
%% Plain file
{copy_file, Base};
_ ->
- reltool_utils:throw_error("read file info ~s failed", [Dir])
+ reltool_utils:throw_error("read file info ~ts failed", [Dir])
end.
spec_mod(Mod, DebugInfo) ->
@@ -1251,7 +1284,7 @@ do_eval_spec({archive, Archive, Options, Files},
{ok, _} ->
ok;
{error, Reason} ->
- reltool_utils:throw_error("create archive ~s failed: ~p",
+ reltool_utils:throw_error("create archive ~ts failed: ~p",
[ArchiveFile, Reason])
end;
do_eval_spec({copy_file, File}, _OrigSourceDir, SourceDir, TargetDir) ->
@@ -1420,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"]),
@@ -1442,9 +1473,18 @@ do_install(RelName, TargetDir) ->
ok = release_handler:create_RELEASES(TargetDir2, RelFile),
ok;
_ ->
- reltool_utils:throw_error("~s: Illegal data file syntax", [DataFile])
+ reltool_utils:throw_error("~ts: 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/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl
index 6149d6ef06..edccb889b1 100644
--- a/lib/reltool/src/reltool_utils.erl
+++ b/lib/reltool/src/reltool_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -106,7 +106,7 @@ normalize_dir([], Path) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
prim_consult(Bin) when is_binary(Bin) ->
- case erl_scan:string(binary_to_list(Bin)) of
+ case erl_scan:string(unicode:characters_to_list(Bin,encoding(Bin))) of
{ok, Tokens, _EndLine} ->
prim_parse(Tokens, []);
{error, {_ErrorLine, Module, Reason}, _EndLine} ->
@@ -120,6 +120,14 @@ prim_consult(FullName) when is_list(FullName) ->
{error, file:format_error(enoent)}
end.
+encoding(Bin) when is_binary(Bin) ->
+ case epp:read_encoding_from_binary(Bin) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end.
+
prim_parse(Tokens, Acc) ->
case lists:splitwith(fun(T) -> element(1,T) =/= dot end, Tokens) of
{[], []} ->
@@ -423,7 +431,7 @@ scroll_size(ObjRef) ->
safe_keysearch(Key, Pos, List, Mod, Line) ->
case lists:keysearch(Key, Pos, List) of
false ->
- io:format("~p(~p): lists:keysearch(~p, ~p, ~p) -> false\n",
+ io:format("~w(~w): lists:keysearch(~p, ~w, ~p) -> false\n",
[Mod, Line, Key, Pos, List]),
erlang:error({Mod, Line, lists, keysearch, [Key, Pos, List]});
{value, Val} ->
@@ -455,7 +463,7 @@ create_dir(Dir) ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("create dir ~s: ~s", [Dir, Text])
+ throw_error("create dir ~ts: ~ts", [Dir, Text])
end.
list_dir(Dir) ->
@@ -464,7 +472,7 @@ list_dir(Dir) ->
Files;
error ->
Text = file:format_error(enoent),
- throw_error("list dir ~s: ~s", [Dir, Text])
+ throw_error("list dir ~ts: ~ts", [Dir, Text])
end.
read_file_info(File) ->
@@ -473,7 +481,7 @@ read_file_info(File) ->
Info;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("read file info ~s: ~s", [File, Text])
+ throw_error("read file info ~ts: ~ts", [File, Text])
end.
write_file_info(File, Info) ->
@@ -482,7 +490,7 @@ write_file_info(File, Info) ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("write file info ~s: ~s", [File, Text])
+ throw_error("write file info ~ts: ~ts", [File, Text])
end.
read_file(File) ->
@@ -491,7 +499,7 @@ read_file(File) ->
Bin;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("read file ~s: ~s", [File, Text])
+ throw_error("read file ~ts: ~ts", [File, Text])
end.
write_file(File, IoList) ->
@@ -500,7 +508,7 @@ write_file(File, IoList) ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("write file ~s: ~s", [File, Text])
+ throw_error("write file ~ts: ~ts", [File, Text])
end.
recursive_delete(Dir) ->
@@ -516,7 +524,7 @@ recursive_delete(Dir) ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("delete file ~s: ~s\n", [Dir, Text])
+ throw_error("delete file ~ts: ~ts\n", [Dir, Text])
end;
false ->
delete(Dir, regular)
@@ -530,7 +538,7 @@ delete(File, Type) ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("delete file ~s: ~s\n", [File, Text])
+ throw_error("delete file ~ts: ~ts\n", [File, Text])
end.
do_delete(File, regular) ->
@@ -569,11 +577,11 @@ copy_file(From, To) ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
- throw_error("copy file ~s -> ~s: ~s\n", [From, To, Text])
+ throw_error("copy file ~ts -> ~ts: ~ts\n", [From, To, Text])
end;
error ->
Text = file:format_error(enoent),
- throw_error("copy file ~s -> ~s: ~s\n", [From, To, Text])
+ throw_error("copy file ~ts -> ~ts: ~ts\n", [From, To, Text])
end.
throw_error(Format, Args) ->
@@ -586,13 +594,10 @@ decode_regexps(Key, {add, Regexps}, Old) when is_list(Regexps) ->
decode_regexps(_Key, {del, Regexps}, Old) when is_list(Regexps) ->
[Re || Re <- Old, not lists:member(Re#regexp.source, Regexps)];
decode_regexps(Key, Regexps, _Old) when is_list(Regexps) ->
- do_decode_regexps(Key, Regexps, []);
-decode_regexps(Key, Regexps, _Old) when is_list(Regexps) ->
- Text = lists:flatten(io_lib:format("~p", [{Key, Regexps}])),
- throw({error, "Illegal option: " ++ Text}).
+ do_decode_regexps(Key, Regexps, []).
do_decode_regexps(Key, [Regexp | Regexps], Acc) ->
- case catch re:compile(Regexp, []) of
+ case catch re:compile(Regexp, [unicode]) of
{ok, MP} ->
do_decode_regexps(Key,
Regexps,
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index f29f6049a5..23338d9ecd 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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,6 +43,11 @@ end_per_suite(Config) ->
reltool_test_lib:end_per_suite(Config).
init_per_testcase(Func,Config) ->
+ Node = full_node_name(?NODE_NAME),
+ case net_adm:ping(Node) of
+ pong -> stop_node(Node);
+ pang -> ok
+ end,
reltool_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
reltool_test_lib:end_per_testcase(Func,Config).
@@ -60,6 +66,7 @@ all() ->
create_script,
create_script_sort,
create_target,
+ create_target_unicode,
create_embedded,
create_standalone,
create_standalone_beam,
@@ -90,8 +97,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() ->
[].
@@ -106,6 +115,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) ->
@@ -298,7 +316,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
@@ -307,11 +324,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]
@@ -326,11 +344,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}]},
@@ -372,7 +391,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, _},
@@ -389,13 +407,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},
@@ -407,7 +441,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,_},
@@ -417,6 +450,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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -477,12 +521,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,
[
@@ -493,10 +541,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}]},
@@ -553,8 +602,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),
@@ -569,6 +618,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]))),
@@ -584,7 +637,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},
@@ -595,6 +652,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]),
@@ -626,26 +691,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.
@@ -687,6 +757,72 @@ create_target(_Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Generate target system
+
+create_target_unicode(Config) ->
+ DataDir = ?config(data_dir,Config),
+
+ %% If file name translation mode is unicode, then use unicode
+ %% characters release name (which will be used as file name for
+ %% .rel, .script and .boot)
+ RelNamePrefix =
+ case file:native_name_encoding() of
+ utf8 ->
+ "Unicode test αβ";
+ latin1 ->
+ "Unicode test"
+ end,
+
+ %% Configure the server
+ RelName1 = RelNamePrefix,
+ RelName2 = RelNamePrefix ++ " with SASL",
+ RelVsn = "1.0",
+ Sys =
+ {sys,
+ [
+ {root_dir, code:root_dir()},
+ {lib_dirs, [filename:join(DataDir,"unicode")]},
+ {app_file, all},
+ {incl_cond,exclude},
+ {boot_rel, RelName2},
+ {rel, RelName1, RelVsn, [stdlib, kernel, ua]},
+ {rel, RelName2, RelVsn, [sasl, stdlib, kernel, ua]},
+ {app, kernel, [{incl_cond, include}]},
+ {app, stdlib, [{incl_cond, include}]},
+ {app, sasl, [{incl_cond, include}]},
+ {app, ua, [{incl_cond, include}]}
+ ]},
+
+ %% Generate target file
+ TargetDir = filename:join([?WORK_DIR, "target_unicode"]),
+ ?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)),
+
+ %% Start a node
+ Erl = filename:join([TargetDir, "bin", "erl"]),
+ {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
+
+
+ %% The ua application has a unicode string as description - check
+ %% that it is translated correctly.
+ wait_for_app(Node,ua,50),
+ Apps = rpc:call(Node,application,which_applications,[]),
+ ?m({ua,"Application for testing unicode in reltool - αβ","1.0"},
+ lists:keyfind(ua,1,Apps)),
+
+ %% Check that the release name is correct (really only
+ %% insteresting if file name translation mode is utf8)
+ [{RelName,_,_,_}] =
+ ?msym([{_,_,_,_}],rpc:call(Node,release_handler,which_releases,[])),
+ ?m(true,lists:prefix(RelNamePrefix,RelName)),
+
+ ?msym(ok, stop_node(Node)),
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate embedded target system
create_embedded(_Config) ->
@@ -747,11 +883,11 @@ create_standalone(_Config) ->
?msym(ok, stop_node(Node)),
%% Execute escript
- Expected = iolist_to_binary(["Root dir: ", RootDir, "\n"
- "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
- "Smp: false\n",
- "ExitCode:0"]),
- io:format("Expected: ~s\n", [Expected]),
+ Expected = s2b(["Root dir: ", RootDir, "\n"
+ "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
+ "Emuarg: [\"emuvalue\"]\n",
+ "ExitCode:0"]),
+ io:format("Expected: ~ts\n", [Expected]),
?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")),
ok.
@@ -794,10 +930,11 @@ create_standalone_beam(Config) ->
?msym(ok, stop_node(Node)),
%% Execute escript
- Expected = iolist_to_binary(["Root dir: ", RootDir, "\n"
- "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
- "ExitCode:0"]),
- io:format("Expected: ~s\n", [Expected]),
+ Expected = s2b(["Module: mymod\n"
+ "Root dir: ", RootDir, "\n"
+ "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
+ "ExitCode:0"]),
+ io:format("Expected: ~ts\n", [Expected]),
?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")),
ok.
@@ -846,10 +983,11 @@ create_standalone_app(Config) ->
?msym(ok, stop_node(Node)),
%% Execute escript
- Expected = iolist_to_binary(["Root dir: ", RootDir, "\n"
- "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
- "ExitCode:0"]),
- io:format("Expected: ~s\n", [Expected]),
+ Expected = s2b(["Module: mymod\n"
+ "Root dir: ", RootDir, "\n"
+ "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
+ "ExitCode:0"]),
+ io:format("Expected: ~ts\n", [Expected]),
?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")),
ok.
@@ -932,27 +1070,26 @@ create_multiple_standalone(Config) ->
?msym(ok, stop_node(Node)),
%% Execute escript1
- Expected1 = iolist_to_binary(["Root dir: ", RootDir, "\n"
- "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
- "Smp: false\n",
- "ExitCode:0"]),
- io:format("Expected1: ~s\n", [Expected1]),
+ Expected1 = s2b(["Root dir: ", RootDir, "\n"
+ "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
+ "Emuarg: [\"emuvalue\"]\n",
+ "ExitCode:0"]),
+ io:format("Expected1: ~ts\n", [Expected1]),
?m(Expected1, run(BinDir, EscriptName1, "-arg1 arg2 arg3")),
%% Execute escript2
- Expected2 = iolist_to_binary(["Root dir: ", RootDir, "\n"
- "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
- "ExitCode:0"]),
- io:format("Expected2: ~s\n", [Expected2]),
+ Expected2 = s2b(["Module: mymod\n"
+ "Root dir: ", RootDir, "\n"
+ "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
+ "ExitCode:0"]),
+ io:format("Expected2: ~ts\n", [Expected2]),
?m(Expected2, run(BinDir, EscriptName2, "-arg1 arg2 arg3")),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate old type of target system
-
-create_old_target(_Config) -> {skip, "Old style of target"};
create_old_target(_Config) ->
%% Configure the server
@@ -975,8 +1112,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"]),
@@ -1024,11 +1160,17 @@ create_slim(Config) ->
RootDir = code:root_dir(),
Erl = filename:join([RootDir, "bin", "erl"]),
- Args = "-boot_var RELTOOL_EXT_LIB " ++ TargetLibDir ++
- " -boot " ++ filename:join(TargetRelVsnDir,RelName) ++
- " -sasl releases_dir \\\"" ++ TargetRelDir ++ "\\\"",
+ 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, [])),
+ wait_for_app(Node,sasl,50),
?msym([{RelName,RelVsn,_,permanent}],
rpc:call(Node,release_handler,which_releases,[])),
?msym(ok, stop_node(Node)),
@@ -1942,7 +2084,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
%%
@@ -2072,6 +2214,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"),
@@ -2196,6 +2379,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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2278,11 +2494,13 @@ mod_path(Node,Mod) ->
start_node(Name, ErlPath) ->
start_node(Name, ErlPath, []).
-start_node(Name, ErlPath, Args) ->
+start_node(Name, ErlPath, Args0) ->
FullName = full_node_name(Name),
- CmdLine = mk_node_cmdline(Name, ErlPath, Args),
- 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: ~ts~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),
@@ -2295,27 +2513,35 @@ start_node(Name, ErlPath, Args) ->
end.
stop_node(Node) ->
- monitor_node(Node, true),
- spawn(Node, fun () -> halt() end),
- receive {nodedown, Node} -> ok end.
+ rpc:call(Node,erlang,halt,[]),
+ wait_for_node_down(Node,50).
+
+wait_for_node_down(Node,0) ->
+ test_server:fail({cant_terminate_node,Node});
+wait_for_node_down(Node,N) ->
+ case net_adm:ping(Node) of
+ pong ->
+ timer:sleep(1000),
+ wait_for_node_down(Node,N-1);
+ pang ->
+ ok
+ end.
-mk_node_cmdline(Name, Prog, Args) ->
- 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())
- ++ " " ++ Args.
+ ["-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,
@@ -2346,6 +2572,22 @@ wait_for_process(Node, Name, N) when is_integer(N), N > 0 ->
ok
end.
+wait_for_app(_Node, Name, 0) ->
+ {error, Name};
+wait_for_app(Node, Name, N) when is_integer(N), N > 0 ->
+ case rpc:call(Node,application,which_applications,[]) of
+ {badrpc,Reason} ->
+ test_server:fail({failed_to_get_applications,Reason});
+ Apps ->
+ case lists:member(Name,Apps) of
+ false ->
+ timer:sleep(1000),
+ wait_for_app(Node, Name, N-1);
+ true ->
+ ok
+ end
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Run escript
@@ -2374,7 +2616,7 @@ do_run(Dir, Cmd) ->
Res = get_data(Port, []),
receive
{Port,{exit_status,ExitCode}} ->
- iolist_to_binary([Res,"ExitCode:"++integer_to_list(ExitCode)])
+ s2b([Res,"ExitCode:"++integer_to_list(ExitCode)])
end.
get_data(Port, SoFar) ->
@@ -2398,3 +2640,9 @@ expected_output([], _) ->
[];
expected_output(Bin, _) when is_binary(Bin) ->
Bin.
+
+%% Convert the given list to a binary with the same encoding as the
+%% file name translation mode
+s2b(List) ->
+ Enc = file:native_name_encoding(),
+ unicode:characters_to_binary(List,Enc,Enc).
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/escript/someapp-1.0/src/mymod.erl b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl
index b6c71c666d..c315f926d3 100644
--- a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl
+++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl
@@ -22,5 +22,6 @@
%%%-----------------------------------------------------------------
%%% escript main function
main(Args) ->
+ io:format("Module: ~w\n", [?MODULE]),
io:format("Root dir: ~s\n", [code:root_dir()]),
io:format("Script args: ~p\n", [Args]).
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/reltool/test/reltool_server_SUITE_data/unicode/ua-1.0/ebin/ua.app b/lib/reltool/test/reltool_server_SUITE_data/unicode/ua-1.0/ebin/ua.app
new file mode 100644
index 0000000000..6f571b7179
--- /dev/null
+++ b/lib/reltool/test/reltool_server_SUITE_data/unicode/ua-1.0/ebin/ua.app
@@ -0,0 +1,7 @@
+% -*- coding: utf-8 -*-
+{application, ua,
+ [{description, "Application for testing unicode in reltool - αβ"},
+ {vsn, "1.0"},
+ {modules,[]},
+ {registered, []},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index 6872539b8c..9df2fc8406 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1 +1 @@
-RELTOOL_VSN = 0.6.1
+RELTOOL_VSN = 0.6.3
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/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c
index 6b77128761..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
@@ -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..62f145f0a5 100644
--- a/lib/runtime_tools/doc/src/book.xml
+++ b/lib/runtime_tools/doc/src/book.xml
@@ -4,7 +4,7 @@
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<header titlestyle="normal">
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 90641719c5..cd59be1e63 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,59 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.8.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix Table Viewer refresh crash on no more existing ets
+ tables (Thanks to Peti G�mori)</p>
+ <p>
+ Own Id: OTP-10635</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ User Guides for the dynamic tracing tools dtrace and
+ systemtap have been added to the documentation.</p>
+ <p>
+ Own Id: OTP-10155</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ The backend module appmon_info.erl is moved from appmon
+ application to runtime_tools. This allows appmon to be
+ run from a remote erlang node towards a target node which
+ does not have appmon (and its dependencies) installed, as
+ long as runtime_tools is installed there.</p>
+ <p>
+ Own Id: OTP-10786</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.8.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/inviso/doc/src/part.xml b/lib/runtime_tools/doc/src/part.xml
index 08ced28f47..948d4a8020 100644
--- a/lib/inviso/doc/src/part.xml
+++ b/lib/runtime_tools/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2012</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,15 +21,22 @@
</legalnotice>
- <title>Inviso User's Guide</title>
- <prepared></prepared>
+ <title>Runtime Tools User's Guide</title>
+ <prepared>Lukas Larsson</prepared>
<docno></docno>
- <date></date>
+ <date>2012-07-18</date>
<rev></rev>
+ <file>part.xml</file>
</header>
+
<description>
- <p><em>Inviso</em>, an Erlang trace tool.</p>
+ <p><em>Runtime Tools</em></p>
</description>
- <xi:include href="inviso_chapter.xml"/>
+
+ <xi:include href="DTRACE.xml"/>
+ <xi:include href="SYSTEMTAP.xml"/>
</part>
+
+
+
diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile
index 810e3e8741..2347986c53 100644
--- a/lib/runtime_tools/src/Makefile
+++ b/lib/runtime_tools/src/Makefile
@@ -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
@@ -35,13 +35,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN)
# ----------------------------------------------------
MODULES= \
+ appmon_info \
erts_alloc_config \
- inviso_rt \
- inviso_rt_meta \
- inviso_rt_lib \
- inviso_as_lib \
- inviso_autostart \
- inviso_autostart_server \
runtime_tools \
runtime_tools_sup \
dbg \
@@ -73,7 +68,8 @@ EXAMPLE_FILES= \
ERL_COMPILE_FLAGS += \
-I../include \
-I ../../et/include \
- -I ../../../libraries/et/include
+ -I ../../../libraries/et/include \
+ -Werror
# ----------------------------------------------------
# Targets
@@ -86,10 +82,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..a728312c97 100644
--- a/lib/appmon/src/appmon_info.erl
+++ b/lib/runtime_tools/src/appmon_info.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. 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
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 385047ee73..6e3bfe31c6 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.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
@@ -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 b4579fd5ce..f7dbef6929 100644
--- a/lib/runtime_tools/src/dyntrace.erl
+++ b/lib/runtime_tools/src/dyntrace.erl
@@ -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,7 +115,7 @@ user_trace_s1(_Message) ->
user_trace_i4s4(_, _, _, _, _, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
--spec user_trace_n(n_probe_label(), iolist(),
+-spec user_trace_n(n_probe_label(), binary() | undefined,
integer_maybe(), integer_maybe(),
integer_maybe(), integer_maybe(),
iolist_maybe(), iolist_maybe(),
diff --git a/lib/runtime_tools/src/inviso_as_lib.erl b/lib/runtime_tools/src/inviso_as_lib.erl
deleted file mode 100644
index 75f3d9d004..0000000000
--- a/lib/runtime_tools/src/inviso_as_lib.erl
+++ /dev/null
@@ -1,155 +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%
-%%
-
-%%% File : inviso_as_lib.erl
-%%% Author : Lennart �hman <[email protected]>
-%%% Description :
-%% The purpose of the inviso autostart library is to provide useful functions
-%% for anyone wanting to customize the autostart mechanism in the inviso
-%% tracer. It is intended to work well with the example 'inviso_autostart_server'.
-%%%
-%%% Created : 15 Dec 2005 by Lennart �hman
-%% -----------------------------------------------------------------------------
-
--module(inviso_as_lib).
-
--export([setup_autostart/7,setup_autostart/8,setup_autostart/9,
- inhibit_autostart/1,
- set_repeat/2,set_repeat_2/2]).
-%% -----------------------------------------------------------------------------
-
-%% setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings) = ok|{error,Reason}.
-%% Repeat=integer(), where 0 means no (more) autostarts.
-%% Options=List of options as taken by the runtime component at start-up.
-%% TracerData= Tracerdata as given to inviso_rt:init_tracing.
-%% CmdFiles=[FileName,...] list of string(), files that will be executed
-%% by the subprocess started during autostart.
-%% Bindings=[{VarName,Value},...] Variable bindings for CmdFiles.
-%% VarName=atom(),
-%%
-%% This function creates the inviso_autostart.config file on Erlang node Node.
-%% This is useful when you wish to prepare for an autostarted trace.
-setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations) ->
- setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,
- Bindings,Translations,inviso_std_ref,off).
-setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations,RTtag) ->
- setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,
- Bindings,Translations,RTtag,off).
-setup_autostart(Node,Repeat,Options,TracerData,CmdFiles,Bindings,Translations,RTtag,Dbg) ->
- case rpc:call(Node,inviso_autostart,which_config_file,[]) of
- FileName when is_list(FileName) -> % Write to this file then.
- {String,Args}=format_config_file(Repeat,TracerData,Options,CmdFiles,
- Bindings,Translations,RTtag,Dbg),
- Bytes=list_to_binary(io_lib:format(String,Args)),
- case rpc:call(Node,file,write_file,[FileName,Bytes]) of
- ok ->
- ok;
- {error,Reason} ->
- {error,{write_file,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{write_file,Reason}}}
- end;
- {error,Reason} ->
- {error,{which_config_file,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{which_config_file,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% inhibit_autostart(Node) = ok|{error,Reason}
-%%
-%% Inhibits autostart by simply making the repeat parameter zero in the
-%% configuration file at node Node. All other parameters are left untouched.
-inhibit_autostart(Node) ->
- set_repeat(Node,0).
-%% -----------------------------------------------------------------------------
-
-%% set_repeat(Node,N)=ok | {error,Reason}
-%% N=integer(), the number of time autostart shall be allowed.
-set_repeat(Node,N) ->
- case examine_config_file(Node) of
- {ok,FileName,Terms} ->
- NewTerms=[{repeat,N}|lists:keydelete(repeat,1,Terms)],
- case rpc:call(Node,?MODULE,set_repeat_2,[FileName,NewTerms]) of
- {badrpc,Reason} ->
- {error,{badrpc,{open,Reason}}};
- Result ->
- Result
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Must be a sepparate function to do rpc on. The entire function must be done
-%% in one rpc call. Otherwise the FD will die since it is linked to the opening
-%% process.
-set_repeat_2(FileName,NewTerms) ->
- case file:open(FileName,[write]) of
- {ok,FD} ->
- String=lists:flatten(lists:map(fun(_)->"~w.~n" end,NewTerms)),
- case catch io:format(FD,String,NewTerms) of
- ok ->
- file:close(FD),
- ok;
- {'EXIT',Reason} ->
- file:close(FD),
- {error,{format,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-examine_config_file(Node) ->
- case rpc:call(Node,inviso_autostart,which_config_file,[]) of
- FileName when is_list(FileName) -> % Read this file, and then modify it.
- case rpc:call(Node,file,consult,[FileName]) of
- {ok,Terms} ->
- {ok,FileName,Terms};
- {error,Reason} ->
- {error,{consult,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{consult,Reason}}}
- end;
- {error,Reason} ->
- {error,{which_config_file,Reason}};
- {badrpc,Reason} ->
- {error,{badrpc,{which_config_file,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-format_config_file(Repeat,TracerData,Options,CmdFiles,Bindings,Translations,RTtag,Dbg) ->
- String="~w.~n~w.~n~w.~n~w.~n",
- Args=[{repeat,Repeat},
- {mfa,{inviso_autostart_server,init,[[{tracerdata,TracerData},
- {cmdfiles,CmdFiles},
- {bindings,Bindings},
- {translations,Translations},
- {debug,Dbg}]]}},
- {options,Options},
- {tag,RTtag}],
- {String,Args}.
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-
diff --git a/lib/runtime_tools/src/inviso_autostart.erl b/lib/runtime_tools/src/inviso_autostart.erl
deleted file mode 100644
index 787292e244..0000000000
--- a/lib/runtime_tools/src/inviso_autostart.erl
+++ /dev/null
@@ -1,201 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Author: Lennart �hman, [email protected]
--module(inviso_autostart).
-
--export([autostart/1,which_config_file/0]).
-
-%% This module implements the default autostart module for the inviso runtime
-%% component.
-%% It will:
-%% (1) Open the autostart configuration file (either the default or the one
-%% pointed out by the runtime_tools application parameter inviso_autostart_config).
-%% (2) Check that the incarnation counter has not reached 0. If so, we do not
-%% allow (yet) one autostart.
-%% (3) Rewrite the configuration file if there was an incarnation counter.
-%% (With the counter decreased).
-%% (4) Inspect the content of the configuration file and pass paramters in the
-%% return value (which is interpreted by the runtime component).
-%%
-%% CONTENT OF A CONFIGURATION FILE:
-%% A plain text file containing erlang tuple terms, each ended with a period(.).
-%% The following parameters are recognized:
-%% {repeat,N} N=interger(),
-%% The number of remaining allowed autostart incarnations of inviso.
-%% {options,Options} Options=list()
-%% The options which controls the runtime component, such as overload and
-%% dependency.
-%% {mfa,{Mod,Func,Args}} Args=list()
-%% Controls how a spy process initiating tracing, patterns and flags shall
-%% be started.
-%% {tag,Tag}
-%% The tag identifying the runtime component to control components.
-%% =============================================================================
-
-%% This function is run in the runtime component's context during autostart
-%% to determine whether to continue and if, then how.
-autostart(_AutoModArgs) ->
- ConfigFile=
- case application:get_env(inviso_autostart_conf) of
- {ok,FileName} when is_list(FileName) -> % Use this filename then.
- FileName;
- {ok,{load,FileNames,{M,F}}} -> % First load the module, then...
- case try_load_module(FileNames) of
- ok ->
- autostart_apply(M,F);
-
- false -> % No such module available
- "inviso_autostart.config"
- end;
- {ok,{M,F}} -> % Use M:F(node())
- autostart_apply(M,F);
- {ok,no_autostart} ->
- false;
- _ -> % Use a default name, in CWD!
- "inviso_autostart.config"
- end,
- if
- is_list(ConfigFile) ->
- case file:consult(ConfigFile) of
- {ok,Terms} -> % There is a configuration.
- case handle_repeat(ConfigFile,Terms) of
- ok -> % Handled or not, we shall continue.
- {get_mfa(Terms),get_options(Terms),get_tag(Terms)};
- stop -> % We are out of allowed starts.
- true % Then no autostart.
- end;
- {error,_} -> % There is no config file
- true % Then no autostart!
- end;
- true -> % Skip it then.
- true
- end.
-
-autostart_apply(M,F) ->
- case catch M:F(node()) of
- FileName when is_list(FileName) ->
- FileName;
- no_autostart -> % No autostart after all.
- false;
- _ ->
- "inviso_autostart.config"
- end.
-
-%% This function is necessary since it is not always the case that all code-paths
-%% are set at the time of an autostart.
-try_load_module([AbsFileName|Rest]) when is_list(AbsFileName) ->
- case catch code:load_abs(AbsFileName) of % May not be a proper filename.
- {module,_Mod} ->
- try_load_module(Rest);
- _ ->
- false
- end;
-try_load_module([]) -> % Load all beam files successfully.
- ok;
-try_load_module(AbsFileName) when is_list(AbsFileName) ->
- try_load_module([AbsFileName]).
-%% -----------------------------------------------------------------------------
-
-%% Function returning the filename probably used as autostart config file.
-%% Note that this function must be executed at the node in question.
-which_config_file() ->
- case application:get_env(runtime_tools,inviso_autostart_conf) of
- {ok,FileName} when is_list(FileName) -> % Use this filename then.
- FileName;
- {ok,{M,F}} -> % Use M:F(node())
- case catch M:F(node()) of
- FileName when is_list(FileName) ->
- FileName;
- _ ->
- {ok,CWD}=file:get_cwd(),
- filename:join(CWD,"inviso_autostart.config")
- end;
- _ -> % Use a default name, in CWD!
- {ok,CWD}=file:get_cwd(),
- filename:join(CWD,"inviso_autostart.config")
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which finds out if there is a limit on the number of times
-%% we shall autostart. If there is a repeat parameter and it is greater than
-%% zero, the file must be rewritten with the parameter decreased with one.
-%% Returns 'ok' or 'stop'.
-handle_repeat(FileName,Terms) ->
- case lists:keysearch(repeat,1,Terms) of
- {value,{_,N}} when N>0 -> % Controlls how many time more.
- handle_repeat_rewritefile(FileName,Terms,N-1),
- ok; % Indicate that we shall continue.
- {value,_} -> % No we have reached the limit.
- stop;
- false -> % There is no repeat parameter.
- ok % No restrictions then!
- end.
-
-%% Help function which writes the configuration file again, but with the
-%% repeat parameter set to NewN.
-%% Returns nothing significant.
-handle_repeat_rewritefile(FileName,Term,NewN) ->
- case file:open(FileName,[write]) of
- {ok,FD} ->
- NewTerm=lists:keyreplace(repeat,1,Term,{repeat,NewN}),
- handle_repeat_rewritefile_2(FD,NewTerm),
- file:close(FD);
- {error,_Reason} -> % Not much we can do then?!
- error
- end.
-
-handle_repeat_rewritefile_2(FD,[Tuple|Rest]) ->
- io:format(FD,"~w.~n",[Tuple]),
- handle_repeat_rewritefile_2(FD,Rest);
-handle_repeat_rewritefile_2(_,[]) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% Three help functions finding the parameters possible to give to the runtime
-%% component. Note that some of them have default values, should the parameter
-%% not exist.
-get_mfa(Terms) ->
- case lists:keysearch(mfa,1,Terms) of
- {value,{_,MFA}} ->
- MFA;
- false ->
- false
- end.
-
-get_options(Terms) ->
- case lists:keysearch(options,1,Terms) of
- {value,{_,Options}} ->
- Options;
- false ->
- []
- end.
-
-get_tag(Terms) ->
- case lists:keysearch(tag,1,Terms) of
- {value,{_,Tag}} ->
- Tag;
- false ->
- default_tag
- end.
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
diff --git a/lib/runtime_tools/src/inviso_autostart_server.erl b/lib/runtime_tools/src/inviso_autostart_server.erl
deleted file mode 100644
index 1e352822f4..0000000000
--- a/lib/runtime_tools/src/inviso_autostart_server.erl
+++ /dev/null
@@ -1,311 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%% Author: Lennart �hman, [email protected]
-%%
--module(inviso_autostart_server).
--export([init/1]).
-
-%% -----------------------------------------------------------------------------
-%% Internal exports
-%% -----------------------------------------------------------------------------
--export([cmd_file_interpreter_init/4]).
-%% -----------------------------------------------------------------------------
-
-
-%% This module provides a (well working) example of how to program an
-%% autostart server responsible for initializing trace, setting patterns
-%% and flags.
-%%
-%% The general idea is that this code spawns interpreter processes in order to
-%% execute commands concurrently. Each of the interpreter processes opens one or
-%% several files (in sequence) containing erlang function calls which are evaluated
-%% in the interpreter process context.
-%% The argument provided to init shall be a list of options controlling
-%% how to initialize tracing, which file(s) to open and variable bindings.
-%%
-%% This autostart_server interpreters understands standard inviso trace case files.
-%%
-%% The runtime component provides an API very similar to the API provided
-%% by the control component. It is therefore easy to translate inviso calls to
-%% inviso_rt calls.
-%%
-%% This process may be killed by the inviso_rt process if stop_tracing is called.
-%% The reason is that there is no time limit to the interpreter processes. Hence
-%% they should be killed if tracing is not possible anylonger.
-%% =============================================================================
-
-
-%% -----------------------------------------------------------------------------
-
-%% The independent autostart process spawned by the runtime component to carry
-%% out initializations is spawened on this function (if using the example
-%% autostart which comes with inviso).
-%% ArgsFromConfig is as can be heard from the name comming from a paramater in
-%% the autostart configuration file. Here it is supposed to be:
-%% ArgsFromConfig=[ServerParam,...]
-%% ServerParam={tracerdata,TracerData}|{cmdfiles,Files}|{bindings,Bindings}|
-%% {translations,Translations}|{debug,DbgLevel}
-%% TracerData=tracerdata given to inviso_rt:init_tracing/1 function.
-%% Files=[FileNameSpecs,...] where each FileNameSpecs will be executed in
-%% a separate process. Making each FileNameSpec parallel.
-%% FileNameSpecs=[FileNameSpec,...]
-%% FileNameSpec=FileName | {FileName,Bindings}
-%% Bindings=[{Var,Value},...] variable environment understood by
-%% erl_eval:exprs/2.
-%% Translations=[Translation,...]
-%% A translation file is a text-file with following tuples
-%% Translation={{Mod,Func,Arity,{Mod2,Func2,ParamMF}}}|
-%% {{Func,Arity,{Mod2,Func2,ParamMF}}}
-%% ParamMF={M,F} | any()
-%% Translates Mod:Func/Arity to Mod2:Func2 with the arguments to
-%% Mod:Func translated using M:F/1. Note that ParamMF is not
-%% necessarily an MF. If no translation shall be done, ParamMF
-%% shall be anything else but an MF.
-%% Also note that Mod is optional in a Translation. That means that
-%% function calls without a module in the trace case file will
-%% be translated according to that translation.
-init(ArgsFromConfig) ->
- case get_tracerdata_opts(ArgsFromConfig) of
- {ok,TracerData} -> % Otherwise we can not start a trace!
- case inviso_rt:init_tracing(TracerData) of
- {ok,_Response} -> % Ok, tracing has been initiated.
- case get_cmdfiles_opts(ArgsFromConfig) of
- {ok,CmdFiles} -> % List of cmd-files.
- Bindings=get_initialbindings_opts(ArgsFromConfig),
- Translations=get_translations_opts(ArgsFromConfig),
- Dbg=get_dbg_opts(ArgsFromConfig),
- Procs=start_cmd_file_interpreters(CmdFiles,
- Bindings,
- Translations,
- Dbg),
- loop(Procs,Dbg); % Wait for procs to be done.
- false -> % Then we can terminate normally.
- true
- end;
- {error,Reason} -> % This is fault, lets terminate abnormally.
- exit({inviso,{error,Reason}})
- end;
- false -> % Then there is not much use then.
- true % Just terminate normally.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a process for each item found in the FileNames
-%% list. The idea is that each item will be processed concurrently. The items
-%% them selves may be a sequence of filenames.
-%% Returns a list of spawned interpret processes.
-start_cmd_file_interpreters([FileNames|Rest],Bindings,Translations,Dbg) ->
- P=spawn_link(?MODULE,cmd_file_interpreter_init,[FileNames,Bindings,Translations,Dbg]),
- MRef=erlang:monitor(process,P), % Can't trap exits in this process.
- [{P,MRef}|start_cmd_file_interpreters(Rest,Bindings,Translations,Dbg)];
-start_cmd_file_interpreters([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% The loop where this process simply waits for all of the interpreters to be
-%% done. Note that that may take som time. An interpreter may take as long time
-%% necessary to do its task.
-loop(Procs,Dbg) ->
- receive
- {'DOWN',MRef,process,Pid,_Reason} ->
- case lists:keysearch(MRef,1,Procs) of
- {value,{Pid,_}} -> % It was an interpreter that terminated.
- case lists:keydelete(MRef,1,Procs) of
- [] -> % No more interpreters.
- true; % Then terminate.
- NewProcs ->
- loop(NewProcs,Dbg)
- end;
- false ->
- loop(Procs,Dbg)
- end;
- _ ->
- loop(Procs,Dbg)
- end.
-
-
-%% -----------------------------------------------------------------------------
-%% The interpret process.
-%%
-%% An interpreter process executes trace case files. Several interpreter processes
-%% may be running in parallel. It is not within the scoop of this implementation
-%% of an autostart server to solve conflicts. (You may implement your own autostart
-%% server!).
-%% An interpret process may run for as long as necessary. Hence the function called
-%% within the trace case file can contain wait functions, waiting for a certain
-%% system state to occure before continuing.
-%% Note that this process also mixes global and local bindings. GlobalBindings
-%% is a binding() structure, where LocalBindings is a list of {Var,Value}.
-%% Further it is possible to let FileName be a {inviso,Func,Args} tuple instead.
-%% -----------------------------------------------------------------------------
-
-%% Init function for an interpreter process instance.
-cmd_file_interpreter_init(FileNames,GlobalBindings,Translations,Dbg) ->
- interpret_cmd_files(FileNames,GlobalBindings,Translations,Dbg).
-
-interpret_cmd_files([{FileName,LocalBindings}|Rest],GlobalBindings,Translations,Dbg) ->
- Bindings=join_local_and_global_vars(LocalBindings,GlobalBindings),
- interpret_cmd_files_1(FileName,Bindings,Translations,Dbg),
- interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg);
-interpret_cmd_files([],_,_,_) -> % Done, return nothing significant!
- true;
-interpret_cmd_files(FileName,GlobalBindings,Translations,Dbg) ->
- interpret_cmd_files_1(FileName,GlobalBindings,Translations,Dbg).
-% interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg).
-
-%% This is "inline" inviso calls.
-interpret_cmd_files_1({inviso,F,Args},Bindings,Translations,Dbg) ->
- {ok,Tokens1,_}=erl_scan:string("inviso:"++atom_to_list(F)++"("),
- Tokens2=tokenize_args(Args),
- {ok,Tokens3,_}=erl_scan:string(")."),
- case erl_parse:parse_exprs(Tokens1++Tokens2++Tokens3) of
- {ok,Exprs} ->
- interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg);
- {error,_Reason} ->
- error
- end;
-interpret_cmd_files_1({Mod,Func,Args},_Bindings,_Translations,_Dbg) ->
- catch apply(Mod,Func,Args);
-%% This is the case when it actually is a trace case file.
-interpret_cmd_files_1(FileName,Bindings,Translations,Dbg) ->
- case file:open(FileName,[read]) of
- {ok,FD} ->
- interpret_cmd_files_2(FD,Bindings,io:parse_erl_exprs(FD,""),Translations,Dbg),
- file:close(FD);
- {error,Reason} -> % Something wrong with the file.
- inviso_rt_lib:debug(Dbg,interpret_cmd_files,[FileName,{error,Reason}])
- end.
-
-%% Help function which handles Exprs returned from io:parse_erl_exprs and
-%% tries to eval them. It is the side-effects we are interested in, like
-%% setting flags and patterns. Note that we will get a failure should there
-%% be a variable conflict.
-%% Also note that there is logic to translate control component API calls to
-%% corresponding runtime component calls.
-%% Returns nothing significant.
-interpret_cmd_files_2(FD,Bindings,{ok,Exprs,_},Translations,Dbg) ->
- {next,NewBindings}=interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg),
- interpret_cmd_files_2(FD,NewBindings,io:parse_erl_exprs(FD,""),Translations,Dbg);
-interpret_cmd_files_2(FD,Bindings,{error,ErrorInfo,Line},Translations,Dbg) ->
- inviso_rt_lib:debug(Dbg,parse_erl_exprs,[ErrorInfo,Line]),
- interpret_cmd_files_2(FD,Bindings,io:parse_erl_exprs(FD,""),Translations,Dbg);
-interpret_cmd_files_2(_,_,{eof,_},_,_) -> % End of file.
- true.
-
-interpret_cmd_files_3(Bindings,Exprs,Translations,Dbg) ->
- case catch inviso_rt_lib:transform(Exprs,Translations) of
- NewExprs when is_list(NewExprs) -> % We may have translated the API.
- case catch erl_eval:exprs(NewExprs,Bindings) of
- {'EXIT',Reason} ->
- inviso_rt_lib:debug(Dbg,exprs,[Exprs,Bindings,{'EXIT',Reason}]),
- {next,Bindings};
- {value,_Val,NewBindings} -> % Only interested in the side effects!
- {next,NewBindings}
- end;
- {'EXIT',Reason} ->
- inviso_rt_lib:debug(Dbg,translate2runtime_funcs,[Exprs,Reason]),
- {next,Bindings}
- end.
-
-%% Help function adding variables to a bindings structure. If the variable already
-%% is assigned in the structure, it will be overridden. Returns a new
-%% bindings structure.
-join_local_and_global_vars([{Var,Val}|Rest],Bindings) when is_atom(Var) ->
- join_local_and_global_vars(Rest,erl_eval:add_binding(Var,Val,Bindings));
-join_local_and_global_vars([_|Rest],Bindings) ->
- join_local_and_global_vars(Rest,Bindings);
-join_local_and_global_vars([],Bindings) ->
- Bindings.
-
-%% Help function returning a string of tokens, including "," separation
-%% between the arguments.
-tokenize_args(Args=[Arg|Rest]) when length(Args)>1 ->
- AbsTerm=erl_parse:abstract(Arg),
- Tokens=erl_parse:tokens(AbsTerm),
- {ok,Token,_}=erl_scan:string(","),
- Tokens++Token++tokenize_args(Rest);
-tokenize_args([Arg]) ->
- AbsTerm=erl_parse:abstract(Arg),
- erl_parse:tokens(AbsTerm);
-tokenize_args([]) ->
- "".
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Help functions working on the options given as argument to init during spawn.
-%% -----------------------------------------------------------------------------
-
-get_tracerdata_opts(ArgsFromConfig) ->
- case lists:keysearch(tracerdata,1,ArgsFromConfig) of
- {value,{_,{mfa,{M,F,CompleteTDGargs}}}} -> % Dynamic tracerdata.
- case catch apply(M,F,CompleteTDGargs) of
- {'EXIT',_Reason} ->
- false;
- TracerData ->
- {ok,TracerData}
- end;
- {value,{_,TracerData}} -> % Interpret this as static tracerdata.
- {ok,TracerData};
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_cmdfiles_opts(ArgsFromConfig) ->
- case lists:keysearch(cmdfiles,1,ArgsFromConfig) of
- {value,{_,CmdFiles}} ->
- {ok,CmdFiles};
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_initialbindings_opts(ArgsFromConfig) ->
- case lists:keysearch(bindings,1,ArgsFromConfig) of
- {value,{_,Bindings}} ->
- Bindings;
- false -> % Then we use empty bindings.
- erl_eval:new_bindings()
- end.
-%% -----------------------------------------------------------------------------
-
-get_translations_opts(ArgsFromConfig) ->
- case lists:keysearch(translations,1,ArgsFromConfig) of
- {value,{_,Translations}} ->
- Translations;
- false -> % This becomes nearly point less.
- []
- end.
-%% -----------------------------------------------------------------------------
-
-get_dbg_opts(ArgsFromConfig) ->
- case lists:keysearch(debug,1,ArgsFromConfig) of
- {value,{_,DbgLevel}} ->
- DbgLevel;
- false ->
- off
- end.
-%% -----------------------------------------------------------------------------
-
-%% EOF
-
-
-
diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl
deleted file mode 100644
index b162f5b045..0000000000
--- a/lib/runtime_tools/src/inviso_rt.erl
+++ /dev/null
@@ -1,2885 +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%
-%%
-%% Description:
-%% The runtime component of the trace tool Inviso.
-%%
-%% Authors:
-%% Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_rt).
-
-
-%% -----------------------------------------------------------------------------
-%% interface for supervisor
-%% -----------------------------------------------------------------------------
--export([start_link_man/3,start_link_auto/1]).
-
-%% API for controll component.
--export([start/4,stop/1,
- init_tracing/2,stop_tracing_parallel/1,
- try_to_adopt/3,confirm_connection/2,get_node_info/1,
- suspend/2,call_suspend/2,cancel_suspension/1,change_options/2,
- clear/2,clear_all_tp/1,
- flush/1,
- trace_patterns_parallel/3,
- trace_flags_parallel/3,trace_flags_parallel/2,trace_flags_parallel/1,
- meta_tracer_call_parallel/2,
- get_status/1,get_tracerdata/1,list_logs/1,list_logs/2,fetch_log/2,fetch_log/3,
- delete_log/1,delete_log/2,
- state/1]).
-%% -----------------------------------------------------------------------------
-
-%% API mostly for autostart scripts, instead of corresponding control component
-%% apis not available doing local function calls.
--export([init_tracing/1,tp/4,tp/5,tp/1,tpg/4,tpg/5,tpg/1,
- tpl/4,tpl/5,tpl/1,
- ctp/1,ctp/3,ctpg/1,ctpg/3,ctpl/1,ctpl/3,
- init_tpm/4,init_tpm/7,
- tpm/4,tpm/5,tpm/8,tpm_tracer/4,tpm_tracer/5,tpm_tracer/8,
- tpm_ms/5,tpm_ms_tracer/5,
- ctpm_ms/4,
- local_register/0,global_register/0,
- ctpm/3,remove_local_register/0,remove_global_register/0,
- tf/2,tf/1,ctf/2,ctf/1]).
-%% -----------------------------------------------------------------------------
-
-%% Internal exports.
--export([init/4,auto_init/2,fetch_init/4]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
--define(DEFAULT_OVERLOAD_FUNC,default_overload_func).
--define(NO_LOADCHECK,no_loadcheck).
-
--define(RT_SUP,runtime_tools_sup). % Refers to the registered name.
--define(CTRL,inviso_c). % Refers to the registered name.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definition.
-%% -----------------------------------------------------------------------------
-
-%% #rt
-%% All record fields must be bound to listed values when leaving init or
-%% auto_init.
-%% dependency: Timeout accepting being without control component.
-%% overload : Controlls which module to call, if any, when time for a check.
-%% timer_ref: Used when timing delayed shutdown due to lost control component.
--record(rt,{state = new, % new | idle | tracing
- status = running, % running | {suspended, Reason}
- next_loadcheck = now(), % now | "No Loadcheck"
- parent, % pid()
- tracerdata, % undefined|{fun(),term()}|{file,Param}|{ip,Param}
- tracer_port, % port() | undefined
- handler, % {fun(), term()} | undefined
- auto_starter, % pid() | undefined; proc starting interpreters.
- meta_tracer, % undefined | pid()
- fetchers=[], % [pid(),...] processes transfering logfiles.
-% spies = [],
- dependency={infinity,node()}, % {TOut,Node} | TOut; TOut=int()|infinity
- overload=no_loadcheck, % ?NO_LOADCHECK|{LoadMF,Interval,InitMFA,RemoveMFA}
- overload_data=void, % Datastructure given to LoadMF and RemoveMFA.
- timer_ref, % undefined | reference()
- ctrl, % undefined | pid()
- ctrl_ref, % undefined | reference()
- vsn, % list()
- tag % term()
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Start API
-%% ==============================================================================
-
-%% Note that the runtime component may be started in many different ways.
-%% It can be autostarted by the runtime_tools_sup during initial start-up of the
-%% system. It is actually most likely that it will be started that way. However
-%% if there are no autostart trace-cases to run, the inviso_rt runtime component
-%% will terminate. It will then however remain as a child of the runtime_tools_sup
-%% supervisor. This means that if the runtime component is started again, manually,
-%% by the control component, some actions must be taken.
-%% For instance is it very likely that the child already exists. But since it
-%% must be started with different arguments when started manually, the child-spec
-%% must be changed.
-%%
-%% The runtime component is not a proper gen_server, to allow full control of
-%% what happens. It however mimcs gen_server behaviour to be managed by the
-%% runtime_tools_sup supervisor.
-
-
-%% start_link_auto(AutoModArgs)={ok,Pid}
-%%
-%% This function is entered into the child-spec when planning on doing autostart
-%% of the runtime component. The autostart is controlled by the so called
-%% inviso_autostart_mod. It is an application environment parameter of the
-%% runtime_tools application. If it exists, it shall point out a module name.
-%% If it does not exist, the default 'inviso_autostart' module will be tried.
-%% Note that these start_link functions do not implement proper otp-behaviour.
-%% For instance they return {ok,Pid} immediately making the init-phase of the
-%% runtime component process empty.
-%%
-%% The inviso_autostart_mod shall export one function:
-%% autostart(AutoModArgs) -> {MFA,Options,Tag}, where
-%% AutoModArgs=term(), comes from the application start parameters in the
-%% runtime_tools application resource file.
-%% MFA={Mod,Func,Args} | term().
-%% If it is MFA it will cause a trace initiator process to start spawning
-%% on spawn_link(Mod,Func,Args). The trace initiator may for instance
-%% initiate the wanted tracing.
-start_link_auto(AutoModArgs) ->
- {ok,spawn_link(?MODULE,auto_init,[AutoModArgs,self()])}.
-%% ------------------------------------------------------------------------------
-
-%% This function is entered into the child-specification of the runtime_tools_sup
-%% if the runtime component shall be started manually via the control component.
-start_link_man(Ctrl,Options,Tag) ->
- {ok,spawn_link(?MODULE,init,[Ctrl,Options,Tag,self()])}.
-%% ------------------------------------------------------------------------------
-
-%% start(Node,Options,Tag,Condition)=tbd
-%% Node=The node where the runtime component shall be started.
-%% Options=[Opt]; List of options to the runtime component.
-%% Opt={dependency,Val}|{dependency,{Val,Node}}
-%% Val=int()|infinity
-%% If the runtime component may run on its own or not. Val=0 means a runtime
-%% component which will terminate immediately without its control component.
-%% Note that if the runtime component is started manually, the Node part
-%% is never used. The runtime is supposed to be dependent of the Ctrl mentioned
-%% in the start_link_man parameters.
-%% Opt={overload,OverLoad} | overload
-%% The latter means no loadcheck. Necessary if changing the options.
-%% Overload=Iterval (int() in milliseconds) |
-%% {LoadMF,Interval}|{LoadMF,Interval,InitMFA,RemoveMFA}
-%% LoadMF={Mod,Func}|function()
-%% InitMFA,RemoveMFA={Mod,Func,ArgList} where
-%% apply(InitM,InitF,InitArgs) -> {ok,DataStruct}|'void'.
-%% apply(RemoveM,RemoveF,[DataStruct|Args]) -> don't care
-%% LoadMF is called each time loadcheck is performed.
-%% Mod:Func(DataStruct)->ok|{suspend,Reason}
-%% If just Interval is used, it means using a default overload check.
-%% Tag=term(), used to identify an incarnation of a runtime component so that
-%% a control component reconnecting will know if it was its own incarnation
-%% still alive, or some elses.
-%% Condition='if_ref'|term(). Controls if we want to adopt the runtime component.
-%% If 'if_ref' is stated it means that we only want to adopt a runtime component
-%% with the suggested Tag.
-%%
-%% This is the API used by the control component when tries to start a runtime
-%% component. Note that it will try to adopt an already running, if possible.
-%% Adoptions are only possible if the runtime component at hand is running
-%% without control component.
-start(Node, Options, Tag, Condition) when Node == node() ->
- ChildSpec = {?MODULE, {?MODULE, start_link_man, [self(), Options, Tag]},
- temporary, 5000, worker, [?MODULE]},
- case catch supervisor:start_child(?RT_SUP, ChildSpec) of
- {ok, Pid} when is_pid(Pid) ->
- {node_info, _Node, Pid, VSN, State, Status, _Tag} =
- get_node_info(Pid),
- {node_info, Node, Pid, VSN, State, Status, new};
- {error, already_present} ->
- supervisor:delete_child(?RT_SUP, ?MODULE),
- start(Node, Options, Tag, Condition);
- {error, {already_started, Pid}} ->
- try_to_adopt(Pid, Tag, Condition);
- {error,Reason} ->
- {error,Reason};
- {'EXIT',Reason} ->
- {error,Reason}
- end;
-start(Node, Options, Tag, Condition) ->
- case rt_version(Node) of
- {error,Error} ->
- {error,Error};
- _VSN ->
- ChildSpec = {?MODULE, {?MODULE, start_link_man,
- [self(), Options, Tag]},
- temporary, 5000, worker, [?MODULE]},
- case catch rpc:call(Node, supervisor, start_child,
- [?RT_SUP, ChildSpec]) of
- {ok, Pid} when is_pid(Pid) ->
- {node_info, _Node, Pid,
- VSN, State, Status, _Tag} = get_node_info(Pid),
- {node_info, Node, Pid, VSN, State, Status, new};
- {error, already_present} ->
- rpc:call(Node, supervisor, delete_child,
- [?RT_SUP, ?MODULE]),
- start(Node, Options, Tag, Condition);
- {error, {already_started, Pid}} ->
- try_to_adopt(Pid, Tag, Condition);
- {error,Reason} -> % Could not start child.
- {error,Reason};
- {badrpc,nodedown} ->
- {error,nodedown};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- {'EXIT',Reason} ->
- {error,Reason}
- end
- end.
-
-rt_version(Node) ->
- case catch rpc:call(Node,application,loaded_applications,[]) of
- List when is_list(List) ->
- case lists:keysearch(runtime_tools,1,List) of
- {value,{_,_,VSN}} ->
- VSN;
- false ->
- {error,not_loaded}
- end;
- {badrpc,nodedown} ->
- {error,nodedown};
- {'EXIT',Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-%% stop(Node)=ok|{error,Reason}
-%% Stops the runtim component on node Node. Note that this is mearly calling the
-%% supervisor API to shutdown the inviso_rt child belonging to the runtime_tools_sup.
-stop(Node) when Node==node() ->
- supervisor:terminate_child(?RT_SUP,?MODULE),
- supervisor:delete_child(?RT_SUP,?MODULE),
- ok;
-stop(Node) ->
- case catch rpc:call(Node,supervisor,terminate_child,[?RT_SUP,?MODULE]) of
- ok ->
- stop_delete_child(Node);
- {error,_} -> % No child running.
- stop_delete_child(Node); % Make sure we remove it also.
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- {'EXIT',Reason} ->
- {error,Reason}
- end.
-
-stop_delete_child(Node) ->
- case catch rpc:call(Node,supervisor,delete_child,[?RT_SUP,?MODULE]) of
- ok ->
- ok;
- {error,_} -> % No child running.
- ok;
- {badrpc,Reason} ->
- {error,{badrpc,Reason}};
- {'EXIT',Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% API for the control component.
-%% ==============================================================================
-
-%% init_tracing(TracerData) ->
-%% TracerData = LogTD | [{trace,LogTD},{ti,TiTD}]
-%% LogTD = {HandlerFun, Data} | collector |
-%% {relayer, pid()} | {ip, IPPortParameters} |
-%% {file, FilePortParameters}
-%% TiTD = {file,FileName} | {file,FileName,{InitPublLD,RemovePublLD,CleanPublLD}}
-%% | {relay,Node} | {relay,Node,{InitPublLD,RemovePublLD,CleanPublLD}}
-%% HandlerFun=fun(TraceMsg,Data)->NewData
-%% IPPortParameters = Portno | {Portno, Qsiz}
-%% Qsiz =
-%% FilePortParameters = {Filename, wrap, Tail, {time, WrapTime}, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize, WrapCnt} |
-%% {FileName, wrap, Tail, WrapSize} |
-%% {FileName, wrap, Tail} | FileName
-%% Defines a tracer:
-%% {HandlerFun, Data} - will be used as handler inside the runtime component for
-%% every incomming trace message.
-%% relayer - the runtime component will relay all comming trace messages to
-%% the runtime component Pid.
-%% collector - the runtime component is used as tracer or collector of relayed
-%% trace messages using the default handler writing them to io.
-%% ip | file - will start a tracer port using PortParameters
-init_tracing(Pid,TracerData) ->
- call(Pid,{init_tracing,TracerData}).
-%% ------------------------------------------------------------------------------
-
-%% stop_tracing(RTpids)=[{Node,NodeResult},...]
-%% RTpids=[RTinfo,...]
-%% RTinfo={RTpid,Node} | {{error,Reason},Node}
-%% NodeResult={ok,State} | {error,Reason}
-%% Sends a request to stop tracing to all nodes in RTpids, in parallel. Stop
-%% tracing means that all trace flags are removed and the nodes go to idle
-%% state.
-stop_tracing_parallel(RTpids) ->
- call_parallel(lists:map(fun({Pid,Node})->{Pid,Node,stop_tracing};
- (Error)->Error
- end,
- RTpids)).
-%% ------------------------------------------------------------------------------
-
-%% try_to_adopt(Pid,NewTag,Condition)=
-%% {node_info,node(),self(),VSN,State,Status,{tag,PreviousTag}}|{error,Reason}
-%% NewTag=term(), the identification tag we want the runtime component to use
-%% from now on if adoption was successful.
-%% Condition='if_ref', only adopt if current tag is NewTag.
-%% PreviousTag= the tag the runtime component had before it accepted the
-%% adoption.
-%% This function shall only be used by a control component wishing to adopt this
-%% runtime component.
-try_to_adopt(Pid, Tag, Condition) ->
- call(Pid,{try_to_adopt,Tag,Condition}).
-%% ------------------------------------------------------------------------------
-
-%% confirm_connection(Pid,Tag)= {node_info,node(),self(),VSN,State,Status,Tag}|
-%% {error,refused}.
-%% Must only be used by a control component having been contacted by the runtime
-%% component Pid. It confirms to the runtime component that the control component
-%% has accepted the connect request.
-confirm_connection(Pid,Tag) ->
- call(Pid,{confirm_connection,Tag}).
-%% ------------------------------------------------------------------------------
-
-%% get_node_info(Pid)={node_info,Node,Pid,VSN,State,Status,Tag}.
-get_node_info(Pid) ->
- call(Pid,get_node_info).
-%% ------------------------------------------------------------------------------
-
-%% suspend(NodeOrPid,Reason)=ok
-%% call_suspend(NodeOrPid,Reason)=ok
-%% Makes the runtime component and all of its helpers suspend. suspend/2 is
-%% assynchronous.
-suspend(NodeOrPid,Reason) ->
- cast(NodeOrPid,{suspend,Reason}).
-
-call_suspend(NodeOrPid,Reason) ->
- call(NodeOrPid,{suspend,Reason}).
-%% ------------------------------------------------------------------------------
-
-%% cancel_suspension(Pid)=ok
-%% Function moving the runtime component to status running. Regardless of its
-%% current status.
-cancel_suspension(Pid) ->
- call(Pid,cancel_suspension).
-%% ------------------------------------------------------------------------------
-
-%% change_options(Pid,Options)=ok
-%% Options=list(); see the start_link_XXX functions.
-%% Changes options according to Options list.
-%% Changing the control component we shall be depending on has no effect. The
-%% dependency value in self can however be changed, and takes effect immediately.
-change_options(Pid,Options) ->
- call(Pid,{change_options,Options}).
-%% ------------------------------------------------------------------------------
-
-%% clear_all_tp(Pid)=ok
-%% Function removing all, both local and global trace-patterns from the node.
-clear_all_tp(Pid) ->
- call(Pid,clear_all_tp).
-%% ------------------------------------------------------------------------------
-
-%% clear(Pid,Options)={ok,{new,Status}}
-%% Options=[Opt,...]
-%% Opt=keep_trace_patterns | keep_log_files
-%% Resets the runtime component to state 'new' by stopping all ongoing tracing,
-%% closing and removing all associated logfiles. The Options can be used to
-%% prevent the runtime component from being totally erased.
-clear(Pid,Options) ->
- call(Pid,{clear,Options}).
-%% ------------------------------------------------------------------------------
-
-%% flush(Pid)=ok | {error,Reason}
-%% Sends the flush command to the trace-port, if we are using a trace-port and
-%% are tracing.
-flush(Pid) ->
- call(Pid,flush).
-%% ------------------------------------------------------------------------------
-
-%% trace_patterns_parallel(RTpids,Args,Flags)=[{Node,Answer},...]
-%% RTpids=[{RTpid,Node},...] or [{Error,Node},...]
-%% Args=[Arg,...]
-%% Arg={Mod,Func,Arity,MS}|{Mod,Func,Arity,MS,Opts}
-%% Mod=atom()|reg_exp()|{Dir,reg_exp()}
-%% Dir=reg_exp()
-%% Answer=[Answer,...]
-%% Answer=int()|{error,Reason}
-%% API function for the control component sending trace-patterns to a list of
-%% runtime components. Returns a [{Node,Answer},...] list in the same order.
-trace_patterns_parallel(RTpids,Args,Flags) -> % Same args and flags for all.
- call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->{Pid,Node,{tp,Args,Flags}};
- (Error)-> Error
- end,
- RTpids)).
-%% ------------------------------------------------------------------------------
-
-%% trace_flags_parallel(RTpids,Args,How)=
-%% trace_flags_parallel(RTpidsArgs,How)=
-%% trace_flags_parallel(RTpidsArgsHow)=[{Node,Reply},...]
-%% RTpids=[RTpidEntry,...]
-%% RTpidEntry={RTpid,Node}|{Error,Node}
-%% Error=term(), any term you wish to have as reply in Answer assoc. to Node.
-%% Args=[{Process,Flags},...]
-%% Process=pid()|registeredname()|'all'|'new'|'existing'
-%% Flags=List of the allowed process trace flags.
-%% RTpidsArgs=[RTpidArgEntry,...]
-%% RTpidArgEntry={RTpid,Node,Args}|{Error,Node}
-%% RTpidsArgsHow=[RTpidArgsHowEntry,...]
-%% RTpidArgsHowEntry={RTpid,Node,Args,How}|{Error,Node}
-%% How=true|false
-%% Reply={ok,Answers}
-%% Answers=[Answer,...], one for each Args and in the same order.
-%% Answer=int()|{error,Reason}
-%% API function used by the control component to send flags to a list of runtime
-%% components. Returns a list of [{Node,Answer},... ] in the same order.
-trace_flags_parallel(RTpids,Args,How) -> % Same args for every node!
- call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->{Pid,Node,{tf,Args,How}};
- (Error)-> Error
- end,
- RTpids)).
-
-trace_flags_parallel(RTpidArgs,How) -> % Different args but same how.
- call_parallel(lists:map(fun({Pid,Node,Args})when is_pid(Pid)->
- {Pid,Node,{tf,Args,How}};
- (Error)->
- Error
- end,
- RTpidArgs)).
-
-trace_flags_parallel(RTpidArgsHow) -> % Both different args and hows.
- call_parallel(lists:map(fun({Pid,Node,Args,How})when is_pid(Pid)->
- {Pid,Node,{tf,Args,How}};
- (Error)->
- Error
- end,
- RTpidArgsHow)).
-%% ------------------------------------------------------------------------------
-
-%% meta_pattern(RTpids,Args)=[{Node,Answer},...]
-%% RTpids=[{RTpid,Node},...] or [{Error,Node},...]
-%% Args={FunctionName,ArgList}
-%% FunctionName=atom()
-%% ArgList=list(), list of the arguments to FunctionName.
-%% Answer=[Answer,...]
-%% Answer=int()|{error,Reason}
-%% Makes a call to the meta-tracer through its runtime component. Returns a list
-%% a answers in the same order as RTpids. Note that if "someone" has discovered
-%% that there is an error with a particular node, the error answer can be placed
-%% in the RTpids list from the start.
-meta_tracer_call_parallel(RTpids,Args) -> % Same args for all nodes.
- call_parallel(lists:map(fun({Pid,Node})when is_pid(Pid)->
- {Pid,Node,{meta_tracer_call,Args}};
- (Error)->
- Error
- end,
- RTpids)).
-%% ------------------------------------------------------------------------------
-
-%% get_status(Pid)={ok,{State,Status}}
-%% State=new|tracing|idle
-%% Status=running|{suspended,Reason}
-get_status(Pid) ->
- call(Pid,get_status).
-%% ------------------------------------------------------------------------------
-
-%% get_tracerdata(Pid)={ok,TracerData} | {ok,no_tracerdata} | {error,Reason}
-%% TracerData=see init_tracing
-%% Fetches the current tracerdata from the runtime component.
-get_tracerdata(Pid) ->
- call(Pid,get_tracerdata).
-%% ------------------------------------------------------------------------------
-
-%% list_log(Pid)={ok,no_log}|{ok,LogCollection}|{error,Reason}
-%% list_log(Pid,TracerData)=
-%% LogCollection=[LogTypes,...]
-%% LogTypes={trace_log,Dir,Files}|{ti_log,Dir,Files}
-%% Dir=string()
-%% Files=[FileNameWithoutDir,...]
-%% Lists all files associated with the current tracerdata. Or finds out which
-%% files there are stored in this node given a tracerdata.
-list_logs(Pid) ->
- call(Pid,list_logs).
-list_logs(Pid,TD) ->
- call(Pid,{list_logs,TD}).
-%% ------------------------------------------------------------------------------
-
-%% fetch_log(Pid,CollectPid)={ok,FetcherPid}|{complete,no_log}|{error,Reason}
-%% fetch_log(Pid,CollectPid,Spec)=
-%% CollectPid=pid(), the process which will be given the transfered logs.
-%% Spec=TracerData|LogCollection
-%% Transferes a number of files using ditributed Erlang to CollectPid. This
-%% function is supposed to be used internally by a control component. It returns
-%% when the transfer is initiated and does not mean it is done or successful.
-fetch_log(Pid,CollectPid) ->
- call(Pid,{fetch_log,CollectPid}).
-fetch_log(Pid,CollectPid,Spec) ->
- call(Pid,{fetch_log,CollectPid,Spec}).
-%% ------------------------------------------------------------------------------
-
-%% delete_log(Pid,TracerDataOrLogList)={ok,Results}|{error,Reason}
-%% TracerDataOrLogList=[FileNameWithPath,...]|LogCollection|TracerData
-%% Results=[LogType,...]
-%% LogType={trace_log,FileSpecs}|{ti_log,FilesSpecs}
-%% FilesSpecs=[FileSpec,...]
-%% FileSpec={ok,FileName}|{error,{Posix,FileName}}
-%% Filename=string(), the filename without dir-path.
-delete_log(Pid) ->
- call(Pid,delete_logs).
-delete_log(Pid,TracerDataOrLogList) ->
- call(Pid,{delete_logs,TracerDataOrLogList}).
-%% ------------------------------------------------------------------------------
-
-%% state(NodeOrPid)=LoopData
-%% Returns the loopdata of the runtime component. Only meant for debugging.
-state(NodeOrPid) ->
- call(NodeOrPid,state).
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% API for local calls made from the same node. E.g autostart.
-%% ==============================================================================
-
-%% init_tracing(TracerData)=
-%% See init_tracing/2.
-init_tracing(TracerData) ->
- call_regname(?MODULE,{init_tracing,TracerData}).
-%% ------------------------------------------------------------------------------
-
-
-%% Meaning that these function does most often not have to be called by a
-%% control component because there are more efficient ones above.
-
-%% tp(Module,Function,Arity,MatchSpec) ->
-%% tp(Module,Function,Arity,MatchSpec,Opts) ->
-%% tp(PatternList) ->
-%% Module = '_'|atom()|ModRegExp|{DirRegExp,ModRegExp}
-%% Function == atom() | '_'
-%% Arity = integer() | '_'
-%% MatchSpec = true | false | [] | matchspec() see ERTS User's guide for a
-%% description of match specifications.
-%% Opts=list(); 'only_loaded'
-%% PatternList = [Pattern],
-%% Pattern = {Module,Function,Arity,MatchSpec,Opts},
-%% Set trace pattern (global).
-tp(Module,Function,Arity,MatchSpec) ->
- tp(Module,Function,Arity,MatchSpec,[]).
-tp(Module,Function,Arity,MatchSpec,Opts) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,Opts}],[global]}).
-tp(PatternList) ->
- call_regname(?MODULE,{tp,PatternList,[global]}).
-%% ------------------------------------------------------------------------------
-
-tpg(Mod,Func,Arity,MatchSpec) ->
- tp(Mod,Func,Arity,MatchSpec).
-tpg(Mod,Func,Arity,MatchSpec,Opts) ->
- tp(Mod,Func,Arity,MatchSpec,Opts).
-tpg(PatternList) ->
- tp(PatternList).
-%% ------------------------------------------------------------------------------
-
-%% tpl(Module,Function,Arity,MatchSpec) ->
-%% tpl(Module,Function,Arity,MatchSpec,Opts) ->
-%% tpl(PatternList) ->
-%% Module = Function == atom() | '_' | RegExpMod | {RegExpDir,RegExpMod}
-%% Arity = integer() | '_'
-%% MatchSpec = true | false | [] | matchspec() see ERTS User's guide for a
-%% Opts=list(); 'only_loaded'
-%% description of match specifications.
-%% PatternList = [Pattern],
-%% Pattern = {Module, Function, Arity, MatchSpec},
-%% Set trace pattern (local).
-tpl(Module,Function,Arity,MatchSpec) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,[]}],[local]}).
-tpl(Module,Function,Arity,MatchSpec,Opts) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,MatchSpec,Opts}],[local]}).
-tpl(PatternList) ->
- call_regname(?MODULE,{tp,PatternList,[local]}).
-%% ------------------------------------------------------------------------------
-
-%% ctp(Module,Function,Arity) ->
-%% ctp(PatternList)=
-%% Module = atom()|'_'|RegExpMod|{RegExpDir,RegExpMod}
-%% Function == atom() | '_'
-%% Arity = integer() | '_'
-%% PatternList=[{Mod,Func,Arity},...]
-%% Clear trace pattern (global).
-%% Note that it is possible to clear patterns using regexps. But we can for
-%% natural reasons only clear patterns for loaded modules. Further more there
-%% seems to be a fault in the emulator (<=R10B) crashing if we remove patterns
-%% for deleted modules. Therefore we use the only_loaded option.
-ctp(Module,Function,Arity) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,false,[only_loaded]}],[global]}).
-ctp(PatternList) ->
- call_regname(?MODULE,
- {tp,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [global]}).
-%% ------------------------------------------------------------------------------
-
-ctpg(Mod,Func,Arity) ->
- ctp(Mod,Func,Arity).
-ctpg(PatternList) ->
- ctp(PatternList).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(Module,Function,Arity) ->
-%% Module = atom()|'_'|RegExpMod|{RegExpDir,RegExpMod}
-%% Function == atom() | '_'
-%% Arity = integer() | '_'
-%% PatternList=[{Mod,Func,Arity},...]
-%% Clear trace pattern (local).
-ctpl(Module,Function,Arity) ->
- call_regname(?MODULE,{tp,[{Module,Function,Arity,false,[only_loaded]}],[local]}).
-ctpl(PatternList) ->
- call_regname(?MODULE,
- {tp,
- lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
- [local]}).
-%% ------------------------------------------------------------------------------
-
-init_tpm(Mod,Func,Arity,CallFunc) ->
- call_regname(?MODULE,{meta_tracer_call,{init_tpm,[Mod,Func,Arity,CallFunc]}}).
-
-init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- call_regname(?MODULE,
- {meta_tracer_call,
- {init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}).
-%% ------------------------------------------------------------------------------
-
-tpm(Mod,Func,Arity,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm,[Mod,Func,Arity,MS]}}).
-tpm(Mod,Func,Arity,MS,CallFunc) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm,[Mod,Func,Arity,MS,CallFunc]}}).
-tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- call_regname(?MODULE,
- {meta_tracer_call,
- {tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}).
-%% ------------------------------------------------------------------------------
-
-tpm_tracer(Mod,Func,Arity,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_tracer,[Mod,Func,Arity,MS]}}).
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_tracer,[Mod,Func,Arity,MS,CallFunc]}}).
-tpm_tracer(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- call_regname(?MODULE,
- {meta_tracer_call,
- {tpm_tracer,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc]}}).
-%% ------------------------------------------------------------------------------
-
-tpm_ms(Mod,Func,Arity,MSname,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_ms,[Mod,Func,Arity,MSname,MS]}}).
-%% ------------------------------------------------------------------------------
-
-tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->
- call_regname(?MODULE,{meta_tracer_call,{tpm_ms_tracer,[Mod,Func,Arity,MSname,MS]}}).
-%% ------------------------------------------------------------------------------
-
-ctpm_ms(Mod,Func,Arity,MSname) ->
- call_regname(?MODULE,{meta_tracer_call,{ctpm_ms,[Mod,Func,Arity,MSname]}}).
-%% ------------------------------------------------------------------------------
-
-local_register() ->
- call_regname(?MODULE,{meta_tracer_call,{local_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-global_register() ->
- call_regname(?MODULE,{meta_tracer_call,{global_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-ctpm(Mod,Func,Arity) ->
- call_regname(?MODULE,{meta_tracer_call,{ctpm,[Mod,Func,Arity]}}).
-%% ------------------------------------------------------------------------------
-
-remove_local_register() ->
- call_regname(?MODULE,{meta_tracer_call,{remove_local_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-remove_global_register() ->
- call_regname(?MODULE,{meta_tracer_call,{remove_global_register,[]}}).
-%% ------------------------------------------------------------------------------
-
-%% tf(PidSpec, FlagList) ->
-%% tf(TraceConfList) ->
-%% TraceConfList = [{PidSpec, FlagList}],
-%% FlagList = [Flags],
-%% PidSpec = all | new | existing | pid() | registeredname()
-%% Flags = all | send | 'receive' | procs | call | silent | return_to |
-%% running | garbage_collection | timestamp | cpu_timestamp | arity |
-%% set_on_spawn | set_on_first_spawn | set_on_link | set_on_first_link
-%% Set trace flags.
-tf(PidSpec, FlagList) ->
- call_regname(?MODULE,{tf,[{PidSpec,FlagList}],true}).
-
-tf(TraceConfList) ->
- call_regname(?MODULE,{tf,TraceConfList,true}).
-%% ------------------------------------------------------------------------------
-
-%% ctf(PidSpec, FlagList) ->
-%% ctf(TraceConfList) ->
-%% TraceConfList = [{PidSpec, FlagList}],
-%% FlagList = [Flags],
-%% PidSpec = all | new | existing | pid() | registeredname()
-%% Flags = all | send | 'receive' | procs | call | silent | return_to |
-%% running | garbage_collection | timestamp | cpu_timestamp | arity |
-%% set_on_spawn | set_on_first_spawn | set_on_link | set_on_first_link
-%% Clear trace flags.
-ctf(PidSpec, FlagList) ->
- call_regname(?MODULE,{tf,[{PidSpec,FlagList}],false}).
-
-ctf(TraceConfList) ->
- call_regname(?MODULE,{tf_as,TraceConfList,false}).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Client side functions.
-%% ------------------------------------------------------------------------------
-
-%% Call function managing the client to server communication. This function may
-%% be run by a client on a different node.
-%% Note that we must use two different functions for calling a named process and
-%% calling the runtime component at a specified node.
-call(Pid,Request) when is_pid(Pid) ->
- call_2(Pid,Request);
-call(Node,Request) when Node==node() -> % To our node!
- call_2(?MODULE,Request);
-call(Node,Request) when is_atom(Node) ->
- call_2({?MODULE,Node},Request);
-call(To,_Request) ->
- {error,{badarg,To}}.
-
-call_regname(Name,Request) when is_atom(Name) -> % To a registered name.
- call_2(Name,Request).
-
-call_2(To,Request) ->
- MRef=erlang:monitor(process,To), % Use a monitor to avoid waiting for ever.
- Ref=make_ref(),
- case catch To ! {Request,self(),Ref} of % Can be a remote pid.
- {'EXIT',_} -> % If we use registered name.
- erlang:demonitor(MRef), % Maybe not necessary!?
- receive
- {'DOWN',MRef,_Type,_Obj,_Info} ->
- true
- after
- 0 ->
- true
- end,
- {error,not_started};
- _ -> % At least no obvious error.
- receive
- {Msg,Ref} ->
- erlang:demonitor(MRef),
- Msg;
- {'DOWN',MRef,_Type,_Obj,Info} -> % The runtime component disapeared.
- {error,{no_response,Info}}
- end
- end.
-%% -----------------------------------------------------------------------------
-
-%% Multicall function taking a list of [{Pid,Node,Request},...] and sends
-%% a request to every Pid. This function then also allows you to send multiple
-%% requests to the same Pid since it will sit and wait for all replies.
-%% Note that RTspec may also be an [{{error,Reason},Node},...]. That tuple will
-%% then be used as reply in the reply list.
-%% Returns [{Node,Reply},...] for every element in RTspec, in the same order.
-call_parallel(RTspec) ->
- Ref=make_ref(),
- {Nr,Pending}=call_parallel_2(RTspec,Ref,0,[]),
- Replies=call_parallel_3(Ref,Pending,Nr,[],[]),
- call_parallel_build_reply(RTspec,1,Replies).
-
-call_parallel_2([{Pid,Node,Request}|Rest],Ref,Nr,Pending) when is_pid(Pid) ->
- Pid ! {Request,self(),{Ref,Nr+1}},
- MRef=erlang:monitor(process,Pid), % So we won't wait for ever for it.
- call_parallel_2(Rest,Ref,Nr+1,[{Nr+1,Node,MRef}|Pending]);
-call_parallel_2([{{error,_Reason},_Node}|Rest],Ref,Nr,Pending) ->
- call_parallel_2(Rest,Ref,Nr,Pending); % Just skip it. This is no process.
-call_parallel_2([_Faulty|Rest],Ref,Nr,Pending) -> % Should not happend.
- call_parallel_2(Rest,Ref,Nr,Pending); % But we choose to skip it instead of crash.
-call_parallel_2([],_,Nr,Pending) ->
- {Nr,Pending}.
-
-%% Help function collecting reply-messages sent from the runtime components. We
-%% count down until we got a reply for every pending request. Or if we get a DOWN
-%% message indicating that the runtime component is no longer present. Note that
-%% we can by accident read away DOWN messages not belonging to this procedure.
-%% They are collected to be reissued after we are done.
-call_parallel_3(_Ref,_Pending,0,Replies,DownMsgs) -> % All expected received.
- lists:foreach(fun({MRef,Pid,Info}) -> self() ! {'DOWN',MRef,process,Pid,Info} end,
- DownMsgs), % Reissue the down messages!
- Replies;
-call_parallel_3(Ref,Pending,NrOfPending,Replies,DownMsgs) ->
- receive
- {Reply,{Ref,Nr}} ->
- case lists:keysearch(Nr,1,Pending) of
- {value,{_Nr,Node,MRef}} ->
- erlang:demonitor(MRef),
- call_parallel_3(Ref,Pending,NrOfPending-1,
- [{Nr,Node,Reply}|Replies],DownMsgs);
- false -> % Really strange!
- call_parallel_3(Ref,Pending,NrOfPending,Replies,DownMsgs)
- end;
- {'DOWN',MRef,process,Pid,Info} -> % Probably process we monitor terminated.
- case lists:keysearch(MRef,3,Pending) of
- {value,{Nr,Node,_}} -> % Yes it was one of our processes.
- call_parallel_3(Ref,Pending,NrOfPending-1,
- [{Nr,Node,{error,no_reponse}}|Replies],DownMsgs);
- false -> % We picked up a DOWN msg by misstake.
- call_parallel_3(Ref,Pending,NrOfPending,Replies,
- [{MRef,Pid,Info}|DownMsgs])
- end
- end.
-
-%% Help function which build up the [{Node,Reply},...] list in the same order as RTspec.
-call_parallel_build_reply([],_,_) ->
- [];
-call_parallel_build_reply([{Pid,Node,_Request}|Rest],Nr,Replies) when is_pid(Pid) ->
- {value,{_Nr,_Node,Reply}}=lists:keysearch(Nr,1,Replies),
- [{Node,Reply}|call_parallel_build_reply(Rest,Nr+1,Replies)];
-call_parallel_build_reply([{{error,Reason},Node}|Rest],Nr,Replies) ->
- [{Node,{error,Reason}}|call_parallel_build_reply(Rest,Nr,Replies)];
-call_parallel_build_reply([_Faulty|Rest],Nr,Replies) ->
- call_parallel_build_reply(Rest,Nr,Replies).
-%% ------------------------------------------------------------------------------
-
-cast(Pid,Request) when is_pid(Pid) ->
- cast2(Pid,Request);
-cast(Node,Request) when Node==node() ->
- catch cast2(?MODULE,Request),
- ok;
-cast(Node,Request) when is_atom(Node) ->
- catch cast2({?MODULE,Node},Request),
- ok;
-cast(BadAddress,_Request) ->
- {error,{badarg,BadAddress}}.
-
-cast2(To,Request) ->
- To ! {Request,void,void}. % Mimics the call protocol.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Implementation of the runtime component (server side).
-%% ==============================================================================
-
-%% Since the runtime component is not implemented using gen_sever we are "free"
-%% to use what ever functionnames we like.
-
-%% Initial function on which the runtime component is spawned on if started by
-%% a controlcomponent.
-init(Ctrl, Options, Tag, Parent) when is_list(Options) ->
- %% started from controller
- process_flag(trap_exit,true),
- register(?MODULE,self()), % Will crash if rt is already running
- do_clear_trace_patterns(), % Remove potential old patterns left.
- LD1=read_option_list(Options,
- #rt{state=new,
- parent=Parent,
- ctrl=Ctrl,
- vsn=get_application_vsn(),
- tag=Tag}),
- OverloadData=initialize_overload(LD1),
- CtrlRef=erlang:monitor(process,Ctrl), % Monitor our control component.
- loop1(LD1#rt{ctrl_ref=CtrlRef,overload_data=OverloadData}).
-%% ----------------------------------------------------------------------------
-
-%% Initial function on which the runtime component is spawned on if started
-%% by the runtime_tools supervisor. It is here it is determined if we shall
-%% autostart.
-auto_init(AutoModArgs,Parent) ->
- %% autostart
- process_flag(trap_exit, true),
- register(?MODULE, self()), % Will crash if a rt is already running
- AutoMod=get_autostart_module(), % Determine which module to use!
- case catch AutoMod:autostart(AutoModArgs) of
- {MFA,Options,Tag} ->
- do_clear_trace_patterns(), % Remove previously left patterns.
- LD1=read_option_list(Options,#rt{state=new,
- parent=Parent,
- vsn=get_application_vsn(),
- tag=Tag}),
- case auto_init_connect_control(LD1) of
- {ok,LD2} -> % Either connected or running_alone.
- OverloadData=initialize_overload(LD2),
- case auto_init_check_mfa(MFA) of
- {ok,{M,F,A}} -> % We shall start somekind of tracing!
- P=spawn_link(M,F,A), % It lives its own life, only link!
- loop1(LD2#rt{auto_starter=P,overload_data=OverloadData});
- false ->
- loop1(LD2#rt{overload_data=OverloadData})
- end;
- stop -> % Not allowed to run alone!
- true % Simply terminate.
- end;
- _ -> % Non existent or faulty autostart mod!
- true % Terminate normally.
- end.
-
-auto_init_connect_control(LD1) ->
- case auto_init_connect_find_pid(LD1#rt.dependency) of
- Pid when is_pid(Pid) -> % There is a control component.
- CtrlRef=erlang:monitor(process,Pid),
- Pid ! {connect,node(),self(),LD1#rt.vsn,LD1#rt.tag},
- {ok,LD1#rt{ctrl_ref=CtrlRef,ctrl=Pid}};
- _ -> % There is no control component.
- do_down_message(LD1) % Will return 'stop' or a LoopData.
- end.
-
-%% Help function which finds the pid of the control component.
-auto_init_connect_find_pid({_TimeOut,Node}) when Node==node() ->
- whereis(?CTRL);
-auto_init_connect_find_pid({_TimeOut,Node}) when is_atom(Node) ->
- rpc:call(Node,erlang,whereis,[?CTRL]);
-auto_init_connect_find_pid(_) -> % Node is not a proper node.
- undefined. % Act as could not find control comp.
-
-%% Help function checking that the parameter is reasonable to be used as
-%% spawn_link argument.
-auto_init_check_mfa({M,F,A}) when is_atom(M),is_atom(F),is_list(A) ->
- {ok,{M,F,A}};
-auto_init_check_mfa(_) ->
- false.
-
-%% Help function to init_auto which finds out which module to call for
-%% guidance on how to proceed. Returns an atom.
-get_autostart_module() ->
- case application:get_env(inviso_autostart_mod) of
- {ok,Mod} when is_atom(Mod) ->
- Mod;
- _ ->
- inviso_autostart % The default autostart module.
- end.
-%% ----------------------------------------------------------------------------
-
-
-%% This is the preloop function which performs loadcheck if necessary. Note
-%% that it calculates the timeout used in the after in the real loop. There is
-%% further no use doing overload checks if we are not tracing or already
-%% suspended. There is yet one more situation, we do not want to perform
-%% overload checks if the interval is set to infinity. This can be the case if
-%% we are using an external source pushing overload information instead.
-loop1(LD=#rt{overload=Overload}) ->
- if
- Overload/=?NO_LOADCHECK,element(2,Overload)/=infinity ->
- Now=now(),
- if
- LD#rt.status==running,
- LD#rt.state==tracing,
- Now>LD#rt.next_loadcheck -> % Do loadcheck only then!
- {NewLD,TimeOut}=do_check_overload(LD,{timeout,LD#rt.overload_data}),
- loop(NewLD,TimeOut);
- LD#rt.status==running,LD#rt.state==tracing ->
- Timeout=calc_diff_to_now(Now,LD#rt.next_loadcheck),
- loop(LD,Timeout);
- true -> % Do not spend CPU on this! :-)
- loop(LD,infinity)
- end;
- true -> % Either no check or infinity.
- loop(LD,infinity)
- end.
-
-loop(LoopData,Timeout) ->
- receive
- Msg when element(1,Msg)==trace_ts;
- element(1,Msg)==trace;
- element(1,Msg)==drop;
- element(1,Msg)==seq_trace ->
- case LoopData#rt.handler of
- {HandlerFun,Data} ->
- NewData=HandlerFun(Msg,Data),
- loop1(LoopData#rt{handler={HandlerFun,NewData}});
- _ ->
- loop1(LoopData)
- end;
- {{tp,Args,Flags},From,Ref} ->
- if
- LoopData#rt.status==running -> % Not when suspended.
- Reply=do_set_trace_patterns(Args,Flags),
- if
- LoopData#rt.state==new -> % No longer new when tp set.
- reply_and_loop({ok,Reply},From,Ref,LoopData#rt{state=idle});
- true ->
- reply_and_loop({ok,Reply},From,Ref,LoopData)
- end;
- true -> % We are suspended!
- reply_and_loop({error,suspended},From,Ref,LoopData)
- end;
- {{tf,Args,How},From,MRef} ->
- Reply=
- case How of
- true ->
- if
- LoopData#rt.status==running ->
- case {LoopData#rt.tracer_port,LoopData#rt.handler} of
- {Port,_} when is_port(Port) ->
- do_set_trace_flags(Port,Args,How);
- {_,{Handler,_D}} when is_function(Handler) ->
- do_set_trace_flags(self(),Args,How);
- _ ->
- {error,no_tracer}
- end;
- true -> % Can't turn *on* flags if suspended.
- {error, suspended}
- end;
- false -> % No tracer needed when turning off.
- do_set_trace_flags(void,Args,How)
- end,
- reply_and_loop(Reply,From,MRef,LoopData);
- {{meta_tracer_call,Args},From,MRef} ->
- if
- LoopData#rt.status==running ->
- case LoopData#rt.meta_tracer of
- MPid when is_pid(MPid) ->
- Reply=do_meta_pattern(MPid,Args),
- reply_and_loop(Reply,From,MRef,LoopData);
- _ ->
- reply_and_loop({error,no_metatracer},From,MRef,LoopData)
- end;
- true ->
- reply_and_loop({error,suspended},From,MRef,LoopData)
- end;
- {clear_all_tp,From,MRef} ->
- do_clear_trace_patterns(),
- reply_and_loop(ok,From,MRef,LoopData);
- {{init_tracing,TracerData},From,MRef} ->
- {NewLoopData,Reply}=
- if
- LoopData#rt.status==running ->
- if
- LoopData#rt.state==tracing ->
- {LoopData,{error,already_initiated}};
- true -> % Otherwise, try to init-tracing!
- case translate_td(TracerData) of
- {ok,LogTD,MetaTD} ->
- do_init_tracing(LoopData,TracerData,LogTD,MetaTD);
- Error ->
- {LoopData,Error}
- end
- end;
- true -> % Can't init tracing if not running.
- {LoopData,{error,suspended}}
- end,
- reply_and_loop(Reply,From,MRef,NewLoopData);
- {stop_tracing,From,MRef} ->
- case LoopData#rt.state of
- tracing -> % Only case we need to do anything.
- reply_and_loop({ok,idle},From,MRef,do_stop_tracing(LoopData));
- idle -> % Already idle!
- reply_and_loop({ok,idle},From,MRef,LoopData);
- new -> % Have actually never traced!
- reply_and_loop({ok,new},From,MRef,LoopData)
- end;
- {{suspend,Reason},From,MRef} ->
- if
- LoopData#rt.status==running ->
- NewLD=do_suspend(LoopData,Reason),
- reply_and_loop(ok,From,MRef,NewLD);
- true -> % No need suspend if not running!
- reply_and_loop(ok,From,MRef,LoopData)
- end;
- {cancel_suspension,From,MRef} ->
- NewLoopData=LoopData#rt{status=running,next_loadcheck=now()},
- send_event(state_change,NewLoopData),
- reply_and_loop(ok,From,MRef,NewLoopData);
- {{clear,Options},From,MRef} ->
- NewLoopData=do_clear(LoopData,Options),
- reply_and_loop({ok,{new,NewLoopData#rt.status}},From,MRef,NewLoopData);
- {flush,From,MRef} ->
- case LoopData#rt.state of
- tracing -> % Can only flush if we are tracing.
- if
- is_port(LoopData#rt.tracer_port) ->
- trace_port_control(LoopData#rt.tracer_port,flush),
- reply_and_loop(ok,From,MRef,LoopData);
- true -> % Not necessary but lets pretend.
- reply_and_loop(ok,From,MRef,LoopData)
- end;
- State ->
- reply_and_loop({error,{not_tracing,State}},From,MRef,LoopData)
- end;
- {list_logs,From,MRef} ->
- TracerData=LoopData#rt.tracerdata, % Current tracerdata.
- if
- TracerData/=undefined -> % There is tracerdata!
- reply_and_loop(do_list_logs(TracerData),From,MRef,LoopData);
- true -> % Have no current tracerdata!
- reply_and_loop({error,no_tracerdata},From,MRef,LoopData)
- end;
- {{list_logs,TracerData},From,MRef} ->
- reply_and_loop(do_list_logs(TracerData),From,MRef,LoopData);
- {{fetch_log,CollectPid},From,MRef} -> % Fetch according to current tracerdata.
- TracerData=LoopData#rt.tracerdata, % Current tracerdata.
- if
- TracerData/=undefined -> % There is tracerdata!
- {Reply,NewLD}=do_fetch_log(LoopData,CollectPid,TracerData),
- reply_and_loop(Reply,From,MRef,NewLD);
- true -> % No tracerdata!
- reply_and_loop({error,no_tracerdata},From,MRef,LoopData)
- end;
- {{fetch_log,CollectPid,Spec},From,MRef} -> % Either list of files or tracerdata.
- {Reply,NewLD}=do_fetch_log(LoopData,CollectPid,Spec),
- reply_and_loop(Reply,From,MRef,NewLD);
- {delete_logs,From,MRef} ->
- if
- LoopData#rt.state==tracing -> % Can't remove then!
- reply_and_loop({error,tracing},From,MRef,LoopData);
- true ->
- TracerData=LoopData#rt.tracerdata,
- reply_and_loop(do_delete_logs(TracerData),From,MRef,LoopData)
- end;
- {{delete_logs,TracerDataOrLogList},From,MRef} ->
- if
- LoopData#rt.state==tracing -> % Can't remove then!
- reply_and_loop({error,tracing},From,MRef,LoopData);
- true ->
- reply_and_loop(do_delete_logs(TracerDataOrLogList),From,MRef,LoopData)
- end;
- {get_node_info,From,MRef} ->
- Reply=collect_node_info(LoopData),
- reply_and_loop(Reply,From,MRef,LoopData);
- {{try_to_adopt,Tag,Condition},From,MRef} ->
- if
- LoopData#rt.ctrl_ref==undefined -> % We have no control component.
- {Reply,NewLoopData}=do_try_to_adopt(Tag,Condition,LoopData,From),
- reply_and_loop(Reply,From,MRef,NewLoopData);
- true -> % We already have a control component.
- reply_and_loop({error,refused},From,MRef,LoopData)
- end;
- {{confirm_connection,_Tag},From,MRef} ->
- if
- LoopData#rt.ctrl==From -> % It must be from this process!
- Reply=collect_node_info(LoopData),
- reply_and_loop(Reply,From,MRef,LoopData);
- true -> % Strange, some one is joking?
- reply_and_loop({error,refused},From,MRef,LoopData)
- end;
- {{change_options,Options},From,MRef} ->
- case do_change_options(Options,LoopData) of
- stop -> % Can't run alone with these options!
- terminate_overload(LoopData),
- From ! {ok,MRef}; % Don't care if From not a proper pid!
- NewLoopData when is_record(NewLoopData,rt) ->
- reply_and_loop(ok,From,MRef,NewLoopData)
- end;
- {get_status,From,MRef} ->
- Reply={ok,{LoopData#rt.state,LoopData#rt.status}},
- reply_and_loop(Reply,From,MRef,LoopData);
- {get_tracerdata,From,MRef} ->
- case LoopData#rt.tracerdata of
- undefined ->
- reply_and_loop({ok,no_tracerdata},From,MRef,LoopData);
- TracerData ->
- reply_and_loop({ok,TracerData},From,MRef,LoopData)
- end;
- {state,From,MRef} -> % For debugging purposes.
- reply_and_loop(LoopData,From,MRef,LoopData);
-
- {'DOWN',CtrlRef,process,_,_} when CtrlRef==LoopData#rt.ctrl_ref ->
- case do_down_message(LoopData) of
- stop -> % inviso_c gone and we must stop!
- terminate_overload(LoopData),
- exit(running_alone);
- {ok,NewLoopData} ->
- loop1(NewLoopData)
- end;
- {'EXIT',Pid,Reason} ->
- case act_on_exit(Pid,Reason,LoopData) of
- exit ->
- terminate_overload(LoopData),
- exit(Reason);
- NewLoopData when is_record(NewLoopData,rt) ->
- loop1(NewLoopData);
- {NewLoopData,NewTimeOut} when is_record(NewLoopData,rt) ->
- loop(NewLoopData,NewTimeOut)
- end;
- Other -> % Check if it concerns overload.
- if
- LoopData#rt.overload/=?NO_LOADCHECK,
- LoopData#rt.status==running,
- LoopData#rt.state==tracing ->
- {NewLD,NewTimeOut}=
- do_check_overload(LoopData,
- {msg,{Other,LoopData#rt.overload_data}}),
- loop(NewLD,NewTimeOut);
- true ->
- NewTimeOut=calc_diff_to_now(now(),LoopData#rt.next_loadcheck),
- loop(LoopData,NewTimeOut)
- end
- after
- Timeout ->
- loop1(LoopData)
- end.
-
-reply_and_loop(Reply,To,MRef,LoopData) when is_pid(To) ->
- To ! {Reply,MRef},
- loop1(LoopData);
-reply_and_loop(_,_,_,LoopData) -> % Used together with incoming casts.
- loop1(LoopData).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% File transfer process implementation.
-%% =============================================================================
-
-%% Files that are to to be transfered from the runtime component to the control
-%% component are done so by reading them as binaries and sending them with
-%% normal message passing (over distributed Erlang).
-%% Reading the files are done in a process separate to the runtime component,
-%% to both make the code more simple. But also to free up the runtime component.
-%%
-%% This help process must be capable of recognizing the fact that the runtime
-%% component has been suspended, and then of course also discontinue any file
-%% transfere.
-fetch_init(Parent,Files,CollectPid,ChunkSize) ->
- process_flag(trap_exit,true), % We must clean-up.
- process_flag(priority,low), % Lets be careful.
- case fetch_open_file(Files,CollectPid) of
- {ok,FileName,FD,RestFiles} ->
- MRef=erlang:monitor(process,CollectPid),
- fetch_loop(Parent,RestFiles,CollectPid,ChunkSize,FileName,FD,MRef);
- done ->
- fetch_end(CollectPid);
- error ->
- fetch_incomplete(CollectPid)
- end.
-
-fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef) ->
- receive
- {suspend,Parent} -> % The runtime component is suspended.
- file:close(FD), % We must clean-up.
- fetch_incomplete(CollectPid);
- {'DOWN',MRef,process,_,_} -> % The CollectPid terminated!
- file:close(FD); % Close file and terminate.
- {'EXIT',Parent,_Reason} -> % The runtime component terminated.
- file:close(FD),
- fetch_incomplete(CollectPid);
- _ ->
- fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef)
- after
- 0 -> % If non of the above, get to work!
- case file:read(FD,ChunkSize) of
- {ok,Bin} ->
- fetch_send_chunk(CollectPid,Bin),
- case fetch_wait_for_chunk_ack(CollectPid,MRef) of
- ok -> % Collector ready to receive next chunk.
- fetch_loop(Parent,Files,CollectPid,ChunkSize,FName,FD,MRef);
- cancel -> % Send no more files!
- file:close(FD), % Close file, send incomplete, terminate!
- fetch_incomplete(CollectPid);
- 'DOWN' -> % Collector has terminate, stop!
- file:close(FD) % Close file and terminate.
- end;
- eof -> % Ok, go on with the next file.
- file:close(FD),
- fetch_send_eof(CollectPid),
- case fetch_open_file(Files,CollectPid) of
- {ok,NewFName,NewFD,RestFiles} ->
- fetch_loop(Parent,RestFiles,CollectPid,
- ChunkSize,NewFName,NewFD,MRef);
- done ->
- fetch_end(CollectPid);
- error ->
- fetch_incomplete(CollectPid)
- end;
- {error,Reason} -> % Do not continue.
- file:close(FD),
- fetch_send_readerror(CollectPid,FName,Reason),
- fetch_incomplete(CollectPid)
- end
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which opens the next file to be transferred. It also communicates
-%% the opening of the file to the collector process.
-%% We know here that it will be a list of three-tuples. But there is no guarantee
-%% that Dir or FileName are proper strings.
-%% Returns {ok,FileName,FileDescriptor,RemainingFiles} or 'done'.
-fetch_open_file([{FType,Dir,FileName}|RestFiles],CollectPid) ->
- case catch file:open(filename:join(Dir,FileName),[read,raw,binary]) of
- {ok,FD} ->
- CollectPid ! {node(),open,{FType,FileName}},
- {ok,FileName,FD,RestFiles};
- {error,_Reason} ->
- CollectPid ! {node(),open_failure,{FType,FileName}},
- error;
- {'EXIT',_Reason} -> % Faulty Dir or FileName.
- CollectPid ! {node(),open_failure,{FType,FileName}},
- error
- end;
-fetch_open_file([],_CollectPid) ->
- done.
-%% -----------------------------------------------------------------------------
-
-%% A group of help functions sending information to the collector process.
-%% Returns nothing significant.
-fetch_send_chunk(CollectPid,Bin) ->
- CollectPid ! {node(),payload,Bin,self()}.
-%% -----------------------------------------------------------------------------
-
-fetch_send_eof(CollectPid) ->
- CollectPid ! {node(),end_of_file}.
-%% -----------------------------------------------------------------------------
-
-fetch_end(CollectPid) ->
- CollectPid ! {node(),end_of_transmission}.
-%% -----------------------------------------------------------------------------
-
-fetch_send_readerror(CollectPid,FName,Reason) ->
- CollectPid ! {node(),{error,{file_read,{Reason,FName}}}}.
-%% -----------------------------------------------------------------------------
-
-fetch_incomplete(CollectPid) ->
- CollectPid ! {node(),incomplete}.
-%% -----------------------------------------------------------------------------
-
-%% Help function waiting for the collector to respond that it is ready to receive
-%% the next chunk. This is in order to exercise flow control protecting the
-%% collector to get swamped if the node where the collector runs is busy.
-fetch_wait_for_chunk_ack(CollectPid,MRef) ->
- receive
- {CollectPid,chunk_ack} ->
- ok;
- {CollectPid,cancel_transmission} -> % Some problem at collector side.
- cancel;
- {'DOWN',MRef,process,_,_} -> % The collector terminated.
- 'DOWN'
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% First level do-functions, called from the main server loop on incomming
-%% requests.
-%% =============================================================================
-
-%% Function performing the overload check. Returns {NewLoopData,TimeOut}.
-%% Note that this function may also cause a suspend to be carried out if the
-%% loadcheck turns out negative.
-do_check_overload(LD,Data) ->
- case do_check_overload_2(LD#rt.overload,Data) of
- ignore -> % Load check not performed.
- {LD,calc_diff_to_now(now(),LD#rt.next_loadcheck)};
- {ok,Interval} -> % No problem, continue.
- NextLoadCheck=add_to_now(now(),Interval),
- {LD#rt{next_loadcheck=NextLoadCheck},Interval};
- {suspend,Reason} -> % Emergency! suspend, suspend!
- NewLD=do_suspend(LD,Reason),
- {NewLD,infinity}; % No need to do load-checks now!
- {new,NewData,Interval} -> % The overload was restarted or something.
- NextLoadCheck=add_to_now(now(),Interval),
- {LD#rt{overload_data=NewData,next_loadcheck=NextLoadCheck},Interval};
- error -> % Inhibit overload check then.
- {LD#rt{overload=?NO_LOADCHECK},infinity}
- end.
-
-%% Help function performing an overload check. Returns {ok,Interval},
-%% {suspend,Reason}, 'error' ir 'ignore'.
-do_check_overload_2({{Mod,Func},Interval,_,_},Data) ->
- do_check_overload_3(Interval,catch Mod:Func(Data));
-do_check_overload_2({Fun,Interval,_,_},Data) when is_function(Fun) ->
- do_check_overload_3(Interval,catch Fun(Data));
-do_check_overload_2(_,_) -> % Bad loadcheck configuration.
- error. % Stop using load checks then.
-
-do_check_overload_3(Interval,ok) ->
- {ok,Interval};
-do_check_overload_3(Interval,{new,NewData}) ->
- {new,NewData,Interval};
-do_check_overload_3(_Interval,{suspend,Reason}) ->
- {suspend,Reason};
-do_check_overload_3(_Interval,ignore) -> % Loadcheck not triggered.
- ignore;
-do_check_overload_3(_Interval,_) -> % Failure or other return value.
- error. % Stop doing loadchecks from now on.
-%% ------------------------------------------------------------------------------
-
-%% Function setting the trace-pattern according to Args and Flags. Note that
-%% Args can contain regexps which must be expanded here.
-%% Returns a list: [Result], where Result can be: int()|{error,Reason}.
-%% Sometimes an error tuple will represent an entire pattern, sometimes the
-%% pattern will expand to a number of error-tuples.
-do_set_trace_patterns(Args,Flags) ->
- Replies=do_set_trace_patterns_2(Args,Flags,[]),
- lists:reverse(Replies).
-
-do_set_trace_patterns_2([{M,F,Arity,MS}|Rest],Flags,Replies) -> % Option-less.
- do_set_trace_patterns_2([{M,F,Arity,MS,[]}|Rest],Flags,Replies);
-do_set_trace_patterns_2(Mlist = [{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) ->
- case length(Mlist) rem 10 of
- 0 ->
- timer:sleep(100);
- _ ->
- ok
- end,
- %% sleep 100 ms for every 10:th element in the list to let other
- %% processes run since this is a potentially
- %% heavy operation that might result in an unresponsive Erlang VM for
- %% several seconds otherwise
- case load_module_on_option(M,Opts) of
- true -> % Already present, loaded or no option!
- case catch erlang:trace_pattern({M,F,Arity},MS,Flags) of
- No when is_integer(No) ->
- do_set_trace_patterns_2(Rest,Flags,[No|Replies]);
- {'EXIT',Reason} ->
- do_set_trace_patterns_2(Rest,
- Flags,
- [{error,{bad_trace_args,[{M,F,Arity,MS},Reason]}}|
- Replies])
- end;
- false -> % Module not present, or not found!
- do_set_trace_patterns_2(Rest,Flags,[0|Replies])
- end;
-do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_list(M) ->
- do_set_trace_patterns_2([{{void,M},F,Arity,MS,Opts}|Rest],Flags,Replies);
-do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies)
- when is_list(Dir),is_list(M) ->
- case check_pattern_parameters('_',F,Arity,MS) of % We don't want to repeat bad params.
- true ->
- case inviso_rt_lib:expand_regexp(Dir,M,Opts) of % Get a list of real modulnames.
- Mods when is_list(Mods) ->
- MoreReplies=
- do_set_trace_patterns_2(lists:map(fun(Mod)->
- {Mod,F,Arity,MS,Opts}
- end,
- Mods),
- Flags,
- Replies),
- do_set_trace_patterns_2(Rest,Flags,MoreReplies);
- {error,Reason} ->
- do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies])
- end;
- false -> % Bad pattern parameters.
- do_set_trace_patterns_2(Rest,
- Flags,
- [{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies])
- end;
-do_set_trace_patterns_2([Arg|Rest],Flags,Replies) ->
- do_set_trace_patterns_2(Rest,Flags,[{error,{bad_trace_args,Arg}}|Replies]);
-do_set_trace_patterns_2([],_Flags,Replies) ->
- Replies.
-%% -----------------------------------------------------------------------------
-
-%% Help function which sets the trace flags for all processes specifed in Args.
-%% Args shall be a list of {ProcessSpecification,ProcessTraceFlags}.
-%% Returns {ok,Answers} where Answers is a list of integer and error descriptions.
-%% Note that a process specification may be a particular pid or a {global,Name}.
-%% In the case the process does not exist we will fake a zero instead of an
-%% error.
-do_set_trace_flags(Tracer,Args,How) ->
- Fun=fun({Proc,Flags}) ->
- case check_traceflag_pidspec(Proc) of
- {ok,Proc2} -> % Reg-names converted.
- case check_flags(Flags) of
- Flags2 when is_list(Flags2) -> % No error!
- case (catch
- case How of
- true ->
- erlang:trace(Proc2,
- true,
- [{tracer,Tracer}|Flags2]);
- false -> % No tracer of turning off.
- erlang:trace(Proc2,
- false,
- Flags2)
- end) of
- N when is_integer(N) ->
- N;
- {'EXIT',Reason} ->
- if
- is_pid(Proc2) ->
- 0; % Proc2 not alive or not at this node!
- true -> % Otherwise, just error!
- {error,
- {bad_trace_args,
- [Reason,Proc2,How,Flags2,Tracer]}}
- end
- end;
- FlagError ->
- FlagError
- end;
- false -> % Skip it.
- 0; % Indicate that zero processes matched.
- {error,Reason} -> % Bad process specification.
- {error,{bad_process,[Reason,Proc]}}
- end;
- (Faulty) ->
- {error,{bad_process,Faulty}}
- end,
- {ok,lists:map(Fun,Args)}.
-%% ------------------------------------------------------------------------------
-
-%% Function calling API:s in the trace information server. Note that we have
-%% given the responsibility to form a correct functionsname and argument list
-%% to the caller.
-%% Returns whatever the called function returns.
-do_meta_pattern(MPid,{FuncName,ArgList}) ->
- case catch apply(inviso_rt_meta,FuncName,[MPid|ArgList]) of
- {'EXIT',_Reason} ->
- {error,{badarg,{FuncName,ArgList}}};
- Result ->
- Result
- end;
-do_meta_pattern(_MPid,BadArgs) ->
- {error,{bad_args,BadArgs}}.
-%% ------------------------------------------------------------------------------
-
-%% Function removing *all* patterns. Beaware that the one for local patterns
-%% causes a walkthrough of all loaded modules.
-do_clear_trace_patterns() ->
- erlang:trace_pattern({'_','_','_'},false,[local]), %% inc. meta, call_count
- erlang:trace_pattern({'_','_','_'},false,[global]).
-%% ------------------------------------------------------------------------------
-
-%% Function that takes TracerData and initializes the tracing. That can be
-%% opening appropriate logfiles, starting meta-tracer. There must be one
-%% clause here for every "type" of logging we want to be able to do.
-%% Returns the Reply to be forwarded to the caller.
-do_init_tracing(LoopData,TD,{HandlerFun,Data},TiTD) when is_function(HandlerFun) ->
- {NewLoopData,Reply}=
- case do_init_metatracing(TiTD,self()) of
- {ok,MetaPid} ->
- {LoopData#rt{handler={HandlerFun,Data},
- tracerdata=TD,
- meta_tracer=MetaPid,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,ok}]}};
- false -> % No meta tracing requested.
- {LoopData#rt{handler={HandlerFun,Data},
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok}]}};
- {error,Reason} -> % Problems starting meta tracing.
- {LoopData#rt{handler={HandlerFun,Data},
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,{error,Reason}}]}}
- end,
- send_event(state_change,NewLoopData), % Send to subscribing processes.
- {NewLoopData,Reply};
-do_init_tracing(LoopData,TD,{Type,Parameters},TiTD) when Type==ip;Type==file ->
- case check_traceport_parameters(Type,Parameters) of
- ok ->
- case catch trace_port(Type,Parameters) of
- Fun when is_function(Fun) ->
- case catch Fun() of
- Port when is_port(Port) -> % Ok, our trace-port is open.
- {NewLoopData,Reply}=
- case do_init_metatracing(TiTD,Port) of
- {ok,MetaPid} ->
- {LoopData#rt{tracer_port=Port,
- tracerdata=TD,
- meta_tracer=MetaPid,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,ok}]}};
- false -> % No meta tracing requested.
- {LoopData#rt{tracer_port=Port,
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok}]}};
- {error,Reason} -> % Problems starting meta tracing.
- {LoopData#rt{tracer_port=Port,
- tracerdata=TD,
- state=tracing},
- {ok,[{trace_log,ok},{ti_log,{error,Reason}}]}}
- end,
- send_event(state_change,NewLoopData),
- {NewLoopData,Reply};
- {'EXIT',Reason} ->
- {LoopData,{error,{bad_port_fun,[Parameters,Reason]}}}
- end;
- {'EXIT',Reason} ->
- {LoopData,{error,{bad_port_args,[Parameters,Reason]}}}
- end;
- {error,Reason} -> % Bad traceport parameters.
- {LoopData,{error,Reason}}
- end.
-
-%% Help function that starts the meta-tracing. Note that the runtime component
-%% will becom linked to it.
-%% Currently the meta tracer handles two types, 'file' and 'relay'.
-%% Note that Tracer tells the meta tracer where regular trace messages shall be
-%% sent. This is because the meta tracer is capable of appending a {tracer,Tracer}
-%% action term to meta match specs.
-do_init_metatracing(LogSpec={_Type,_Arg},Tracer) ->
- case inviso_rt_meta:start(LogSpec,Tracer) of
- {ok,MetaPid} ->
- {ok,MetaPid};
- {error,Reason} ->
- {error,Reason}
- end;
-do_init_metatracing({Type,Arg,{InitPublLDmfa,RemovePublLDmf,CleanPublLDmf}},Tracer)->
- case inviso_rt_meta:start({Type,Arg},Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) of
- {ok,MetaPid} ->
- {ok,MetaPid};
- {error,Reason} ->
- {error,Reason}
- end;
-do_init_metatracing(void,_) -> % Means no meta tracer.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function that stops all tracing and closes all open files. This function
-%% can't fail :-) It tries as hard as it can.
-%% This function also kills the autostarter process if one exists. Otherwise it
-%% will not be possible from a control component to end an ongoing autostarted
-%% tracing.
-%% Returns a new loopdata structure since stopping tracing involves updating it.
-do_stop_tracing(LoopData) ->
- do_stop_tracing_kill_autostarter(LoopData#rt.auto_starter),
- do_clear_trace_flags(), % Do not generate any more traces.
- NewLoopData1=do_stop_tracing_tracelog(LoopData),
- NewLoopData2=do_stop_tracing_metatracing(NewLoopData1),
- NewLoopData3=NewLoopData2#rt{state=idle,auto_starter=undefined},
- send_event(state_change,NewLoopData3),
- NewLoopData3.
-
-do_stop_tracing_tracelog(LoopData=#rt{tracer_port=Port}) when is_port(Port) ->
- trace_port_control(Port,flush), % Write buffered trace messages.
- catch port_close(Port),
- LoopData#rt{tracer_port=undefined};
-do_stop_tracing_tracelog(LoopData) ->
- LoopData#rt{handler=undefined}.
-
-do_stop_tracing_metatracing(LoopData=#rt{meta_tracer=MPid}) when is_pid(MPid) ->
- inviso_rt_meta:stop(MPid),
- LoopData#rt{meta_tracer=undefined};
-do_stop_tracing_metatracing(LoopData) -> % No meta tracer running!
- LoopData.
-
-%% Help function killing the autostarter, if one is active.
-do_stop_tracing_kill_autostarter(P) when is_pid(P) ->
- exit(P,stop_tracing);
-do_stop_tracing_kill_autostarter(_) -> % No autostarter, do nothing.
- true.
-%% -----------------------------------------------------------------------------
-
-%% Help function implementing suspending the runtime component.
-%% Returns a new loopdata structure.
-do_suspend(LD,Reason) ->
- do_clear_trace_flags(), % If no process flags, no output!
- do_suspend_metatracer(LD#rt.meta_tracer),
- do_suspend_fetchers(LD#rt.fetchers),
- do_stop_tracing_kill_autostarter(LD#rt.auto_starter),
- NewLD=LD#rt{fetchers=[],status={suspended,Reason},auto_starter=undefined},
- send_event(state_change,NewLD), % Notify subscribers.
- NewLD.
-
-do_suspend_metatracer(MetaTracer) when is_pid(MetaTracer) ->
- inviso_rt_meta:suspend(MetaTracer); % This makes it suspended.
-do_suspend_metatracer(_) ->
- true.
-
-do_suspend_fetchers([FetcherPid|Rest]) ->
- FetcherPid ! {suspend,self()}, % This makes it terminate.
- do_suspend_fetchers(Rest);
-do_suspend_fetchers([]) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Function that stops all tracing, removes all trace-patterns and removes all
-%% logfiles. The idea is to return the runtime component to the 'new' state.
-do_clear(LoopData,Opts) when is_list(Opts) ->
- NewLoopData=do_stop_tracing(LoopData), % First stop tracing, if tracing.
- case lists:member(keep_trace_patterns,Opts) of
- false ->
- do_clear_trace_patterns();
- _ ->
- true
- end,
- case lists:member(keep_log_files,Opts) of
- false ->
- if
- NewLoopData#rt.tracerdata/=undefined ->
- do_delete_logs(NewLoopData#rt.tracerdata);
- true -> % If no tracerdata, nothing to remove!
- true % Do nothing then.
- end;
- _ ->
- true
- end,
- NewLoopData#rt{state=new,tracerdata=undefined};
-do_clear(LoopData,_Opts) -> % Faulty Opts.
- do_clear(LoopData,[]). % Then just ignore the options.
-%% -----------------------------------------------------------------------------
-
-%% Function which takes a tracerdata, either our own or a "suggested"
-%% and tries to find the corresponding files. Note that the return value only
-%% contains "types" of logs that the tracerdata is pointing out. Hence
-%% is there no ti-log, no one will be mentioned in the return value.
-do_list_logs(TracerData) -> % Handles both list and tuple.
- case translate_td(TracerData) of
- {ok,LogTD,TiTD} ->
- {TraceDir,TraceLogs}=list_logs_tracelog(LogTD),
- {TiDir,TiLogs}=list_logs_tilog(TiTD),
- case {TraceLogs,TiLogs} of
- {no_log,no_log} -> % Tracerdata not generating logs!
- {ok,no_log};
- {_,no_log} -> % No ti logs.
- {ok,[{trace_log,TraceDir,TraceLogs}]};
- {no_log,_} -> % Only ti-logs, unusual!
- {ok,[{ti_log,TiDir,TiLogs}]};
- _ -> % Both trace and ti logs.
- {ok,[{trace_log,TraceDir,TraceLogs},{ti_log,TiDir,TiLogs}]}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function implementing fetching logfiles using distributed Erlang.
-%% This function works for both situations, a list of specific files are
-%% requested, or a tracerdata is specified.
-%% Returns {Reply,NewLoopData}.
-do_fetch_log(LD,CollectPid,What) ->
- if
- LD#rt.state/=tracing ->
- case is_list_of_files_or_tracerdata(What) of
- files ->
- FetcherPid=do_fetch_log_listoffiles(CollectPid,What),
- {{ok,FetcherPid},add_fetcher_ld(FetcherPid,LD)};
- tracerdata ->
- case do_fetch_log_tracerdata(CollectPid,What) of
- {Reply,FetcherPid} when is_pid(FetcherPid) ->
- {Reply,add_fetcher_ld(FetcherPid,LD)};
- {Reply,_} -> % No fetch process was started.
- {Reply,LD}
- end;
- false -> % It is an empty list!
- {{complete,no_log},LD};
- error -> % Incorrect parameter.
- {{error,badarg},LD}
- end;
- true -> % No transfere during tracing.
- {{error,tracing},LD}
- end.
-
-%% Function taking tracerdata to find out what files to send over to the RemotePid.
-%% Note that we will not go back to the loop function from here but rather call
-%% the fetch_loop instead, prepresenting the fetch-log state. Unless we encounter
-%% a problem.
-do_fetch_log_tracerdata(CollectPid,TracerData) ->
- case do_list_logs(TracerData) of
- {ok,no_log} ->
- {{complete,no_log},void};
- {ok,Logs} -> % Ok, some trace_log and ti_log.
- FetcherPid=do_fetch_log_listoffiles(CollectPid,Logs),
- {{ok,FetcherPid},FetcherPid};
- {error,Reason} -> % Problem with tracerdata!
- {{error,Reason},void}
- end.
-
-do_fetch_log_listoffiles(CollectPid,FileSpec) ->
- ExpandedFileSpec=do_fetch_log_expand_filespec(FileSpec),
-%% !!! try out different ChunkSizes
-% ChunkSize = 60,
-% ChunkSize = 7*1024,
- ChunkSize=1024,
- _Fetcher=spawn_link(?MODULE,
- fetch_init,
- [self(),ExpandedFileSpec,CollectPid,ChunkSize]).
-
-%% Help function which expands the list of logs to have tags in front of every
-%% file, as required by the fetch_loop.
-do_fetch_log_expand_filespec(Logs) ->
- TraceLogs=
- case lists:keysearch(trace_log,1,Logs) of
- {value,{_,Dir1,Logs1}} -> % There is a list of trace-logs.
- lists:map(fun(File)->{trace_log,Dir1,File} end,Logs1);
- false -> % No trace-logs!
- []
- end,
- TiLogs=
- case lists:keysearch(ti_log,1,Logs) of
- {value,{_,Dir2,Logs2}} ->
- lists:map(fun(File)->{ti_log,Dir2,File} end,Logs2);
- false ->
- []
- end,
- TiLogs++TraceLogs.
-
-%% ------------------------------------------------------------------------------
-
-%% Function that removes all logfiles associated with a certain tracerdata.
-do_delete_logs(TracerDataOrLogList) ->
- case is_list_of_files_or_tracerdata(TracerDataOrLogList) of
- tracerdata ->
- case translate_td(TracerDataOrLogList) of
- {ok,LogTD,TiTD} ->
- case {list_logs_tracelog(LogTD),list_logs_tilog(TiTD)} of
- {{_,no_log},{_,no_log}} -> % No logs nowhere!
- {ok,no_log};
- {{LogDir,LogFiles},{_,no_log}} -> % No ti.
- {ok,[{trace_log,delete_files(LogDir,LogFiles)}]};
- {{_,no_log},{TiDir,TiFiles}} ->
- {ok,[{ti_log,delete_files(TiDir,TiFiles)}]};
- {{LogDir,LogFiles},{TiDir,TiFiles}} ->
- {ok,[{trace_log,delete_files(LogDir,LogFiles)},
- {ti_log,delete_files(TiDir,TiFiles)}]}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- files -> % It is [{trace_log,Dir,Files},..
- if
- is_list(hd(TracerDataOrLogList)) -> % Just a list of files.
- {ok,delete_files(".",TracerDataOrLogList)};
- is_tuple(hd(TracerDataOrLogList)) -> % A "modern" logspec.
- case {lists:keysearch(trace_log,1,TracerDataOrLogList),
- lists:keysearch(ti_log,1,TracerDataOrLogList)} of
- {false,false} -> % Hmm, no logs specified!
- {ok,[]}; % Easy response!
- {{value,{_,LogDir,LogFiles}},false} ->
- {ok,[{trace_log,delete_files(LogDir,LogFiles)}]};
- {false,{value,{_,TiDir,TiFiles}}} ->
- {ok,[{ti_log,delete_files(TiDir,TiFiles)}]};
- {{value,{_,LogDir,LogFiles}},{value,{_,TiDir,TiFiles}}} ->
- {ok,[{trace_log,delete_files(LogDir,LogFiles)},
- {ti_log,delete_files(TiDir,TiFiles)}]}
- end
- end;
- false -> % Can't tell which!
- {ok,[]};
- error ->
- {error,{badarg,TracerDataOrLogList}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling the request when a control component wishing to take
-%% control over this already existing control component. It does not matter
-%% what state it is in. It can very well already be tracing.
-%% Returns {Reply,NewLoopData}.
-%% Where the Reply tells the control component wether it took control of it
-%% or not. {node_info,node(),self(),Vsn,State,Status,{tag,Tag}} means that we
-%% can be adopted (and more precisely considers ourselves being adopted now).
-do_try_to_adopt(Tag,if_ref,LoopData=#rt{tag=Tag},_Ctrl) ->
- {{error,{wrong_reference,LoopData#rt.tag}},LoopData};
-do_try_to_adopt(NewTag,_Condition,LoopData,CtrlPid) ->
- case LoopData#rt.timer_ref of % Do we have a running-alone timer?
- undefined -> % No we don't.
- true;
- TimerRef ->
- timer:cancel(TimerRef)
- end,
- CtrlRef=erlang:monitor(process,CtrlPid), % Lets monitor our new "master"!
- {DepVal,_}=LoopData#rt.dependency,
- {node_info,Node,Pid,VSN,State,Status,Tag}=collect_node_info(LoopData),
- NewLoopData=
- LoopData#rt{dependency={DepVal,node(CtrlPid)},
- ctrl=CtrlPid,
- ctrl_ref=CtrlRef, % Monitoring our new master.
- tag=NewTag, % Use this tag from now on.
- timer_ref=undefined},
- {{node_info,Node,Pid,VSN,State,Status,{tag,Tag}},NewLoopData}.
-%% -----------------------------------------------------------------------------
-
-%% Function changing parameters accoring to a new options list. Note that we
-%% can not change control component if the one we have is still working.
-%% We can however of course change how this runtime component will react to
-%% a running alone scenario.
-%% Returns 'stop' or NewLoopData.
-do_change_options(Options,LoopData) ->
- NewLoopData=read_option_list(Options,LoopData),
- if
- NewLoopData/=LoopData -> % Some options changed.
- case do_change_options_ctrl(LoopData,NewLoopData) of
- stop ->
- stop;
- {ok,NewLoopData2} ->
- NewLoopData3=do_change_options_overload(LoopData,NewLoopData2),
- NewLoopData3#rt{next_loadcheck=now()} % Force a load check next.
- end;
- true ->
- LoopData
- end.
-
-%% Help function which sets up the new dependencies. Note that we only do that
-%% if do not have a working control component.
-%% Returns {ok,NewLoopData} or 'stop'.
-do_change_options_ctrl(OldLD,NewLD) ->
- if
- OldLD#rt.timer_ref/=undefined -> % No control and waiting to terminate.
- timer:cancel(OldLD#rt.timer_ref),
- do_down_message(NewLD#rt{timer_ref=undefined});
- OldLD#rt.ctrl==undefiend -> % No control component.
- do_down_message(NewLD);
- true -> % We have a working control component!
- {ok,NewLD}
- end.
-
-do_change_options_overload(OldLD,NewLD) ->
- if
- OldLD#rt.overload/=NewLD#rt.overload ->
- terminate_overload(OldLD),
- NewOverloadData=initialize_overload(NewLD),
- NewLD#rt{overload_data=NewOverloadData};
- true -> % No changes done.
- NewLD
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling an incoming DOWN message from our control component.
-%% If the runtime component is not allowed to run without a control component, it
-%% simply terminates which closes the trace-port and process trace flags are
-%% therefore automatically removed.
-%% Returns 'stop' or a {ok,NewLoopData} structure.
-do_down_message(LoopData) ->
- case LoopData#rt.dependency of
- {0,_} -> % Not allowed to run without controller.
- stop;
- {infinity,_} -> % Don't care. Just remove the controller.
- {ok,LoopData#rt{ctrl=undefined,ctrl_ref=undefined}};
- {TimeOut,_} -> % Allowed to run TimeOut ms alone.
- {ok,TimerRef}=timer:exit_after(TimeOut,self(),running_alone),
- {ok,LoopData#rt{timer_ref=TimerRef,ctrl=undefined,ctrl_ref=undefined}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling incomming exit signals. We can expect exit signals from the
-%% following: Our parent supervisor (runtime_tools_sup), a meta-tracer process,
-%% a logfile fetcher process, or the auto_starter.
-%% A trace-port may also generate an exit signal.
-%% In addition it is possible that an overload mechanism generates exit-signals.
-%% We can also get the running_alone exit signal from our self. This is the
-%% situation if our control component has terminated and this runtime component
-%% is not allowed to exist on its own for ever.
-%% Also note that after we have stopped tracing, for any reason, it is not
-%% impossible that we receive the EXIT signals from still working parts that
-%% we are now shuting down. This is no problem, the code will mearly update
-%% the loopdata structure once again.
-%% Returns 'exit' indicating that the runtime component shall terminate now,
-%% {NewLoopData,NewTimeOut} if the exit-signal resulted in an overload check, or
-%% a new loopdata structure shall we ignore the exit, or it simply resulted in
-%% a state-change.
-act_on_exit(Parent,_Reason,#rt{parent=Parent}) ->
- exit;
-act_on_exit(_Pid,running_alone,_LoopData) ->
- exit;
-act_on_exit(MetaTracer,_Reason,LoopData=#rt{meta_tracer=MetaTracer}) ->
- LoopData#rt{meta_tracer=undefined}; % It does not exit anylonger.
-act_on_exit(Port,Reason,LoopData=#rt{tracer_port=Port}) ->
- send_event({port_down,node(),Reason},LoopData),
- _NewLoopData=do_stop_tracing(LoopData);
-act_on_exit(AutoStarter,_Reason,LoopData=#rt{auto_starter=AutoStarter}) ->
- LoopData#rt{auto_starter=undefined}; % The autostarter has terminated.
-act_on_exit(Pid,Reason,LoopData) ->
- case remove_fetcher_ld(Pid,LoopData) of
- {true,NewLoopData} -> % Yes it really was a fetcher.
- NewLoopData;
- false -> % No it was not a fetcher.
- act_on_exit_overload(Pid,Reason,LoopData)
- end.
-
-%% Help function checking if this exit has anything to do with an overload
-%% mechanism. Note that here we run the overload mechanism regardless of
-%% if we are tracing or not. This because an exit signal from the overload
-%% must most likely always be handled.
-act_on_exit_overload(Pid,Reason,LoopData) ->
- if
- LoopData#rt.overload/=?NO_LOADCHECK ->
- {_NewLD,_NewTimeOut}=
- do_check_overload(LoopData,
- {'EXIT',{Pid,Reason,LoopData#rt.overload_data}});
- true -> % Overload not in use.
- LoopData
- end.
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function which calculates a new now-tuple by adding Interval milliseconds
-%% to the first argument. Note that Interval may be 'infinity' too.
-%% Returns a new now-tuple or "bigvalue" which is greater than any now-tuple.
-add_to_now({MegSec,Sec,MicroSec},Interval) when is_integer(Interval) ->
- NewSec=Sec+(Interval div 1000),
- if
- NewSec>=1000000 ->
- {MegSec+1,NewSec-1000000,MicroSec};
- true ->
- {MegSec,NewSec,MicroSec}
- end;
-add_to_now(_,infinity) ->
- "bigvalue".
-%% ------------------------------------------------------------------------------
-
-%% Help function calculating the difference in milliseconds between its first
-%% and second argument. This is useful when calculating an after timeout value
-%% from current now() and next_loadcheck value.
-calc_diff_to_now(T1={_,_,_},T2={_,_,_}) ->
- TimeOut1=timer:now_diff(T2,T1), % The difference in microseconds.
- if
- TimeOut1<0 ->
- 0;
- true -> % Make milliseconds out of it.
- TimeOut1 div 1000
- end;
-calc_diff_to_now(_T1,_) -> % Next loadcheck is not activated.
- infinity. % The the after timeout is infinity.
-%% ------------------------------------------------------------------------------
-
-
-%% Help function returning information about this runtime component.
-collect_node_info(#rt{vsn=VSN,state=State,status=Status,tag=Tag}) ->
- {node_info,node(),self(),VSN,State,Status,Tag}.
-%% ------------------------------------------------------------------------------
-
-%% Help function sending information to the control component that state/status
-%% change has occurred. Returns nothing significant.
-send_event(state_change,LoopData=#rt{ctrl=CtrlPid}) when is_pid(CtrlPid) ->
- Event={trace_event,{state_change,node(),{LoopData#rt.state,LoopData#rt.status}}},
- CtrlPid ! Event;
-send_event(Event,#rt{ctrl=CtrlPid}) when is_pid(CtrlPid) ->
- CtrlPid ! {event,Event};
-send_event(_,_) -> % We have no control to send to!
- true. % Maybe tracing alone after autostart.
-%% ------------------------------------------------------------------------------
-
-%% Help function initializing the overload protection mechanism. This may be
-%% necessary if it is a port program or similar. Returns {ok,Data} or 'void'.
-%% The datastructure vill be given to LoadMF as argument whenever loadchecks
-%% are done.
-initialize_overload(#rt{overload={_MF,_Interval,{M,F,Args},_RemoveMFA}}) ->
- case catch apply(M,F,Args) of
- {ok,Data} ->
- Data;
- _ -> % 'EXIT' or other faulty returnvalue.
- void
- end;
-initialize_overload(_) ->
- void.
-%% ------------------------------------------------------------------------------
-
-%% Help function which terminates an overload protection mechanism.
-%% Returns nothing significant.
-terminate_overload(#rt{overload={_MF,_Interval,_InitMFA,{M,F,Args}},
- overload_data=Data}) ->
- catch apply(M,F,[Data|Args]), % Interested in the side-effect.
- true;
-terminate_overload(_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-
-%% Help function which checks that a process specified for trace flags is correct.
-%% Either the built-in "aliases" for groups of processes, a pid, a locally registered
-%% name. This function also works for globally registered names. It must then
-%% first be established that the process is local for this node before setting any
-%% process flags.
-%% Returns {ok,PidSpec}, 'false' or {error,Reason}.
-check_traceflag_pidspec(all) -> {ok,all};
-check_traceflag_pidspec(new) -> {ok,new};
-check_traceflag_pidspec(existing) -> {ok,existing};
-check_traceflag_pidspec(Name) when is_atom(Name) ->
- check_traceflag_pidspec({local,Name});
-check_traceflag_pidspec({local,A}) when is_atom(A) ->
- case whereis(A) of
- undefined -> % Then it is considered faulty.
- {error,{nonexistent_name,A}};
- Pid when is_pid(Pid) ->
- {ok,Pid}
- end;
-check_traceflag_pidspec({global,Name}) when is_atom(Name) ->
- case global:whereis_name(Name) of
- undefined -> % Then the name does not exist at all.
- {error,{nonexistent_name,{global,Name}}};
- Pid when is_pid(Pid) -> % Ok, but must check that it is here.
- if
- node()==node(Pid) ->
- {ok,Pid};
- true -> % Pid is not at this node.
- false % Not an error but cant be used.
- end
- end;
-check_traceflag_pidspec(Pid) when is_pid(Pid) ->
- {ok,Pid};
-check_traceflag_pidspec(Proc) ->
- {error,{faulty,Proc}}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all trace flags from all processes. Useful in connection
-%% with suspend. Returns nothing significant.
-do_clear_trace_flags() ->
- erlang:trace(all, false, [all]).
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that only valid process trace flags are mentioned.
-%% In order to create better fault reports.
-%% Returns a list of the approved flags, or {error,Reason}.
-check_flags(Flags) ->
- check_flags_2(Flags,Flags).
-
-check_flags_2([send|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2(['receive'|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([call|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([return_to|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([procs|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([garbage_collection|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([running|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([set_on_spawn|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([set_on_first_spawn|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([set_on_link|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([timestamp|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([arity|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([silent|Rest],Flags) -> check_flags_2(Rest,Flags);
-check_flags_2([],Flags) -> Flags;
-check_flags_2([Faulty|_],_Flags) -> {error,{bad_flag,Faulty}}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks parameters to erlang:trace_pattern. The purpose of
-%% the function is to avoid to get multiple error return values in the return
-%% list for a pattern used together with a regexp expanded module name.
-check_pattern_parameters(Mod,Func,Arity,MS) ->
- MSresult = check_MS(MS),
- MFAresult = check_MFA(Mod,Func,Arity),
- MFAresult and MSresult.
-
-check_MS(MS) when is_list(MS) -> true;
-check_MS(true) -> true;
-check_MS(false) -> true.
-
-check_MFA('_','_','_') -> true;
-check_MFA(Mod,'_','_') when is_atom(Mod) -> true;
-check_MFA(Mod,'_',A) when is_atom(Mod), is_integer(A) -> false;
-check_MFA(Mod,F,'_') when is_atom(Mod), is_atom(F) -> true;
-check_MFA(Mod,F,A) when is_atom(Mod), is_atom(F), is_integer(A) -> true.
-
-%% -----------------------------------------------------------------------------
-
-%% Help function finding out if Mod is loaded, and if not, if it can successfully
-%% be loaded. The Opts list can prevent modules from being loaded.
-%% Returns 'true' or 'false'.
-load_module_on_option(Mod,Opts) when is_list(Opts) ->
- case lists:member(no_loadcheck,Opts) of
- true -> % Then just skip this, return true.
- true;
- false ->
- case erlang:module_loaded(Mod) of
- true ->
- true; % It is loaded, do no more.
- false ->
- case lists:member(only_loaded,Opts) of
- true -> % Then, make no attempts to load.
- false;
- false -> % Try to load!
- case code:ensure_loaded(Mod) of
- {module,_Mod} -> % Successfully loaded!
- true;
- {error,_Reason} ->
- false
- end
- end
- end
- end;
-load_module_on_option(Mod,_Opts) -> % Most likely Opts not a list!
- load_module_on_option(Mod,[]). % Call without options.
-%% -----------------------------------------------------------------------------
-
-%% Help function taking a tuplelist of options turning them into a loopdata
-%% structure. Returns the loopdata structure with the new values changed.
-read_option_list([],LD) -> % Done, return loopdata.
- LD;
-read_option_list([{dependency,{Value,Node}}|Rest],LD) ->
- read_option_list(Rest,LD#rt{dependency={Value,Node}});
-read_option_list([{dependency,Value}|Rest],LD) when is_integer(Value);Value==infinity ->
- read_option_list(Rest,LD#rt{dependency={Value,node()}});
-read_option_list([overload|Rest],LD) -> % So that we can remove loadcheck.
- read_option_list(Rest,LD#rt{overload=?NO_LOADCHECK});
-read_option_list([{overload,{MF,Interval}}|Rest],LD)
- when is_integer(Interval);Interval==infinity ->
- read_option_list(Rest,LD#rt{overload={MF,Interval,void,void}});
-read_option_list([{overload,{MF,Interval,InitMFA,RemoveMFA}}|Rest],LD)
- when is_integer(Interval);Interval==infinity ->
- read_option_list(Rest,LD#rt{overload={MF,Interval,InitMFA,RemoveMFA}});
-read_option_list([{overload,Interval}|Rest],LD)
- when is_integer(Interval);Interval==infinity ->
- read_option_list(Rest,LD#rt{overload={fun ?DEFAULT_OVERLOAD_FUNC/1,
- Interval,
- void,
- void}});
-read_option_list([_|Rest],LD) -> % Unknown option.
- read_option_list(Rest,LD).
-%% -----------------------------------------------------------------------------
-
-%% Help function which returns the version number for the runtime_tools
-%% application. Since it is called from within the runtime_tools application
-%% we can be "sure" that it really exists.
-get_application_vsn() ->
- {value,{_,_,VSN}}=lists:keysearch(runtime_tools,1,application:loaded_applications()),
- VSN.
-%% -----------------------------------------------------------------------------
-
-%% Help function that examines an argument to determine if it is a list of files
-%% or tracerdata. This since they are both complex structures, looking alike.
-%% Returns 'tracerdata', 'files', 'false' or 'error'. Error is returned if it
-%% can not be decided which it is.
-is_list_of_files_or_tracerdata(What) ->
- case inviso_rt_lib:is_tracerdata(What) of
- true ->
- tracerdata;
- false ->
- if
- What==[] ->
- false;
- is_list(What),is_list(hd(What)) ->
- files;
- is_list(What) ->
- case lists:keysearch(trace_log,1,What) of
- {value,_} ->
- files;
- false ->
- case lists:keysearch(ti_log,1,What) of
- {value,_} ->
- files;
- false ->
- error % Neither tracerdata nor list of files.
- end
- end;
- true ->
- error
- end
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which removes all files in the ListOfFiles, assuming they
-%% are located in Dir.
-%% Returns a list of [{ok,FileName},...{error,Reason},...]
-delete_files(Dir,ListOfFiles) ->
- delete_files_2(Dir,ListOfFiles, []).
-
-delete_files_2(Dir,[File|Tail],Reply) when is_list(Dir),is_list(File) ->
- case catch file:delete(filename:join(Dir,File)) of
- ok ->
- delete_files_2(Dir,Tail,[{ok,File}|Reply]);
- {error,Posix} ->
- delete_files_2(Dir,Tail,[{error,{Posix,File}}|Reply]);
- {'EXIT',_Reason} -> % Probably not proper string.
- delete_files_2(Dir,Tail,[{error,{badarg,[Dir,File]}}|Reply])
- end;
-delete_files_2(Dir,[Faulty|Tail],Reply) ->
- delete_files_2(Dir,Tail,[{error,{badarg,[Dir,Faulty]}}|Reply]);
-delete_files_2(_,[],Reply) ->
- Reply.
-%% -----------------------------------------------------------------------------
-
-%% Help function which lists all trace logs belonging to this tracerdata.
-%% Note that this function operates on internal LogTD structures.
-list_logs_tracelog({file,FileName}) when is_list(FileName) ->
- case file:read_file_info(FileName) of
- {ok,_} -> % The file exists.
- {filename:dirname(FileName),[filename:basename(FileName)]};
- _ -> % The file does not exist
- {filename:dirname(FileName),[]}
- end;
-list_logs_tracelog({file,Wrap}) when is_tuple(Wrap),element(2,Wrap)==wrap ->
- case {element(1,Wrap),element(3,Wrap)} of
- {FileName,Tail} when is_list(FileName),is_list(Tail) ->
- case catch {filename:dirname(FileName),list_wrapset(FileName,Tail)} of
- {'EXIT',_Reason} -> % Garbage in either lists.
- {"",no_log}; % Interpret as no log for tracerdata.
- Tuple ->
- Tuple
- end;
- _ ->
- {"",no_log}
- end;
-list_logs_tracelog(void) -> % Trace log not used.
- {"",no_log};
-list_logs_tracelog(_) -> % Some fun or similar.
- {"",no_log}. % Then there are no files to report.
-%% -----------------------------------------------------------------------------
-
-%% Help function which lists all ti-files belonging to this tracerdata.
-%% Note that this function operates on the internal TiTD structure.
-list_logs_tilog(TiTD)
- when tuple_size(TiTD)>=2,element(1,TiTD)==file,is_list(element(2,TiTD)) ->
- FileName=element(2,TiTD),
- case file:read_file_info(FileName) of
- {ok,_} -> % Yes the file exists.
- {filename:dirname(FileName),[filename:basename(FileName)]};
- _ ->
- {filename:dirname(FileName),[]}
- end;
-list_logs_tilog(void) -> % Internal representation for
- {"",no_log}; % ti-file not in use.
-list_logs_tilog(_) ->
- {"",no_log}.
-%% -----------------------------------------------------------------------------
-
-%% Help function which lists all files belonging to the wrap-set specified by
-%% Prefix and Suffix. Note that there can be a directory in Prefix as well.
-%% Will fail if either of Prefix or Suffix are not proper strings.
-%% Returns a list of files, without dirname.
-list_wrapset(Prefix,Suffix) ->
- Name=filename:basename(Prefix),
- Dirname=filename:dirname(Prefix),
- case file:list_dir(Dirname) of
- {ok,Files} ->
- RegExp="^"++list_wrapset_escapes(Name)++"[0-9]+"++
- list_wrapset_escapes(Suffix)++"$",
- list_wrapset_2(Files,RegExp);
- {error,_Reason} -> % Translate this to no files!
- []
- end.
-
-list_wrapset_2([File|Rest],RegExp) ->
- Length=length(File),
- case re:run(File,RegExp) of
- {match,[{0,Length}]} -> % This is a member of the set.
- [File|list_wrapset_2(Rest,RegExp)];
- _ ->
- list_wrapset_2(Rest,RegExp)
- end;
-list_wrapset_2([],_) ->
- [].
-
-%% Help function which inserts escape characters infront of characters which
-%% will otherwise be missinterpreted by the regexp function as meta rather than
-%% just the character itself.
-list_wrapset_escapes([$.|Rest]) ->
- [$\\,$.|list_wrapset_escapes(Rest)];
-list_wrapset_escapes([Char|Rest]) ->
- [Char|list_wrapset_escapes(Rest)];
-list_wrapset_escapes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-
-
-
-
-%% ==============================================================================
-%% Handler functions for implementing simple trace-message handlers.
-%% ==============================================================================
-%%
-%% A handler must be a function taking two arguments. The first is the trace-
-%% message. The second is datastructure used by the handler. The handler shall
-%% returns (possibly) new datastructure.
-
-%% ------------------------------------------------------------------------------
-%% Function implementing a relayer. This function is used to creat a fun handler
-%% if the relay option is used in tracer-data.
-%% ------------------------------------------------------------------------------
-relay_handler(Msg,Tracer) ->
- Tracer ! Msg,
- Tracer.
-
-%% ------------------------------------------------------------------------------
-%% Function implementing a default terminal io handler.
-%% ------------------------------------------------------------------------------
-
-dhandler(end_of_trace, Out) ->
- Out;
-dhandler(Trace, Out) when element(1, Trace) == trace,
- tuple_size(Trace) >= 3 ->
- dhandler1(Trace, tuple_size(Trace), Out);
-dhandler(Trace, Out) when element(1, Trace) == trace_ts,
- tuple_size(Trace) >= 4 ->
- dhandler1(Trace, tuple_size(Trace)-1, Out);
-dhandler(Trace, Out) when element(1, Trace) == drop,
- tuple_size(Trace) == 2 ->
- io:format(Out, "*** Dropped ~p messages.~n", [element(2,Trace)]),
- Out;
-dhandler(Trace, Out) when element(1, Trace) == seq_trace,
- tuple_size(Trace) >= 3 ->
- SeqTraceInfo = case Trace of
- {seq_trace, Lbl, STI, TS} ->
- io:format(Out, "SeqTrace ~p [~p]: ",
- [TS, Lbl]),
- STI;
- {seq_trace, Lbl, STI} ->
- io:format(Out, "SeqTrace [~p]: ",
- [Lbl]),
- STI
- end,
- case SeqTraceInfo of
- {send, Ser, Fr, To, Mes} ->
- io:format(Out, "(~p) ~p ! ~p [Serial: ~p]~n",
- [Fr, To, Mes, Ser]);
- {'receive', Ser, Fr, To, Mes} ->
- io:format(Out, "(~p) << ~p [Serial: ~p, From: ~p]~n",
- [To, Mes, Ser, Fr]);
- {print, Ser, Fr, _, Info} ->
- io:format(Out, "-> ~p [Serial: ~p, From: ~p]~n",
- [Info, Ser, Fr]);
- Else ->
- io:format(Out, "~p~n", [Else])
- end,
- Out;
-dhandler(_Trace, Out) ->
- Out.
-
-dhandler1(Trace, Size, Out) ->
-%%%! Self = self(),
- From = element(2, Trace),
- case element(3, Trace) of
- 'receive' ->
- case element(4, Trace) of
- {dbg,ok} -> ok;
- Message -> io:format(Out, "(~p) << ~p~n", [From,Message])
- end;
- 'send' ->
- Message = element(4, Trace),
- case element(5, Trace) of
-%%%! This causes messages to disappear when used by ttb (observer). Tests
-%%%! so far show that there is no difference in results with dbg even if I
-%%%! comment it out, so I hope this is only some old code which isn't
-%%%! needed anymore... /siri
-%%%! Self -> ok;
- To -> io:format(Out, "(~p) ~p ! ~p~n", [From,To,Message])
- end;
- call ->
- case element(4, Trace) of
- MFA when Size == 5 ->
- Message = element(5, Trace),
- io:format(Out, "(~p) call ~s (~p)~n",
- [From,ffunc(MFA),Message]);
- MFA ->
- io:format(Out, "(~p) call ~s~n", [From,ffunc(MFA)])
- end;
- return -> %% To be deleted...
- case element(4, Trace) of
- MFA when Size == 5 ->
- Ret = element(5, Trace),
- io:format(Out, "(~p) old_ret ~s -> ~p~n",
- [From,ffunc(MFA),Ret]);
- MFA ->
- io:format(Out, "(~p) old_ret ~s~n", [From,ffunc(MFA)])
- end;
- return_from ->
- MFA = element(4, Trace),
- Ret = element(5, Trace),
- io:format(Out, "(~p) returned from ~s -> ~p~n",
- [From,ffunc(MFA),Ret]);
- return_to ->
- MFA = element(4, Trace),
- io:format(Out, "(~p) returning to ~s~n", [From,ffunc(MFA)]);
- spawn when Size == 5 ->
- Pid = element(4, Trace),
- MFA = element(5, Trace),
- io:format(Out, "(~p) spawn ~p as ~s~n", [From,Pid,ffunc(MFA)]);
- Op ->
- io:format(Out, "(~p) ~p ~s~n", [From,Op,ftup(Trace,4,Size)])
- end,
- Out.
-
-
-%%% These f* functions returns non-flat strings
-
-%% {M,F,[A1, A2, ..., AN]} -> "M:F(A1, A2, ..., AN)"
-%% {M,F,A} -> "M:F/A"
-ffunc({M,F,Argl}) when is_list(Argl) ->
- io_lib:format("~p:~p(~s)", [M, F, fargs(Argl)]);
-ffunc({M,F,Arity}) ->
- io_lib:format("~p:~p/~p", [M,F,Arity]);
-ffunc(X) -> io_lib:format("~p", [X]).
-
-%% Integer -> "Integer"
-%% [A1, A2, ..., AN] -> "A1, A2, ..., AN"
-fargs(Arity) when is_integer(Arity) -> integer_to_list(Arity);
-fargs([]) -> [];
-fargs([A]) -> io_lib:format("~p", [A]); %% last arg
-fargs([A|Args]) -> [io_lib:format("~p,", [A]) | fargs(Args)];
-fargs(A) -> io_lib:format("~p", [A]). % last or only arg
-
-%% {A_1, A_2, ..., A_N} -> "A_Index A_Index+1 ... A_Size"
-ftup(Trace, Index, Index) ->
- io_lib:format("~p", [element(Index, Trace)]);
-ftup(Trace, Index, Size) ->
- [io_lib:format("~p ", [element(Index, Trace)])
- | ftup(Trace, Index+1, Size)].
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Functions handling the trace-port. Copied from dbg.erl
-%% ==============================================================================
-
-trace_port_control(Port, flush) ->
- case trace_port_control(Port, $f, "") of
- {ok, [0]} -> ok;
- {ok, _} -> {error, not_supported_by_trace_driver};
- Other -> Other
- end.
-
-trace_port_control(Port, Command, Arg) when is_port(Port)->
- case catch port_control(Port, Command, Arg) of
- {'EXIT', _} -> {error, {no_trace_driver, node()}};
- Result -> Result
- end.
-
-
-trace_port(file, {Filename, wrap, Tail}) ->
- trace_port(file, {Filename, wrap, Tail, 128*1024});
-trace_port(file, {Filename, wrap, Tail, WrapSize}) ->
- trace_port(file, {Filename, wrap, Tail, WrapSize, 8});
-trace_port(file, {Filename, wrap, Tail, WrapSize, WrapCnt})
- when is_list(Tail),
- is_integer(WrapSize), WrapSize >= 0, WrapSize < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- trace_port1(file, Filename, {wrap, Tail, WrapSize, WrapCnt, 0});
-trace_port(file, {Filename, wrap, Tail, {time, WrapTime}, WrapCnt})
- when is_list(Tail),
- is_integer(WrapTime), WrapTime >= 1, WrapTime < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- trace_port1(file, Filename, {wrap, Tail, 0, WrapCnt, WrapTime});
-trace_port(file, Filename) when is_list(Filename) ->
- trace_port1(file, Filename, nowrap);
-
-trace_port(ip, Portno) when is_integer(Portno) ->
- trace_port(ip,{Portno,50});
-
-trace_port(ip, {Portno, Qsiz}) when is_integer(Portno), is_integer(Qsiz) ->
- fun() ->
- Driver = "trace_ip_drv",
- Dir1 = filename:join(code:priv_dir(runtime_tools), "lib"),
- case catch erl_ddll:load_driver(Dir1, Driver) of
- ok ->
- ok;
- _ ->
- Dir2 = filename:join(
- Dir1,
- erlang:system_info(system_architecture)),
- catch erl_ddll:load_driver(Dir2, Driver)
- end,
- L = lists:flatten(
- io_lib:format("~s ~p ~p 2",
- [Driver, Portno, Qsiz])),
- open_port({spawn, L}, [eof])
- end.
-
-trace_port1(file, Filename, Options) ->
- Driver = "trace_file_drv",
- 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.
- %% 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)
- {Wrap, Tail} =
- case Options of
- {wrap, T, WrapSize, WrapCnt, WrapTime} ->
- {lists:flatten(
- io_lib:format("w ~p ~p ~p ~p ",
- [WrapSize, WrapCnt, WrapTime,
- length(Name)])),
- T};
- nowrap ->
- {"", ""}
- end,
- Command = Driver ++ " " ++ Wrap ++ "n " ++ Name ++ Tail,
- Dir1 = filename:join(code:priv_dir(runtime_tools), "lib"),
- case catch erl_ddll:load_driver(Dir1, Driver) of
- ok ->
- ok;
- _ ->
- Dir2 = filename:join(
- Dir1,
- erlang:system_info(system_architecture)),
- catch erl_ddll:load_driver(Dir2, Driver)
- end,
- if element(1, Options) == wrap ->
- %% Delete all files from any previous wrap log
- Files = wrap_postsort(wrap_presort(Name, Tail)),
- lists:foreach(
- fun(N) -> file:delete(N) end,
- Files);
- true -> ok
- end,
- open_port({spawn, Command}, [eof])
- end.
-
-%% Find all possible wrap log files.
-%% Returns: a list of sort converted filenames.
-%%
-%% The sort conversion is done by extracting the wrap sequence counter
-%% from the filename, and calling wrap_encode/2.
-wrap_presort(Filename, Tail) ->
- Name = filename:basename(Filename),
- Dirname = filename:dirname(Filename),
- case file:list_dir(Dirname) of
- {ok, Files} ->
- lists:zf(
- fun(N) ->
- case match_front(N, Name) of
- false ->
- false;
- X ->
- case match_rear(X, Tail) of
- false ->
- false;
- C -> % Counter
- case match_0_9(C) of
- true ->
- {true,
-% filename:join(Dirname, N)}
- wrap_encode(
- filename:join(Dirname, N),
- C)};
- false ->
- false
- end
- end
- end
- end,
- Files);
- _ ->
- []
- end.
-
-%% Extract the filenames from a list of sort converted ones.
-wrap_postsort(Files) ->
- lists:map(fun wrap_name/1, Files).
-
-wrap_encode(N, C) ->
- {list_to_integer(C), N}.
-
-wrap_name({_C, N}) ->
- N.
-
-%% Returns what is left of ListA when removing all matching
-%% elements from ListB, or false if some element did not match,
-%% or if ListA runs out of elements before ListB.
-match_front(ListA, []) when is_list(ListA) ->
- ListA;
-match_front([], ListB) when is_list(ListB) ->
- false;
-match_front([Hd|TlA], [Hd|TlB]) ->
- match_front(TlA,TlB);
-match_front([_HdA|_], [_HdB|_]) ->
- false.
-
-%% Reversed version of match_front/2
-match_rear(ListA, ListB) when is_list(ListA), is_list(ListB) ->
- case match_front(lists:reverse(ListA), lists:reverse(ListB)) of
- false ->
- false;
- List ->
- lists:reverse(List)
- end.
-
-%% Returns true if the non-empty list arguments contains all
-%% characters $0 .. $9.
-match_0_9([]) ->
- false;
-match_0_9([H]) when is_integer(H), $0 =< H, H =< $9 ->
- true;
-match_0_9([H|T]) when is_integer(H), $0 =< H, H =< $9 ->
- match_0_9(T);
-match_0_9(L) when is_list(L) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions working on the tracerdata structure.
-%% -----------------------------------------------------------------------------
-
-%% Tracerdata is the structure which specifies to where tracing is logged at this
-%% runtime component. It may now (and in the future specify) several things.
-%% Currently it can consist of:
-%% LogTD: specifying how trace-log data shall be handled.
-%% TiTD : trace information, specifying how trace information shall be handled.
-%%
-%% Tracerdata may also contain quick or standard forms of LogTD and/or TiTD.
-%% For instance if a standard handler-fun shall be used. The handler fun is not
-%% part of the tracerdata but rather specified by a constant.
-
-
-%% Help function that translates an input-tracerdata to useful internal formats.
-%% This since the tracerdata may consist of specifications which shall be
-%% translated into funs or similar.
-%% Returns {ok,LogTD,TiTD} or {error,Reason}.
-%% Note that TiTD may be 'void' since TiTD is not mandatory.
-translate_td(TracerData) when is_list(TracerData) -> % Both log and ti.
- case translate_td_logtd(get_trace_log_tracerdata(TracerData)) of
- {ok,LogTD} ->
- case translate_td_titd(get_ti_log_tracerdata(TracerData)) of
- {ok,TiTD} ->
- {ok,LogTD,TiTD};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
-translate_td(TracerData) -> % The it is just LogTD!?
- case translate_td_logtd(TracerData) of
- {ok,LogTD} ->
- {ok,LogTD,void};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function translating trace-log tracerdata.
-translate_td_logtd(collector) -> % This rt will act as receiver.
- {ok,{fun dhandler/2,user}}; % Simple terminal io.
-translate_td_logtd({relayer,Tracer}) when is_pid(Tracer) ->
- {ok,{fun relay_handler/2,Tracer}}; % Relay trace-msg to Tracer-pid.
-translate_td_logtd({HandlerFun,Data}) when is_function(HandlerFun) ->
- {ok,{HandlerFun,Data}}; % Own invented fun.
-translate_td_logtd({Type,Parameters}) when Type==ip;Type==file ->
- {ok,{Type,Parameters}}; % Built in trace-port
-translate_td_logtd(false) -> % Unusual but no trace log.
- {ok,void};
-translate_td_logtd(Arg) ->
- {error,{bad_log_td,Arg}}.
-%% -----------------------------------------------------------------------------
-
-%% Help function translating ti-log tracerdata.
-translate_td_titd(TiTD={file,FileName}) when is_list(FileName) ->
- {ok,TiTD};
-translate_td_titd({file,FileName,
- {InitPublLDmfa={M1,F1,L1},
- RemovePublLDmf={M2,F2},
- CleanPublLDmf={M3,F3}}})
- when is_list(FileName),is_atom(M1),is_atom(F1),is_atom(M2),is_atom(F2),is_list(L1),is_atom(M3),is_atom(F3) ->
- {ok,{file,FileName,{InitPublLDmfa,RemovePublLDmf,CleanPublLDmf}}};
-translate_td_titd({file,FileName,
- {InitPublLDmfa={M1,F1,L1},
- void,
- CleanPublLDmf={M3,F3}}})
- when is_list(FileName),is_atom(M1),is_atom(F1),is_list(L1),is_atom(M3),is_atom(F3) ->
- {ok,{file,FileName,{InitPublLDmfa,void,CleanPublLDmf}}};
-translate_td_titd(false) -> % Means no ti-tracerdata.
- {ok,void};
-translate_td_titd(TiTD) ->
- {error,{bad_ti_td,TiTD}}.
-%% -----------------------------------------------------------------------------
-
-%% This function retrieves the trace-log part of a TracerData list structure.
-%% Returns TraceLogTD or 'false'.
-get_trace_log_tracerdata(TracerData) ->
- case lists:keysearch(trace,1,TracerData) of
- {value,{_,LogTD}} ->
- LogTD;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% This function retrieves the ti-log part of a TracerData list structure.
-%% Returns TiLogTD or 'false'.
-get_ti_log_tracerdata(TracerData) ->
- case lists:keysearch(ti,1,TracerData) of
- {value,{_,TiTD}} ->
- TiTD;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which checks that parameters to the built in trace-port are
-%% sane.
-check_traceport_parameters(Type,Args) ->
- case {Type,Args} of
- {file,{FileName,wrap,Tail}} when is_list(FileName),is_list(Tail) ->
- ok;
- {file,{FileName,wrap,Tail,WrapSize}}
- when is_list(FileName),
- is_list(Tail),
- is_integer(WrapSize),WrapSize>=0,WrapSize< (1 bsl 32) ->
- ok;
- {file,{FileName,wrap,Tail,WrapSize,WrapCnt}}
- when is_list(FileName),is_list(Tail),
- is_integer(WrapSize), WrapSize >= 0, WrapSize < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- ok;
- {file,{FileName,wrap,Tail,{time,WrapTime},WrapCnt}}
- when is_list(FileName),is_list(Tail),
- is_integer(WrapTime), WrapTime >= 1, WrapTime < (1 bsl 32),
- is_integer(WrapCnt), WrapCnt >= 1, WrapCnt < (1 bsl 32) ->
- ok;
- {file,FileName} when is_list(FileName) ->
- ok;
- {ip,Portno} when is_integer(Portno),Portno=<16#FFFF ->
- ok;
- {ip,{Portno,Qsiz}} when is_integer(Portno),Portno=<16#FFFF,is_integer(Qsiz) ->
- ok;
- _ ->
- {error,{trace_port_args,[Type,Args]}}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Default overload functionality.
-%% -----------------------------------------------------------------------------
-
-%% A default overload protection function. An overload function must take
-%% one argument and return 'ok' or {suspend,SuspendReason}.
-default_overload_func(_) ->
- case process_info(self(),message_queue_len) of
- {message_queue_len,N} when N > 1000 ->
- {suspend,rt_max_queue_len};
- _ ->
- ok
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Functions working on the internal loopdata structure.
-%% =============================================================================
-
-%% Help function simply adding Fetcher as a fetcher process to the loopdata.
-%% Returns a new loopdata structure.
-add_fetcher_ld(Fetcher,LD) ->
- LD#rt{fetchers=[Fetcher|LD#rt.fetchers]}.
-%% -----------------------------------------------------------------------------
-
-%% Help function investigating if the first argument is a known fetcher process
-%% or not. If it is, it also removed it from the fetchers list in the loopdata
-%% structure.
-%% Returns {true,NewLoopData} or 'false'.
-remove_fetcher_ld(Fetcher,LD) ->
- NewFetchers=lists:delete(Fetcher,LD#rt.fetchers),
- if
- NewFetchers/=LD#rt.fetchers ->
- {true,LD#rt{fetchers=NewFetchers}};
- true -> % No it was not a fetcher process.
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%%% end of file
-
diff --git a/lib/runtime_tools/src/inviso_rt_lib.erl b/lib/runtime_tools/src/inviso_rt_lib.erl
deleted file mode 100644
index 5dfe14068a..0000000000
--- a/lib/runtime_tools/src/inviso_rt_lib.erl
+++ /dev/null
@@ -1,474 +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%
-%%
-%% ------------------------------------------------------------------------------
-%% File : inviso_rt_lib.erl
-%% Author : Lennart �hman <[email protected]>
-%% Description :
-%%
-%% Created : 27 Sep 2005 by Lennart �hman <[email protected]>
-%% ------------------------------------------------------------------------------
--module(inviso_rt_lib).
-
--export([expand_regexp/2,expand_regexp/3,expand_regexp/4]).
--export([is_tracerdata/1]).
--export([transform/2]).
-
--export([rpc/4,rpc/5,match_modules/2,match_modules/3]).
--export([debug/3]).
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) = [{Node,Answer},...] | {error,Reason}
-%% expand_regexp(Nodes,RegExpMod,Opts) = [{Node,Answer},...] | {error,Reason}
-%% expand_regexp(RegExpDir,RegExpMod,Opts) = ListOfModules | {error,Reason}
-%% expand_regexp(RegExpMod,Opts) = ListOfModules | {error,Reason}
-%% Nodes=List of all nodes (atoms) where to expand.
-%% RegExpDir=Reg.exp (string) specifying directories.
-%% RegExpMod=Reg.exp (string) specifying module names.
-%% Node=node name (atom).
-%% Opts=[Opt,...]
-%% Opt=only_loaded
-%% Answer=List of modules (atoms) | 'badrpc'
-%%
-%% Expands, concurrently, the regular expression on Nodes and returns a list
-%% of what modules it expanded to on the different nodes. Note that it may
-%% differ between Erlang nodes depending on whether the modules are the same
-%% or not. Also note that all modules becomes loaded as a result.
-%% RegExpDir can further limit the modules. It introduces the requirement that
-%% a module must be loaded from a directory with a path satisfying the RegExpDir.
-%% All regular expression are according to the standard lib regexp module.
-expand_regexp(RegExpMod,Opts) when is_list(RegExpMod),is_list(Opts) ->
- match_modules(RegExpMod,Opts);
-expand_regexp(RegExpMod,Opts) ->
- {error,{badarg,[RegExpMod,Opts]}}.
-expand_regexp(NodesOrRegExpDir,RegExpMod,Opts)
- when is_list(NodesOrRegExpDir),is_list(RegExpMod),is_list(Opts) ->
- case is_list_of_atoms(NodesOrRegExpDir) of
- true -> % Interpret as list of nodes.
- lists:foreach(fun(N)->spawn(?MODULE,rpc,[self(),N,RegExpMod,Opts]) end,
- NodesOrRegExpDir),
- expand_regexp_answers(NodesOrRegExpDir,[]);
- false -> % Interpret as a string.
- match_modules(NodesOrRegExpDir,RegExpMod,Opts)
- end;
-expand_regexp(NodesOrRegExpDir,RegExpMod,Opts) ->
- {error,{badarg,[NodesOrRegExpDir,RegExpMod,Opts]}}.
-expand_regexp(Nodes,RegExpDir,RegExpMod,Opts)
- when is_list(Nodes),is_list(RegExpDir),is_list(RegExpMod),is_list(Opts) ->
- lists:foreach(fun(N)->
- spawn(?MODULE,rpc,[self(),N,RegExpDir,RegExpMod,Opts])
- end,
- Nodes),
- expand_regexp_answers(Nodes,[]);
-expand_regexp(Nodes,RegExpDir,RegExpMod,Opts) ->
- {error,{badarg,[Nodes,RegExpDir,RegExpMod,Opts]}}.
-
-expand_regexp_answers([],Answers) -> Answers; % List of [{Node,Answer},...].
-expand_regexp_answers(Nodes,Answers) ->
- receive
- {?MODULE,Node,Answer} ->
- expand_regexp_answers(lists:delete(Node,Nodes),[{Node,Answer}|Answers])
- end.
-%% ------------------------------------------------------------------------------
-
-%% is_tracerdata(TracerData)=true|false
-%% Answers the question if TracerData is proper tracerdata. Note that true can be
-%% returned if it resembles tracerdata very closely.
-is_tracerdata({Fun,_Data}) when is_function(Fun) -> true;
-is_tracerdata({relayer,To}) when is_pid(To);is_atom(To) -> true;
-is_tracerdata(collector) -> true;
-is_tracerdata({file,Param}) when is_tuple(Param);is_list(Param) -> true;
-is_tracerdata({ip,_Param}) -> true;
-is_tracerdata([{trace,LogTD}|Rest]) ->
- case is_tracerdata(LogTD) of
- true ->
- is_tracerdata(Rest);
- false ->
- false
- end;
-is_tracerdata([{ti,TiData}|Rest]) ->
- case is_tidata(TiData) of
- true ->
- is_tracerdata(Rest);
- false ->
- false
- end;
-is_tracerdata([]) ->
- true;
-is_tracerdata(_) ->
- false.
-
-is_tidata({file,FileName}) when is_list(FileName) -> true;
-is_tidata({file,FileName,{M,F,Args}}) when is_list(FileName),is_atom(M),is_atom(F),is_list(Args) ->
- true;
-is_tidata(_) -> false.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Help functions.
-%% ==============================================================================
-
-%% Help function intended to be run in its own process. Will report with
-%% a message when done.
-%% This function will be spawned on.
-rpc(Parent,Node,RegExpMod,Opts) ->
- case rpc:call(Node,?MODULE,match_modules,[RegExpMod,Opts]) of
- {badrpc,_Reason} -> % The node is probably not healthy.
- Parent ! {?MODULE,Node,badrpc};
- Modules ->
- Parent ! {?MODULE,Node,Modules}
- end.
-
-rpc(Parent,Node,RegExpDir,RegExpMod,Opts) ->
- case rpc:call(Node,?MODULE,match_modules,[RegExpDir,RegExpMod,Opts]) of
- {badrpc,_Reason} -> % The node is probably not healthy.
- Parent ! {?MODULE,Node,badrpc};
- Modules ->
- Parent ! {?MODULE,Node,Modules}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported function which actually shall be in code.erl.
-%% ==============================================================================
-
-%% match_modules(RegExpMod,Actions) = [Module,...] | {error,Reason}
-%% match_modules(RegExpDir,RegExpMod,Actions)=[Module,...] | {error,Reason}
-%% RegExpMod=Erlang regular expression describing module names (string).
-%% RegExpDir=Erlang regular expression describing directory paths(string) |
-%% void
-%% Actions=List of;'only_loaded'.
-%%
-%% Function which matches a regular expresion against module names. The function
-%% can also match the directory from where the module is loaded or will be loaded
-%% against a regular expresion for directory paths.
-%% The function uses the same strategy as code-loading if the same module is
-%% discovered in several places.
-%% (1) An already loaded module shadows all other occurancies.
-%% (2) .beams found in by a path shadows .beams found by paths later in the
-%% code paths.
-%%
-%% Description of actions:
-%% only_loaded: Only consider modules which are loaded.
-match_modules(RegExpMod,Actions) ->
- match_modules(void,RegExpMod,Actions).
-match_modules(RegExpDir,RegExpMod,Actions) ->
- AllLoaded=code:all_loaded(),
- Mods1=handle_expand_regexp_2(AllLoaded,RegExpDir,RegExpMod,[]),
- case lists:member(only_loaded,Actions) of % Shall we do not loaded too?
- false -> % Ok, search all paths too then.
- Paths=code:get_path(),
- handle_expand_regexp_3(Paths,RegExpDir,RegExpMod,AllLoaded,Mods1);
- true -> % Only loaded modules then.
- Mods1
- end.
-
-
-%% Help function which traverses all loaded modules and determines
-%% which shall be returned. First we check that the module satisfies the
-%% module-regexp. Then we, if a dir reg-exp is given, checks that the
-%% module is loaded from an approved path. Note that if it can not be
-%% determined from where it was loaded (like preloaded or cover-compiled
-%% etc), but dir reg-exps are used. That module will be excluded.
-%% Returns a list of modules.
-handle_expand_regexp_2([{Mod,Path}|Rest],RegExpDir,RegExpMod,Result) ->
- ModStr=atom_to_list(Mod),
- ModLen=length(ModStr),
- case re:run(ModStr,RegExpMod) of
- {match,[{0,ModLen}]} -> % Ok, The regexp matches the module.
- if
- is_list(RegExpDir),is_atom(Path) -> % Preloaded or covercompiled...
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result);
- is_list(RegExpDir),is_list(Path) -> % Dir reg-exp is used!
- PathOnly=filename:dirname(Path), % Must remove beam-file name.
- case re:run(PathOnly,RegExpDir,[{capture,none}]) of
- match -> % Did find a match, that is enough!
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result]);
- _ -> % Either error or nomatch.
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result)
- end;
- true -> % Otherwise already done!
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,[Mod|Result])
- end;
- _ -> % Then Mod is not part of the set.
- handle_expand_regexp_2(Rest,RegExpDir,RegExpMod,Result)
- end;
-handle_expand_regexp_2([],_,_,Result) -> Result.
-
-%% Help function which traverses all paths and looks for modules satisfying
-%% the module reg.exp.
-%% Returns a list of unique module names.
-handle_expand_regexp_3([Path|Rest],RegExpDir,RegExpMod,AllLoaded,Result) ->
- if
- is_list(RegExpDir) -> % We must consider the directory name.
- AbsPath=
- case filename:pathtype(Path) of
- absolute -> % Is already abs.
- Path;
- relative -> % Then it must be made absolute.
- filename:absname(Path);
- volumerelative -> % Only on Windows!?
- filename:absname(Path)
- end,
- case re:run(AbsPath,RegExpDir,[{capture,none}]) of
- match -> % Ok, the directory is allowed.
- NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result),
- handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult);
- _ -> % This directory does not qualify.
- handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,Result)
- end;
- true -> % RegExpDir is not used!
- NewResult=handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result),
- handle_expand_regexp_3(Rest,RegExpDir,RegExpMod,AllLoaded,NewResult)
- end;
-handle_expand_regexp_3([],_,_,_,Result) -> Result.
-
-handle_expand_regexp_3_1(Path,RegExpMod,AllLoaded,Result) ->
- case file:list_dir(Path) of
- {ok,FileNames} ->
- handle_expand_regexp_3_2(FileNames,RegExpMod,AllLoaded,Result);
- {error,_Reason} -> % Bad path!? Skip it.
- Result
- end.
-
-handle_expand_regexp_3_2([File|Rest],RegExpMod,AllLoaded,Result) ->
- case filename:extension(File) of
- ".beam" -> % It is a beam-file. Consider it!
- ModStr=filename:basename(File,".beam"),
- Mod=list_to_atom(ModStr),
- case {lists:keysearch(Mod,1,AllLoaded),lists:member(Mod,Result)} of
- {false,false} -> % This module is not tried before.
- ModLen=length(ModStr),
- case re:run(ModStr,RegExpMod) of
- {match,[{0,ModLen}]} -> % This module satisfies the regexp.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,[Mod|Result]);
- _ -> % Error or not perfect match.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
- end;
- {_,_} -> % This module is already tested.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
- end;
- _ -> % Not a beam-file, skip it.
- handle_expand_regexp_3_2(Rest,RegExpMod,AllLoaded,Result)
- end;
-handle_expand_regexp_3_2([],_,_,Result) -> Result.
-%% ------------------------------------------------------------------------------
-
-%% Help function which finds out if its argument is a list of zero or more
-%% atoms.
-%% Returns 'true' or 'false'.
-is_list_of_atoms([A|Rest]) when is_atom(A) ->
- is_list_of_atoms(Rest);
-is_list_of_atoms([_|_]) ->
- false;
-is_list_of_atoms([]) ->
- true.
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions transforming function calls in trace-case file.
-%% =============================================================================
-
-%% transform(Exprs,Translations)=NewExprs
-%% Exprs=list(); List of abstract format erlang terms, as returned by
-%% io:parse_erl_exprs/2.
-%% Translations=list(); List of translations from function calls to other
-%% function calls. [{Mod,Func,Arity,{NewMod,NewFunc,ParamTransformMF}},...]
-%% Mod can actually be omitted, ParamTransformMF shall be {M,F} where F is
-%% a function taking one argument (the parameter list), and returning the
-%% new parameter list. It can also be anything else should no transformation
-%% of the parameters be the case.
-%%
-%% Function that transforms function calls in a trace-case file. The transform/2
-%% can only transform shallow function calls. I.e where both module and function
-%% name are specified as atoms. Any binding-environment is not examined.
-transform([Expr|Rest],Translations) ->
- [transform_2(Expr,Translations)|transform(Rest,Translations)];
-transform([],_) ->
- [].
-
-%% Help function handling a single expr.
-transform_2({call,L1,{remote,L2,ModExpr,FuncExpr},Params},Translations) ->
- case transform_2(ModExpr,Translations) of
- {atom,L3,M} ->
- case transform_2(FuncExpr,Translations) of
- {atom,L4,F} -> % Now we have a M:F/Arity!
- case do_call_translation(M,F,Params,Translations) of
- {ok,NewM,NewF,NewP} ->
- NewParams=transform(NewP,Translations),
- {call,L1,{remote,L2,{atom,L3,NewM},{atom,L4,NewF}},NewParams};
- false -> % No translation or faulty.
- NewParams=transform(Params,Translations),
- {call,L1,{remote,L2,ModExpr,FuncExpr},NewParams}
- end;
- NewFuncExpr -> % Not translated to a shallow term.
- NewParams=transform(Params,Translations),
- {call,L1,{remote,L2,ModExpr,NewFuncExpr},NewParams}
- end;
- NewModExpr -> % Not translated to a shallow term.
- NewFuncExpr=transform_2(FuncExpr,Translations),
- NewParams=transform(Params,Translations),
- {call,L1,{remote,L2,NewModExpr,NewFuncExpr},NewParams}
- end;
-transform_2({call,L1,FuncExpr,Params},Translations) ->
- case transform_2(FuncExpr,Translations) of
- {atom,L3,F} -> % Now we have a M:F/Arity!
- case do_call_translation(F,Params,Translations) of
- {ok,NewM,NewF,NewP} -> % It is turned into a global call.
- NewParams=transform(NewP,Translations),
- {call,L1,{remote,L1,{atom,L3,NewM},{atom,L3,NewF}},NewParams};
- false -> % No translation or faulty.
- NewParams=transform(Params,Translations),
- {call,L1,FuncExpr,NewParams}
- end;
- NewFuncExpr -> % Not translated to a shallow term.
- NewParams=transform(Params,Translations),
- {call,L1,NewFuncExpr,NewParams}
- end;
-transform_2({match,L,P,E},Translations) ->
- NewPattern=transform_2(P,Translations),
- NewExpr=transform_2(E,Translations),
- {match,L,NewPattern,NewExpr};
-transform_2({op,L,Op,Arg1,Arg2},Translations) ->
- NewArg1=transform_2(Arg1,Translations),
- NewArg2=transform_2(Arg2,Translations),
- {op,L,Op,NewArg1,NewArg2};
-transform_2({op,L,Op,Arg},Translations) ->
- NewArg=transform_2(Arg,Translations),
- {op,L,Op,NewArg};
-transform_2({block,L,Body},Translations) ->
- NewBody=transform(Body,Translations),
- {block,L,NewBody};
-transform_2({'if',L,Clauses},Translations) ->
- NewClauses=transform_clauses(Clauses,Translations),
- {'if',L,NewClauses};
-transform_2({'case',L,Func,Clauses},Translations) ->
- NewFunc=transform_2(Func,Translations),
- NewClauses=transform_clauses(Clauses,Translations),
- {'case',L,NewFunc,NewClauses};
-transform_2({'fun',L,{clauses,Clauses}},Translations) ->
- NewClauses=transform_clauses(Clauses,Translations),
- {'fun',L,NewClauses};
-transform_2({lc,L,Items,GeneratorsFilters},Translations) ->
- NewItem=transform_2(Items,Translations),
- NewGensAndFilters=transform_gensandfilters(GeneratorsFilters,Translations),
- {lc,L,NewItem,NewGensAndFilters};
-transform_2({'catch',L,Expr},Translations) ->
- NewExpr=transform_2(Expr,Translations),
- {'catch',L,NewExpr};
-transform_2({tuple,L,Elements},Translations) ->
- NewElements=transform(Elements,Translations),
- {tuple,L,NewElements};
-transform_2({cons,L,Element,Tail},Translations) ->
- NewElement=transform_2(Element,Translations),
- NewTail=transform_2(Tail,Translations),
- {cons,L,NewElement,NewTail};
-transform_2({nil,L},_) ->
- {nil,L};
-transform_2({bin,L,Elements},Translations) ->
- NewElements=transform_binary(Elements,Translations),
- {bin,L,NewElements};
-transform_2(Expr,_) -> % Can be a var for instance.
- Expr.
-
-transform_binary([{bin_element,L,Val,Size,TSL}|Rest],Translations) ->
- NewVal=transform_2(Val,Translations),
- NewSize=transform_2(Size,Translations),
- [{bin_element,L,NewVal,NewSize,TSL}|transform_binary(Rest,Translations)];
-transform_binary([],_) ->
- [].
-
-transform_clauses([{clause,L,Pattern,Guards,Body}|Rest],Translations) ->
- NewPattern=transform(Pattern,Translations),
- NewBody=transform(Body,Translations),
- [{clause,L,NewPattern,Guards,NewBody}|transform_clauses(Rest,Translations)];
-transform_clauses([],_Translations) ->
- [].
-
-transform_gensandfilters([{generator,L,Pattern,Exprs}|Rest],Translations) ->
- NewExprs=transform(Exprs,Translations),
- [{generator,L,Pattern,NewExprs}|transform_gensandfilters(Rest,Translations)];
-transform_gensandfilters([Expr|Rest],Translations) ->
- [transform_2(Expr,Translations)|transform_gensandfilters(Rest,Translations)];
-transform_gensandfilters([],_) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% This is the heart of the translation functionality. Here we actually try to
-%% replace calls to certain functions with other calls. This can include removing
-%% arguments.
-do_call_translation(M,F,Params,Translations) ->
- case lists:keysearch({M,F,length(Params)},1,Translations) of
- {value,{_,{NewM,NewF,ArgFun}}} -> % Lets transform the function.
- do_call_translation_2(Params,NewM,NewF,ArgFun);
- _ ->
- false % No translations at all.
- end.
-do_call_translation(F,Params,Translations) ->
- case lists:keysearch({F,length(Params)},1,Translations) of
- {value,{_,{NewM,NewF,ArgFun}}} -> % Lets transform the function.
- do_call_translation_2(Params,NewM,NewF,ArgFun);
- _ ->
- false % No translations at all.
- end.
-
-do_call_translation_2(Params,NewM,NewF,ArgFun) ->
- case ArgFun of
- {M,F} when is_atom(M),is_atom(F) ->
- case catch M:F(Params) of
- {'EXIT',_Reason} ->
- false; % If it does not work, skipp it.
- MungedParams when is_list(MungedParams) ->
- {ok,NewM,NewF,MungedParams};
- _ ->
- false
- end;
- _ -> % No munging of parameters.
- {ok,NewM,NewF,Params}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for the runtime component internal debugging system.
-%% =============================================================================
-
-%% The debug system is meant to provide tracing of ttb at different levels.
-%%
-%% debug(What,Level,Description) -> nothing significant.
-%% What : controls what kind of event. This can both be certain parts of ttb
-%% as well as certain levels (info to catastrophy).
-%% Level: Determines if What shall be printed or not.
-%% Description: this is what happend.
-debug(off,_What,_Description) ->
- true; % Debug is off, no action.
-debug(On,What,Description) ->
- debug_2(On,What,Description).
-
-debug_2(_,What,Description) ->
- io:format("INVISO DEBUG:~w, ~p~n",[What,Description]).
-%% -----------------------------------------------------------------------------
diff --git a/lib/runtime_tools/src/inviso_rt_meta.erl b/lib/runtime_tools/src/inviso_rt_meta.erl
deleted file mode 100644
index 6865dc2242..0000000000
--- a/lib/runtime_tools/src/inviso_rt_meta.erl
+++ /dev/null
@@ -1,1207 +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%
-%%
-%% Author: Lennart �hman, [email protected]
-%%
-%% This module implements the meta tracer process belonging to the
-%% runtime component. Its main purpose is to write the ti-file (traceinformation).
-%% The ti-file contains translations between process id:s and what ever "you"
-%% want to read in the merged and formatted logfile.
-%% This process interacts with the runtime component process.
-%%
-%% Currently it handles the following types of ti-files:
-%% Plain raw, binary log.
-%% Relay to other inviso_rt_meta process on another node.
-%%
-%% The TI file will be on binary format and each entry is:
-%% <<LengthIndicator:32, {Pid,Alias,Op,NowStamp} >>
-%% Pid=pid(), or if OP==unalias pid()|any_other_than_pid()
-%% Op=alias|unalias
-%% -----------------------------------------------------------------------------
--module(inviso_rt_meta).
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/2,start/5]).
--export([stop/1,suspend/1]).
--export([init_tpm/5,init_tpm/8]).
--export([tpm/5,tpm/6,tpm/9,tpm_tracer/5,tpm_tracer/6,tpm_tracer/9]).
--export([tpm_ms/6,tpm_ms_tracer/6,ctpm_ms/5,ctpm/4]).
--export([local_register/1,global_register/1]).
--export([remove_local_register/1,remove_global_register/1]).
-
--export([write_ti/1]).
-
--export([get_tracer/0,tpm_ms/5,tpm_ms_tracer/5,list_tpm_ms/3,ctpm_ms/4]).
-
--export([metacast_call/5,metacast_return_from/6]).
--export([get_state/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([init/6]).
--export([init_std_publld/2,clean_std_publld/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
--define(NAMED_MS_TAB,inviso_rt_meta_named_ms).
-
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Exported API (Meant to be used by a runtime component).
-%% =============================================================================
-
-%% start(TiData,Tracer)={ok,Pid} | {error,Reason}
-%% start(TiData,Tracer,InitPublLDmfa,RemovePublLDmfa,CleanPublLDmf)=
-%% {ok,Pid} | {error,Reason}
-%% TiData={file,FileName}|{relay,Node}
-%% Tracer=pid()|port()
-%% FileName=string()
-%% InitPublLDmfa={Mod,Func,ArgList}
-%% RemovePublLDmf={Mod,Func} | void
-%% RemovePublLDmf(PublLD)->nothing significant.
-%% These functions are called to create and destroy the public loopdata
-%% structure available to the meta-trace CallFunc and ReturnFunc.
-%% CleanPublLDmf={Mod,Func}
-%% This function will periodically be called to clean the public LD from
-%% pending meta-trace messages waiting for a corresponding return_from
-%% message.
-%%
-%% Starts a meta-tracer process, opening the ti-file specified in TiData. PublLD
-%% is used to communicate data, typically between a call and return_from.
-%% If no special initialization function is specified a standard one is used.
-%% Note that the meta tracer function must know "who" is the regular tracer
-%% (process or port). This because it must be possible to append {tracer,Tracer}
-%% in meta match specs.
-start(TiData,Tracer) ->
- Pid=spawn_link(?MODULE,
- init,
- [self(),
- TiData,
- Tracer,
- {?MODULE,init_std_publld,[2,[]]},
- void,
- {?MODULE,clean_std_publld}]),
- wait_for_reply(Pid).
-start(TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) ->
- Pid=spawn_link(?MODULE,
- init,
- [self(),TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf]),
- wait_for_reply(Pid).
-
-wait_for_reply(Pid) ->
- receive
- {Pid,ok} ->
- {ok,Pid};
- {Pid,{error,Reason}} ->
- {error,Reason}
- after
- 10000 -> % After very long time.
- exit(Pid,kill), % It must be hanging.
- {error,time_out}
- end.
-%% -----------------------------------------------------------------------------
-
-%% stop(Pid)=ok
-%% Pid=Adders to the meta tracer, pid().
-%% Shutsdown the metatracer.
-stop(Pid) ->
- Pid ! {stop,self()},
- ok.
-%% -----------------------------------------------------------------------------
-
-%% suspend(Pid)=ok
-%% Pid=Adders to the meta tracer, pid().
-%% Suspends the meta tracer by removing all meta trace patterns.
-suspend(Pid) ->
- Pid ! {suspend,self()},
- ok.
-%% -----------------------------------------------------------------------------
-
-%% init_tpm(Pid,Mod,Func,Arity,CallFunc)=
-%% init_tpm(Pid,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=ok|{error,Reason}.
-%% Pid=Address to meta tracer process, pid().
-%% Mod,Func=Pointing out the function which shall be meta traced, atom().
-%% Arity=As above, integer().
-%% InitFunc,RemoveFunc={Module,Function}|fun(), functions being called when
-%% to initialize the public loopdata structure, and to reset it.
-%% InitFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD,Output}
-%% Supposed to initialize whatever needs to be done before
-%% handling any incoming meta-trace message for the Mod:Func/Arity.
-%% RemoveFunc(Mod,Func,Arity,PublLD)->{ok,NewPublLD}
-%% Called when meta tracing of Mod:Func/Arity is stopped. It is supposed
-%% to clear datastructures away from the PublLD.
-%% Initializes the public loopdata for this function. Note that we can not use wildcards
-%% here (even if it is perfectly legal in Erlang). It also sets the CallFunc and
-%% ReturnFunc for the meta traced function. The function is hence ready to be
-%% meta traced with either tpm/5 or tpm_ms/5.
-%% This function is synchronous, waiting for a reply from the meta server.
-init_tpm(Pid,Mod,Func,Arity,CallFunc) ->
- init_tpm(Pid,Mod,Func,Arity,void,CallFunc,void,void).
-init_tpm(Pid,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- send_wait(Pid,
- {init_tpm,{Mod,Func,Arity},InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-%% -----------------------------------------------------------------------------
-
-%% tpm(Pid,Mod,Func,Arity,MatchSpec)={ok,N}|{error,Reason}
-%% tpm(Pid,Mod,Func,Arity,MatchSpec,CallFunc)={ok,N}|{error,Reason}
-%% tpm(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)=
-%% Pid=Address to meta tracer process, pid().
-%% Mod,Func=Pointing out the function which shall be meta traced, atom().
-%% Arity=As above, integer().
-%% MatchSpec=List of match specification, possibly empty. Remember {return_trace}
-%% if expecting return_from messages.
-%% InitFunc,CallFunc,ReturnFunc,RemoveFunc={Module,Function}|fun(),
-%% functions being called when these functions are called by the meta trace
-%% server at certain events.
-%% CallFunc(CallingPid,ActualArgList,PublLD)->{ok,NewPrivLD,Output}
-%% ReturnFunc(CallingPid,ReturnValue,PublLD)->{ok,NewPrivLD,Output}
-%% When a call respectively return_from trace message arrives for the meta
-%% traced function, the corresponding function is called.
-%% The ReturnFunc must handle the fact that a return_from message arrives
-%% for a call which was never noticed. This because the message queue of the
-%% meta tracer may have been emptied.
-%% Reason=badarg |
-%% Output=Characters to be written to the ti-file, bin() | 'void'
-%% The tpm/5 function simply starts meta tracing for the function. It must
-%% previously have been initialized.
-%% tpm/6 & /9 initializes the function and starts meta tracing.
-tpm(Pid,Mod,Func,Arity,MatchSpec)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_'->
- send_wait(Pid,{tpm,{Mod,Func,Arity,MatchSpec}});
-tpm(_,_,_,_,_) ->
- {error,badarg}.
-
-tpm(Pid,Mod,Func,Arity,MatchSpec,CallFunc) ->
- tpm(Pid,Mod,Func,Arity,MatchSpec,void,CallFunc,void,void).
-
-tpm(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_' ->
- send_wait(Pid,{tpm,{Mod,Func,Arity,MatchSpec},InitFunc,CallFunc,ReturnFunc,RemoveFunc});
-tpm(_,_,_,_,_,_,_,_,_) ->
- {error,badarg}.
-%% -----------------------------------------------------------------------------
-
-%% Same as tpm/X but the meta tracer will automatically append {tracer,Tracer}
-%% to the enable list in a {trace,Disable,Enable} match spec action term.
-tpm_tracer(Pid,Mod,Func,Arity,MatchSpec)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_'->
- send_wait(Pid,{tpm_tracer,{Mod,Func,Arity,MatchSpec}});
-tpm_tracer(_,_,_,_,_) ->
- {error,badarg}.
-
-tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,CallFunc) ->
- tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,void,CallFunc,void,void).
-
-tpm_tracer(Pid,Mod,Func,Arity,MatchSpec,InitFunc,CallFunc,ReturnFunc,RemoveFunc)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),is_list(MatchSpec),Mod/='_',Func/='_' ->
- send_wait(Pid,{tpm_tracer,
- {Mod,Func,Arity,MatchSpec},
- InitFunc,CallFunc,ReturnFunc,RemoveFunc});
-tpm_tracer(_,_,_,_,_,_,_,_,_) ->
- {error,badarg}.
-%% -----------------------------------------------------------------------------
-
-%% tpm_ms(Pid,Mod,Func,Arity,MSname,MS)={ok,N}|{error,Reason}
-%% Pid=Address to meta tracer process, pid().
-%% Mod,Func=Pointing out the function to which we shall add a match-spec., atom().
-%% Arity=As above, integer().
-%% MSname=A name to be used if this MS shall be removed later. term().
-%% MatchSpec=List of match specification, Remember {return_trace}
-%% if expecting return_from messages.
-%% This function adds a list of match-specs to the already existing ones. It
-%% uses an internal database to keep track of existing match-specs. If the
-%% match-spec does not result in any meta traced functions (for whatever reason),
-%% the MS is not saved in the database. The previously known match-specs are
-%% not removed.
-tpm_ms(Pid,Mod,Func,Arity,MSname,MS) ->
- send_wait(Pid,{tpm_ms,{Mod,Func,Arity},MSname,MS}).
-%% -----------------------------------------------------------------------------
-
-%% Same as tpm_ms/6 but the meta tracer will automatically append {tracer,Tracer}
-%% to the enable list in a {trace,Disable,Enable} match spec action term.
-tpm_ms_tracer(Pid,Mod,Func,Arity,MSname,MS) ->
- send_wait(Pid,{tpm_ms_tracer,{Mod,Func,Arity},MSname,MS}).
-%% -----------------------------------------------------------------------------
-
-%% ctpm_ms(Pid,Mod,Func,Arity)=ok
-%%
-%% Removes a names match-spec from the meta traced function. Note that is never
-%% a fault to remove an MS. Not even from a function which is non existant.
-ctpm_ms(Pid,Mod,Func,Arity,MSname) ->
- send_wait(Pid,{ctpm_ms,{Mod,Func,Arity},MSname}).
-%% -----------------------------------------------------------------------------
-
-%% Quick versions for erlang:register/2 which also uses a default CallFunc
-%% and a default ReturnFunc.
-local_register(Pid) ->
- Res1=tpm(Pid,
- erlang,register,2,[{'_',[],[{exception_trace}]}],
- fun metafunc_init/4,fun local_register_call/3,
- fun local_register_return/3,void),
- Res2=tpm(Pid,
- erlang,unregister,1,[],
- void,fun local_unregister_call/3,void,void),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% Quick version for global:register_name/2, /3.
-global_register(Pid) ->
- Res1=tpm(Pid,global,handle_call,3,[{[{register,'_','_','_'},'_','_'],[],[]}],
- void,fun global_register_call/3,void,void),
- Res2=tpm(Pid,global,delete_global_name,2,[],
- void,fun global_unregister_call/3,void,void),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% ctpm(Pid,Mod,Func,Arity)=ok|{error,bad_mfa}
-%%
-%% Removes the meta trace pattern for the function, means stops generating output
-%% for this function. The public LD may be cleared by the previously entered
-%% RemoveFunc.
-ctpm(Pid,Mod,Func,Arity) ->
- send_wait(Pid,{ctpm,{Mod,Func,Arity}}).
-%% -----------------------------------------------------------------------------
-
-%% remove_local_register(Pid)={Res1,Res2}
-%% Res1,Res2=ok|{error,Reason}
-remove_local_register(Pid) ->
- Res1=ctpm(Pid,erlang,register,2),
- Res2=ctpm(Pid,erlang,unregister,1),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% remove_global_register(Pid)={Res1,Res2}
-%% Res1,Res2=ok|{error,Reason}
-remove_global_register(Pid) ->
- Res1=ctpm(Pid,global,handle_call,3),
- Res2=ctpm(Pid,global,delete_global_name,2),
- {Res1,Res2}.
-%% -----------------------------------------------------------------------------
-
-%% Exported help functions which may be used in programming CallFunc and/or
-%% ReturnFunc. Useful if the call is done on one node but must trigger the
-%% start of something at other nodes.
-metacast_call(Nodes,OrigPid,M,F,Args) ->
- multicast(Nodes,{trace_ts,OrigPid,call,{M,F,Args},void}),
- ok.
-
-metacast_return_from(Nodes,OrigPid,M,F,Arity,Value) ->
- multicast(Nodes,{trace_ts,OrigPid,return_from,{M,F,Arity},Value,void}),
- ok.
-
-multicast([Node|Rest],Msg) ->
- {?MODULE,Node} ! Msg,
- multicast(Rest,Msg);
-multicast([],_) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% get_states(Pid)={ok,LD,PubLD}.
-get_state(Pid) ->
- send_wait(Pid,get_state).
-%% -----------------------------------------------------------------------------
-
-
-send_wait(To,Msg) ->
- Ref=make_ref(),
- MRef=erlang:monitor(process,To),
- To ! {Msg,Ref,self()},
- receive
- {inviso_rt_meta_reply,Ref,Reply} ->
- erlang:demonitor(MRef),
- Reply;
- {'DOWN',MRef,_,_To,_Reason} ->
- {error,no_metatracer}
- end.
-
-reply(To,Ref,Reply) ->
- To ! {inviso_rt_meta_reply,Ref,Reply}.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Special API.
-%% =============================================================================
-
-%% write_ti(OutPut)=
-%% OutPut=binary()
-%% Makes an extra entry into the trace information file (ti-file). This is useful
-%% if a pid-alias association is learned in another way than through a meta traced
-%% function call. Note that this API can only be used locally at the node in
-%% question.
-write_ti(OutPut) ->
- catch ?MODULE ! {write_ti,OutPut}.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API intended to be used on CallFuncs and RemoveFuncs.
-%% =============================================================================
-
-%% The reason there must be a special API for CallFuncs and RemoveFuncs are is
-%% that those functions are executed inside *this* process context. Hence they
-%% can not make function calls requiering this process to receive messages.
-
-%% Returns the tracer used for regular tracing. The reason this is implemented
-%% in this way is that this function is intended to be used in meta trace call-
-%% back functions. And there we can not have message passing API:s to the meta
-%% trace(!).
-get_tracer() ->
- get(tracer).
-%% -----------------------------------------------------------------------------
-
-%% Function equivalent to inviso_rt:tpm_ms/6. This function can *only* be used
-%% inside a CallFunc or a RemoveFunc.
-tpm_ms(Mod,Func,Arity,MSname,MS) ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- {ok,h_tpm_ms(Mod,Func,Arity,MSname,MS)};
- no ->
- {error,not_initiated}
- end.
-%% -----------------------------------------------------------------------------
-
-tpm_ms_tracer(Mod,Func,Arity,MSname,MS) ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- NewMS=add_tracer(MS,get_tracer()),
- {ok,h_tpm_ms(Mod,Func,Arity,MSname,NewMS)};
- no ->
- {error,not_initiated}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function that returns all MSname in use for Mod:Func/Arity
-list_tpm_ms(Mod,Func,Arity) ->
- {ok,h_list_tpm_ms(Mod,Func,Arity)}.
-%% -----------------------------------------------------------------------------
-
-%% Function equivalent to inviso_rt:ctpm_ms/5. This function can *only* be used
-%% inside a CallFunc or a RemoveFunc.
-ctpm_ms(Mod,Func,Arity,MSname) ->
- h_ctpm_ms(Mod,Func,Arity,MSname),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% The server implemenation.
-%% =============================================================================
-
-init(Parent,TiData,Tracer,InitPublLDmfa,RemovePublLDmf,CleanPublLDmf) ->
- process_flag(priority,high), % Since we may receive from many procs.
- register(?MODULE,self()), % So we can act as relay receiver.
- case open_traceinfo_file(TiData) of
- {ok,TI} -> % The ti.-file.
- TId=ets:new(?NAMED_MS_TAB,[named_table,set,protected]),
- PublLD=do_init_publ_ld(InitPublLDmfa),
- Parent ! {self(),ok},
- put(tracer,Tracer), % Uggly quick fix!
- loop(Parent,
- Tracer,
- TI,
- mk_new_ld(InitPublLDmfa,RemovePublLDmf,CleanPublLDmf,TId),
- PublLD,
- now());
- {error,Reason} ->
- Parent ! {self(),{error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-loop(Parent,Tracer,TI,LD,PrevPublLD,PrevCleanTime) ->
- {PublLD,CleanTime}=throw_old_failed(get_cleanpublldmf_ld(LD),PrevPublLD,PrevCleanTime),
- receive
- {{init_tpm,{Mod,Func,Arity},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- no -> % Good then we can add it!
- case check_tpm_args(Mod,Func,Arity) of
- true -> % Args are ok.
- {NewLD,NewPublLD}=
- h_init_tpm(Mod,Func,Arity,
- InitFunc,CallFunc,ReturnFunc,RemoveFunc,
- TI,LD,PublLD),
- reply(Parent,Ref,ok),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime);
- false -> % Faulty arguments,
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- yes -> % If it already exists, cant init again.
- reply(Parent,Ref,{error,already_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm,{Mod,Func,Arity,MS},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- no -> % Good then we can add it!
- case check_tpm_args(Mod,Func,Arity) of
- true -> % Args are ok.
- {NewLD,NewPublLD,N}=
- h_tpm(Mod,Func,Arity,MS,
- InitFunc,CallFunc,ReturnFunc,RemoveFunc,
- TI,LD,PublLD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime);
- false ->
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- yes ->
- reply(Parent,Ref,{error,already_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm,{Mod,Func,Arity,MS}},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- {NewLD,N}=h_tpm(Mod,Func,Arity,MS,LD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,PublLD,CleanTime);
- no -> % Must be initiated before.
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_tracer,{Mod,Func,Arity,MS},InitFunc,CallFunc,ReturnFunc,RemoveFunc},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- no -> % Good then we can add it!
- case check_tpm_args(Mod,Func,Arity) of
- true -> % Args are ok.
- NewMS=add_tracer(MS,Tracer),
- {NewLD,NewPublLD,N}=
- h_tpm(Mod,Func,Arity,NewMS,
- InitFunc,CallFunc,ReturnFunc,RemoveFunc,
- TI,LD,PublLD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime);
- false ->
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- yes ->
- reply(Parent,Ref,{error,already_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_tracer,{Mod,Func,Arity,MS}},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- NewMS=add_tracer(MS,Tracer),
- {NewLD,N}=h_tpm(Mod,Func,Arity,NewMS,LD),
- reply(Parent,Ref,{ok,N}),
- loop(Parent,Tracer,TI,NewLD,PublLD,CleanTime);
- no -> % Must be initiated before.
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_ms,{Mod,Func,Arity},MSname,MS},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- reply(Parent,Ref,{ok,h_tpm_ms(Mod,Func,Arity,MSname,MS)}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- no ->
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{tpm_ms_tracer,{Mod,Func,Arity},MSname,MS},Ref,Parent} ->
- case check_mfarity_exists(Mod,Func,Arity) of
- yes -> % Ok, and args must be ok then also.
- NewMS=add_tracer(MS,Tracer),
- reply(Parent,Ref,{ok,h_tpm_ms(Mod,Func,Arity,MSname,NewMS)}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- no ->
- reply(Parent,Ref,{error,not_initiated}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {{ctpm_ms,{Mod,Func,Arity},MSname},Ref,Parent} ->
- reply(Parent,Ref,ok),
- h_ctpm_ms(Mod,Func,Arity,MSname),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- {{ctpm,{Mod,Func,Arity}},Ref,Parent} ->
- case get_remove_func_ld(Mod,Func,Arity,LD) of
- false -> % Incorrect Mod:Func/Arity!
- reply(Parent,Ref,{error,bad_mfa}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime); % Do nothing!
- MF -> % {M,F}, Func or 'void'.
- catch erlang:trace_pattern({Mod,Func,Arity},false,[meta]),
- NewPublLD=do_removefunc(MF,Mod,Func,Arity,PublLD),
- NewLD=ctpm_ld(Mod,Func,Arity,LD),
- reply(Parent,Ref,ok),
- loop(Parent,Tracer,TI,NewLD,NewPublLD,CleanTime)
- end;
- {suspend,Parent} -> % Removes all meta trace patterns.
- stop_all_meta_tracing(get_all_meta_funcs_ld(LD),PublLD,LD),
- do_remove_publ_ld(get_removepublldmf_ld(LD),PublLD),
- NewPublLD=do_init_publ_ld(get_initpublldmfa_ld(LD)),
- loop(Parent,Tracer,TI,reset_ld(LD),NewPublLD,CleanTime);
- {stop,Parent} -> % Make a controlled shutdown.
- stop_all_meta_tracing(get_all_meta_funcs_ld(LD),PublLD,LD),
- do_remove_publ_ld(get_removepublldmf_ld(LD),PublLD),
- close_traceinfo_file(TI); % And then simply terminate.
- {trace_ts,Pid,call,{M,F,Args},TS} ->
- case handle_meta(get_call_func_ld(M,F,length(Args),LD),Pid,{call,Args,TS},PublLD) of
- {ok,NewPublLD,Output} when is_binary(Output);is_list(Output) ->
- write_output(TI,Output),
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- {ok,NewPublLD,_} -> % No output to the ti-file this time.
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- _ -> % Not handled correct, not much to do.
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {trace_ts,Pid,TypeTag,{M,F,Arity},Value,TS}
- when TypeTag==return_from;TypeTag==exception_from ->
- case handle_meta(get_return_func_ld(M,F,Arity,LD),Pid,{TypeTag,Value,TS},PublLD) of
- {ok,NewPublLD,Output} when is_binary(Output);is_list(Output) ->
- write_output(TI,Output),
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- {ok,NewPublLD,_} -> % No output to the ti-file this time.
- loop(Parent,Tracer,TI,LD,NewPublLD,CleanTime);
- _ -> % Not handled correct, not much to do.
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end;
- {relayed_meta,Bin} ->
- write_output(TI,Bin),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- {write_ti,OutPut} ->
- write_output(TI,OutPut),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- {get_state,Ref,From} -> % Debug function.
- reply(From,Ref,{ok,LD,PublLD}),
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime);
- _Other ->
- loop(Parent,Tracer,TI,LD,PublLD,CleanTime)
- end.
-
-
-%% =============================================================================
-%% First level help functions.
-%% =============================================================================
-
-%% Function which opens the trace-information file(s). It must understand
-%% the tidata specification which is part of the tracerdata given to the
-%% runtime component during init_tracing.
-%% It must return an internal notation of the time of file open and a
-%% useful descriptor the write_output function can use.
-%% Returns {ok,TiDescriptor} or {error,Reason}.
-open_traceinfo_file({file,FileName}) -> % A plain raw binary file.
- case file:open(FileName,[write,raw,binary]) of
- {ok,FD} ->
- {ok,{file,FD}};
- {error,Reason} ->
- {error,{open,[FileName,Reason]}}
- end;
-open_traceinfo_file({relay,ToNode}) -> % Use distributed Erlang.
- {ok,{relay,ToNode}};
-open_traceinfo_file(IncorrectTI) ->
- {error,{badarg,IncorrectTI}}.
-%% -----------------------------------------------------------------------------
-
-close_traceinfo_file({file,FD}) ->
- file:close(FD);
-close_traceinfo_file(_) ->
- ok.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling initializing meta tracing of a function.
-%% Returns {NewLD,NewPublLD}.
-h_init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD) ->
- case do_initfunc(InitFunc,Mod,Func,Arity,PublLD) of
- {NewPublLD,Output} ->
- write_output(TI,Output),
- NewLD=init_tpm_ld(Mod,Func,Arity,CallFunc,ReturnFunc,RemoveFunc,LD),
- {NewLD,NewPublLD};
- false -> % The initfunc did not do anything.
- NewLD=init_tpm_ld(Mod,Func,Arity,CallFunc,ReturnFunc,RemoveFunc,LD),
- {NewLD,PublLD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling initializing meta tracing of a function and also
-%% set the meta trace pattern as specified.
-%% Returns {NewLD,NewPublLD,N}.
-h_tpm(Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD) ->
- {NewLD,NewPublLD}=
- h_init_tpm(Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc,TI,LD,PublLD),
- case set_meta_tracing(Mod,Func,Arity,MS) of
- true -> % Ok, set one pattern.
- {NewLD,NewPublLD,1};
- false ->
- {NewLD,NewPublLD,0}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function handling setting meta trace patter for a function which has
-%% already been intialized. Note that we must remove all potentially stored
-%% match-specs, if this function has been given match-specs before with
-%% tpm_ms.
-%% Returns a {NewLD,N}.
-h_tpm(Mod,Func,Arity,MS,LD) ->
- case set_meta_tracing(Mod,Func,Arity,MS) of
- true ->
- {remove_ms_ld(Mod,Func,Arity,LD),1};
- false ->
- {LD,0}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function that adds a match-spec to Mod:Func/Arity. It is not defined
-%% in which order the match-specs will be given to the BIF.
-%% Note that if an MS with the same name as an exiting is inserted, the previous
-%% match-spec will be removed.
-%% Very important to realise is that the empty meta match spec [] imposes no
-%% restrictions what so ever on the generating of meta trace call messages.
-%% Uncontrolled sending of such messages may quickly drain power from the system.
-%% Since an empty match-spec will "disappear" when added to other match specs,
-%% the empty match is transformed to what it actually is: [{'_',[],[]}].
-%% Returns 0 or 1 indicating failure or success.
-h_tpm_ms(Mod,Func,Arity,MSname,MS) ->
- MSsNames=get_ms_ld(Mod,Func,Arity), % Fetch all previous match-specs.
- TransformedMS=h_tpm_ms_convert_null_ms(MS),
- MSsNames1=lists:keydelete(MSname,1,MSsNames), % If it already existed, it is gone!
- NewMSs=lists:flatten([TransformedMS,lists:map(fun({_Name,MSx})->MSx end,MSsNames1)]),
- case set_meta_tracing(Mod,Func,Arity,NewMSs) of
- true -> % We only save the MS if it was good.
- put_ms_ld(Mod,Func,Arity,MSname,TransformedMS,MSsNames1),
- 1;
- false ->
- 0
- end.
-
-%% Help function converting the null match spec into, still a null match spec,
-%% on a proper match spec format. This because it will otherwise be difficult
-%% to see the difference between no active tpm_ms and all a set of null ms.
-h_tpm_ms_convert_null_ms([]) ->
- [{'_',[],[]}];
-h_tpm_ms_convert_null_ms(MS) ->
- MS.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a list of all names used for match-functions for
-%% the Mod:Func/Arity in question.
-h_list_tpm_ms(Mod,Func,Arity) ->
- MSsNames=get_ms_ld(Mod,Func,Arity), % A list of {MSname,MS}.
- lists:map(fun({MSname,_})->MSname end,MSsNames).
-%% -----------------------------------------------------------------------------
-
-%% Function that removes a named match-spec. Returns nothing significant.
-%% Note that if we end up with no match-specs, we must remove the meta trace
-%% patten all together. That is bringing the function back to just initiated.
-h_ctpm_ms(Mod,Func,Arity,MSname) ->
- case get_ms_ld(Mod,Func,Arity) of
- [] -> % The name does certainly not exist!
- true; % We don't have to do anything.
- MSsNames ->
- case lists:keysearch(MSname,1,MSsNames) of
- {value,{_,_MS}} -> % Ok, we must do something!
- NewMSsNames=lists:keydelete(MSname,1,MSsNames),
- case lists:flatten(lists:map(fun({_Name,MS})->MS end,NewMSsNames)) of
- [] -> % This means stop meta tracing.
- set_meta_tracing(Mod,Func,Arity,false);
- NewMSs ->
- set_meta_tracing(Mod,Func,Arity,NewMSs)
- end,
- set_ms_ld(Mod,Func,Arity,NewMSsNames);
- false -> % But this name does not exist.
- true % So we do not have to do anything.
- end
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function that checks the arguments to the meta trace pattern. The reason we
-%% must do this is that we can only allow meta tracing on specific functions and
-%% not using wildpatterns. Otherwise the meta trace server will not understand
-%% which callfunc for instance to call when a meta-trace message is generated
-%% for a function.
-%% Returns 'true' or 'false'.
-check_tpm_args(Mod,Func,Arity)
- when is_atom(Mod),is_atom(Func),is_integer(Arity),Mod/='_',Func/='_' ->
- true;
-check_tpm_args(_,_,_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function which calls the actual BIF setting meta-trace-patterns.
-%% Returns 'true' or 'false'.
-set_meta_tracing(Mod,Func,Arity,MS) when is_atom(Mod) ->
- case erlang:module_loaded(Mod) of
- true ->
- set_meta_tracing_2(Mod,Func,Arity,MS);
- false -> % The module is not loaded.
- case code:ensure_loaded(Mod) of
- {module,_Mod} ->
- set_meta_tracing_2(Mod,Func,Arity,MS);
- {error,_Reason} -> % Could not load the module.
- false % No use try to trace.
- end
- end;
-set_meta_tracing(_,_,_,_) ->
- false.
-
-set_meta_tracing_2(Mod,Func,Arity,MS) ->
- case catch erlang:trace_pattern({Mod,Func,Arity},MS,[meta]) of
- 0 -> % Hmm, nothing happend :-)
- false;
- N when is_integer(N) -> % The normal case, some functions were hit.
- true;
- {'EXIT',_Reason} ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which removes all meta trace pattern for the functions mentioned
-%% in the list being first argument. It also executes the remove funcs for each
-%% and every no longer meta traced function. This done since some of the remove
-%% functions may do side-effects (like deleteing ETS tables).
-%% Returns nothing significant.
-stop_all_meta_tracing([{M,F,Arity}|Rest],PublLD,LD) ->
- catch erlang:trace_pattern({M,F,Arity},false,[meta]),
- NewPublLD=do_removefunc(get_remove_func_ld(M,F,Arity,LD),M,F,Arity,PublLD),
- stop_all_meta_tracing(Rest,NewPublLD,LD);
-stop_all_meta_tracing([],_,_) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% This function calls the function registered to be handler for a certain
-%% meta-traced function. Such a function or fun must take three arguments
-%% and return {ok,NewPrivLD,OutPutBinary} or 'false'. OutPutBinary may be
-%% something else, and is then ignored.
-handle_meta({M,F},Pid,Arg1,PrivLD) ->
- (catch M:F(Pid,Arg1,PrivLD));
-handle_meta(Fun,Pid,Arg1,PrivLD) when is_function(Fun) ->
- (catch Fun(Pid,Arg1,PrivLD));
-handle_meta(_,_,_,_) -> % Don't know how to do this.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function writing output from a callback function to the ti-file.
-%% Output can be a binary or a list of binaries.
-write_output(TI,[OutPut|Rest]) ->
- write_output(TI,OutPut),
- write_output(TI,Rest);
-write_output({file,FD},Bin) when is_binary(Bin) -> % Plain direct-binary file
- Size=byte_size(Bin),
- file:write(FD,list_to_binary([<<0,Size:32>>,Bin]));
-write_output({relay,ToNode},Bin) when is_atom(ToNode),is_binary(Bin) ->
- {inviso_rt_meta,ToNode} ! {relayed_meta,Bin};
-write_output(_,_) -> % Don't understand, just skip.
- true.
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Various help functions.
-%% =============================================================================
-
-%% Help function initializing the public loopdata structure. Note that if the
-%% supplied InitPublLDmfa is faulty we let the structure become the error.
-%% The error will most likely turn up in an error report somewhere, eventually.
-do_init_publ_ld({M,F,Args}) when is_atom(M),is_atom(F),is_list(Args) ->
- case catch apply(M,F,Args) of
- {'EXIT',_Reason} ->
- {error,init_publ_ld_func}; % Let the struct be this error!
- InitialPublLD ->
- InitialPublLD
- end;
-do_init_publ_ld(_) ->
- {error,init_publ_ld_func}.
-%% -----------------------------------------------------------------------------
-
-%% Help function which removes the public loopdata structure. The function does
-%% not necessarily have to exist. Returns nothing significant.
-do_remove_publ_ld({M,F},PublLD) when is_atom(M),is_atom(F) ->
- catch M:F(PublLD);
-do_remove_publ_ld(_,_) ->
- true.
-%% -----------------------------------------------------------------------------
-
-%% Hlp function initializing a particular meta traced function into the public
-%% loopdata. Note that the function is not mandatory.
-%% Returns {NewPublLD,Output} or 'false'.
-do_initfunc({M,F},Mod,Func,Arity,PublLD) when is_atom(M),is_atom(F) ->
- case catch M:F(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD,Output} ->
- {NewPublLD,Output};
- _ -> % Everything else is an error.
- false % Act as no initialization function.
- end;
-do_initfunc(Fun,Mod,Func,Arity,PublLD) when is_function(Fun) ->
- case catch Fun(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD,Output} ->
- {NewPublLD,Output};
- _ -> % Everything else is an error.
- false % Act as no initialization function.
- end;
-do_initfunc(_,_,_,_,_) -> % Perhaps too generous, should be 'void' only.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing a particular meta traced function from the public
-%% loopdata. Note that we do not make much noice should the call back function
-%% be faulty.
-do_removefunc({M,F},Mod,Func,Arity,PublLD) when is_atom(M),is_atom(F) ->
- case catch M:F(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD} ->
- NewPublLD;
- _ -> % Everything else is an error.
- PublLD % Act as no initialization function.
- end;
-do_removefunc(Fun,Mod,Func,Arity,PublLD) when is_function(Fun) ->
- case catch Fun(Mod,Func,Arity,PublLD) of
- {ok,NewPublLD} ->
- NewPublLD;
- _ -> % Everything else is an error.
- PublLD % Act as no initialization function.
- end;
-do_removefunc(_,_,_,_,PublLD) ->
- PublLD.
-%% -----------------------------------------------------------------------------
-
-%% Function that, if the time has come, goes through the priv-ld structure and
-%% cleans away entryn left behind. The usual cause is that the function call
-%% caused an exception and there were therefore no matching return_from.
-%% Returns {NewPrivLD,now()}.
-throw_old_failed({M,F},PrivLD,PrevClean) ->
- case difference_in_now(PrevClean,now(),60) of % We clean once every minute.
- true ->
- case catch apply(M,F,[PrivLD]) of
- {'EXIT',_Reason} -> % Something went wrong, ignore it.
- {PrivLD,now()}; % Just keep the old priv-ld.
- NewPrivLD -> % The function must return a priv-ld.
- {NewPrivLD,now()}
- end;
- false -> % Not time yet!
- {PrivLD,PrevClean}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function comparing two now timestamps. Returns true or false depending
-%% on if S2 is more than DiffS seconds after S1. Only works for differences
-%% less than 1 million seconds.
-difference_in_now({MegaS1,S1,_},{MegaS2,S2,_},DiffS) ->
- if
- MegaS1+1<MegaS2 -> % More than 1 Mega sec. difference.
- true;
- MegaS1==MegaS2,S1+DiffS<S2 ->
- true;
- MegaS1+1==MegaS2,S1+DiffS<S2+1000000 ->
- true;
- true ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% This help function adds a {tracer,Tracer} to the enable-list in a 'trace'
-%% match spec action. The reason for this is that the author of the a meta
-%% match spec meant to turn tracing on for the process executing the match spec
-%% can not know the tracer. This since the match spec is most likely authored
-%% at the control component's node, and not here.
-%% Note the double tuple necessary to make it just precise a tuple!
-%% Returns a new match spec.
-add_tracer([MS1|Rest],Tracer) ->
- [add_tracer_2(MS1,Tracer)|add_tracer(Rest,Tracer)];
-add_tracer([],_) ->
- [];
-add_tracer(NotList,_Tracer) -> % Can be 'false', but also an error.
- NotList.
-
-add_tracer_2({Head,Cond,Body},Tracer) ->
- {Head,Cond,add_tracer_3(Body,Tracer)};
-add_tracer_2(Faulty,_Tracer) ->
- Faulty.
-
-add_tracer_3([{trace,Disable,Enable}|Rest],Tracer) when is_list(Enable) ->
- [{trace,Disable,Enable++[{{tracer,Tracer}}]}|Rest];
-add_tracer_3([ActionTerm|Rest],Tracer) ->
- [ActionTerm|add_tracer_3(Rest,Tracer)];
-add_tracer_3([],_Tracer) ->
- [];
-add_tracer_3(FaultyBody,_Tracer) ->
- FaultyBody.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Help functions handling internal loopdata.
-%% -----------------------------------------------------------------------------
-
--record(ld,{init_publ_ld_mfa, % {M,F,Args}
- remove_publ_ld_mf, % {M,F} | void
- clean_publ_ld_mf, % {Mod,Func}
- ms_mfarities=notable, % ETS holding names match functions.
- call_mfarities=[], % [{{M,F,Arity},2-TupleOrFun},...]
- return_mfarities=[], % [{{M,F,Arity},2-TupleOrFun},...]
- remove_mfarities=[]
- }).
-
-mk_new_ld(InitPublLDmfa,RemovePublLDmf,CleanPublLDmf,TId) ->
- #ld{
- init_publ_ld_mfa=InitPublLDmfa,
- remove_publ_ld_mf=RemovePublLDmf,
- clean_publ_ld_mf=CleanPublLDmf,
- ms_mfarities=TId
- }.
-%% -----------------------------------------------------------------------------
-
-%% Function which restores the internal loop data to somekind of initial state.
-%% This is useful when tracing has been suspended.
-reset_ld(#ld{init_publ_ld_mfa=InitPublLDmfa,
- remove_publ_ld_mf=RemovePublLDmf,
- clean_publ_ld_mf=CleanPublLDmf,
- ms_mfarities=TId}) ->
- ets:match_delete(TId,{'_','_'}), % Empty the table.
- #ld{init_publ_ld_mfa=InitPublLDmfa,
- remove_publ_ld_mf=RemovePublLDmf,
- clean_publ_ld_mf=CleanPublLDmf,
- ms_mfarities=TId}.
-%% -----------------------------------------------------------------------------
-
-get_initpublldmfa_ld(#ld{init_publ_ld_mfa=InitPublLDmfa}) ->
- InitPublLDmfa.
-%% -----------------------------------------------------------------------------
-
-get_removepublldmf_ld(#ld{remove_publ_ld_mf=RemovePublLDmf}) ->
- RemovePublLDmf.
-%% -----------------------------------------------------------------------------
-
-get_cleanpublldmf_ld(#ld{clean_publ_ld_mf=CleanPublLDmf}) ->
- CleanPublLDmf.
-%% -----------------------------------------------------------------------------
-
-%% Help function adding data associated with a meta traced function to the
-%% internal loopdata. Called when meta tracing is activated for M:F/Arity.
-init_tpm_ld(M,F,Arity,CallFunc,ReturnFunc,RemoveFunc,LD) ->
- ets:insert(LD#ld.ms_mfarities,{{M,F,Arity},[]}),
- CallFuncs=LD#ld.call_mfarities,
- ReturnFuncs=LD#ld.return_mfarities,
- RemoveFuncs=LD#ld.remove_mfarities,
- LD#ld{call_mfarities=[{{M,F,Arity},CallFunc}|CallFuncs],
- return_mfarities=[{{M,F,Arity},ReturnFunc}|ReturnFuncs],
- remove_mfarities=[{{M,F,Arity},RemoveFunc}|RemoveFuncs]}.
-%% -----------------------------------------------------------------------------
-
-%% Help function which answers the question if we have already initiated the
-%% function. It is done by looking in the ETS-table with named match-functions.
-%% If there is an entry in the set-type table for M:F/Arity, the function is
-%% initiated.
-%% Returns 'yes' or 'no'.
-check_mfarity_exists(M,F,Arity) ->
- case ets:lookup(?NAMED_MS_TAB,{M,F,Arity}) of
- [] ->
- no;
- [_] ->
- yes
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function adding an entry with [{MSname,MSlist}|MSsNames] for M:F/Arity.
-%% Note that any already existing entry is removed.
-%% Returns nothing significant.
-put_ms_ld(M,F,Arity,MSname,MS,MSsNames) ->
- ets:insert(?NAMED_MS_TAB,{{M,F,Arity},[{MSname,MS}|MSsNames]}).
-%% -----------------------------------------------------------------------------
-
-%% Help function taking a list of {MSname,MSs} and storing them in the
-%% internal loop data structure. The storage is actually implemented as an ETS
-%% table. Any previous list of {MSname,MSs} associated with this {M,F,Arity} will
-%% be lost. Returns nothing significant.
-set_ms_ld(M,F,Arity,MSsNames) ->
- ets:insert(?NAMED_MS_TAB,{{M,F,Arity},MSsNames}).
-%% -----------------------------------------------------------------------------
-
-%% Help function fetching a list of {MSname,MatchSpecs} for a M:F/Arity. The
-%% match-functions are stored in an ETS table searchable on {M,F,Arity}.
-get_ms_ld(M,F,Arity) ->
- case ets:lookup(?NAMED_MS_TAB,{M,F,Arity}) of
- [{_MFArity,MSsNames}] ->
- MSsNames;
- [] ->
- []
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing all saved match-specs for a certain M:F/Arity.
-%% Returns a new loopdata structure.
-remove_ms_ld(M,F,Arity,LD) ->
- ets:delete(LD#ld.ms_mfarities,{M,F,Arity}),
- LD.
-%% -----------------------------------------------------------------------------
-
-%% Help function which removes all information about a meta traced function from
-%% the internal loopdata. Returns a new loopdata structure.
-ctpm_ld(M,F,Arity,LD) ->
- ets:delete(LD#ld.ms_mfarities,{M,F,Arity}),
- NewCallFuncs=lists:keydelete({M,F,Arity},1,LD#ld.call_mfarities),
- NewReturnFuncs=lists:keydelete({M,F,Arity},1,LD#ld.return_mfarities),
- NewRemoveFuncs=lists:keydelete({M,F,Arity},1,LD#ld.remove_mfarities),
- LD#ld{call_mfarities=NewCallFuncs,
- return_mfarities=NewReturnFuncs,
- remove_mfarities=NewRemoveFuncs}.
-%% -----------------------------------------------------------------------------
-
-get_call_func_ld(M,F,Arity,#ld{call_mfarities=CallFuncs}) ->
- case lists:keysearch({M,F,Arity},1,CallFuncs) of
- {value,{_,MF}} ->
- MF;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_return_func_ld(M,F,Arity,#ld{return_mfarities=CallFuncs}) ->
- case lists:keysearch({M,F,Arity},1,CallFuncs) of
- {value,{_,MF}} ->
- MF;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-get_remove_func_ld(M,F,Arity,#ld{remove_mfarities=RemoveFuncs}) ->
- case lists:keysearch({M,F,Arity},1,RemoveFuncs) of
- {value,{_,MF}} ->
- MF;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function returning a list of all {Mod,Func,Arity} which are currently meta
-%% traced. It does do by listifying the call_mfarities field in the internal
-%% loopdata.
-get_all_meta_funcs_ld(#ld{call_mfarities=CallFuncs}) ->
- lists:map(fun({MFArity,_})->MFArity end,CallFuncs).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Functions for the standard PublLD structure.
-%%
-%% It is tuple {Part1,GlobalData} where Part1 is of length at least 2.
-%% Where each field is a list of tuples. The last item in each tuple shall be
-%% a now tuple, making it possible to clean it away should it be too old to be
-%% relevant (there was no return_from message due to a failure).
-%% Other fields can be used for other functions.
-%% The GlobalData is not cleaned but instead meant to store data must be passed
-%% to each CallFunc when a meta trace message arrives.
-%% =============================================================================
-
-%% Function returning our standard priv-loopdata structure.
-init_std_publld(Size,GlobalData) ->
- {list_to_tuple(lists:duplicate(Size,[])),GlobalData}.
-%% -----------------------------------------------------------------------------
-
-%% Function capable of cleaning out a standard publ-ld. The last element of each
-%% tuple must be the now item.
-%% Returns a new publ-ld structure.
-clean_std_publld({Part1,GlobalData}) ->
- {clean_std_publld_2(Part1,now(),tuple_size(Part1),[]),GlobalData}.
-
-clean_std_publld_2(_,_,0,Accum) ->
- list_to_tuple(Accum);
-clean_std_publld_2(PublLD,Now,Index,Accum) ->
- NewTupleList=clean_std_publld_3(element(Index,PublLD),Now),
- clean_std_publld_2(PublLD,Now,Index-1,[NewTupleList|Accum]).
-
-clean_std_publld_3([Tuple|Rest],Now) ->
- PrevNow=element(tuple_size(Tuple),Tuple), % Last item shall be the now item.
- case difference_in_now(PrevNow,Now,30) of
- true -> % Remove it then!
- clean_std_publld_3(Rest,Now);
- false -> % Keep it!
- [Tuple|clean_std_publld_3(Rest,Now)]
- end;
-clean_std_publld_3([],_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Functions used as handling functions (as funs) for registered process names.
-%% (Given that we use the standard priv-ld, otherwise you must do your own!).
-%% =============================================================================
-
-%% Call-back for initializing the meta traced functions there are quick functions
-%% for. Returns a new public loop data structure.
-metafunc_init(erlang,register,2,{Part1,GlobalData}) ->
- {setelement(1,Part1,[]),GlobalData}.
-%% -----------------------------------------------------------------------------
-
-%% Call-function for erlang:register/2.
-%% This function adds the call to register/2 to a standard priv-ld structure.
-%% Note that we *must* search for previous entries from the same process. If such
-%% still in structure it means a failed register/2 call. It must first be removed
-%% so it can not be mixed up with this one. Since meta-trace message will arrive
-%% in order, there was no return_from message for that call if we are here now.
-local_register_call(CallingPid,{call,[Alias,Pid],TS},{Part1,GlobalData}) ->
- TupleList=element(1,Part1), % The register/2 entry in a std. priv-ld.
- NewTupleList=lists:keydelete(CallingPid,1,TupleList), % If present, remove previous call.
- {ok,
- {setelement(1,Part1,[{CallingPid,{Alias,Pid},TS}|NewTupleList]),GlobalData},
- void}.
-
-%% Return-function for the erlang:register/2 BIF.
-%% This function formulates the output and removes the corresponding call entry
-%% from the standard priv-ld structure.
-local_register_return(CallingPid,{return_from,_Val,_TS},PublLD={Part1,GlobalData}) ->
- TupleList=element(1,Part1), % The register/2 entry in a std. priv-ld.
- case lists:keysearch(CallingPid,1,TupleList) of
- {value,{_,{Alias,Pid},NowTS}} ->
- NewTupleList=lists:keydelete(CallingPid,1,TupleList),
- {ok,
- {setelement(1,Part1,NewTupleList),GlobalData},
- term_to_binary({Pid,Alias,alias,NowTS})};
- false -> % Strange, then don't know what to do.
- {ok,PublLD,void} % Do nothing seems safe.
- end;
-local_register_return(CallingPid,{exception_from,_Val,_TS},{Part1,GlobalData}) ->
- TupleList=element(1,Part1), % The register/2 entry in a std. priv-ld.
- NewTupleList=lists:keydelete(CallingPid,1,TupleList),
- {ok,{setelement(1,Part1,NewTupleList),GlobalData},void}; % No association then.
-local_register_return(_,_,PublLD) -> % Don't understand this.
- {ok,PublLD,void}.
-
-%% When unregister/1 us called we simply want a unalias entry in the ti-file.
-%% We can unfortunately not connect it with a certain pid.
-local_unregister_call(_CallingPid,{_TypeTag,[Alias],TS},PublLD) ->
- {ok,PublLD,term_to_binary({undefined,Alias,unalias,TS})}.
-%% -----------------------------------------------------------------------------
-
-%% Call-function for global:register_name/2,/3.
-%% This function is actually the call function for the handle_call/3 in the
-%% global server. Note that we must check that we only do this on the node
-%% where Pid actually resides.
-global_register_call(_CallingPid,{call,[{register,Alias,P,_},_,_],TS},PublLD)
- when node(P)==node()->
- {ok,PublLD,term_to_binary({P,{global,Alias},alias,TS})};
-global_register_call(_CallingPid,_,PublLD) ->
- {ok,PublLD,void}.
-
-%% Call-function for global:unregister_name. It acutally checks on the use of
-%% global:delete_global_name/2 which is called when ever a global name is removed.
-global_unregister_call(_CallingPid,{call,[Alias,P],TS},PublLD) when node(P)==node()->
- {ok,PublLD,term_to_binary({P,{global,Alias},unalias,TS})};
-global_unregister_call(_CallingPid,_,PublLD) ->
- {ok,PublLD,void}.
-%% -----------------------------------------------------------------------------
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
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 1152f7259d..602048dc21 100644
--- a/lib/runtime_tools/src/runtime_tools.app.src
+++ b/lib/runtime_tools/src/runtime_tools.app.src
@@ -17,16 +17,13 @@
%% %CopyrightEnd%
%%
{application, runtime_tools,
- [{description, "RUNTIME_TOOLS version 1"},
+ [{description, "RUNTIME_TOOLS"},
{vsn, "%VSN%"},
- {modules, [dbg,observer_backend,percept_profile,
- inviso_rt,inviso_rt_lib,inviso_rt_meta,
- inviso_as_lib,inviso_autostart,inviso_autostart_server,
+ {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..ab9fa534d5 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-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 +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.
-init(AutoModArgs) ->
+%% -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/Makefile b/lib/runtime_tools/test/Makefile
index 4979b9c7b1..bcabdf13ed 100644
--- a/lib/runtime_tools/test/Makefile
+++ b/lib/runtime_tools/test/Makefile
@@ -5,8 +5,6 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
dyntrace_SUITE \
runtime_tools_SUITE \
- inviso_testmodule1_foo \
- inviso_SUITE \
dbg_SUITE \
erts_alloc_config_SUITE
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/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl
deleted file mode 100644
index c64c40b945..0000000000
--- a/lib/runtime_tools/test/inviso_SUITE.erl
+++ /dev/null
@@ -1,2838 +0,0 @@
-%%
-%% %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%
-%%
-%% Description:
-%% Test suite for inviso (basic parts, i.e not inviso tools). Note that
-%% inviso basic parts have modules in both the runtime_tools and
-%% inviso applications.
-%%
-%% Authors:
-%% Ann-Marie L�f, [email protected]
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_SUITE).
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("kernel/include/file.hrl").
-
--define(l,?line).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [basic_dist_trace_1, basic_dist_trace_2,
- basic_dist_trace_3, basic_dist_trace_ti_1,
- basic_dist_trace_ti_2, basic_dist_trace_ti_3,
- suspend_dist_trace_ti_1, suspend_dist_trace_ti_2,
- meta_cleanfunc_dist_1, basic_handlerfun_dist_1,
- delete_log_dist_1, autostart_dist_1, autostart_dist_2,
- autostart_dist_3, running_alone_dist_1,
- running_alone_dist_2, running_alone_dist_3,
- running_alone_dist_4, running_alone_dist_5,
- overload_dist_1, overload_dist_2, overload_dist_3,
- overload_dist_4, overload_dist_5, subscribe_dist_1,
- lfm_trace_dist_1, lfm_trace_ti_dist_2,
- handle_logfile_sort_wrapset, fetch_log_dist_trace_1,
- fetch_log_dist_trace_2, fetch_log_dist_trace_3,
- fetch_log_dist_error_1, fetch_log_dist_error_2,
- expand_regexp_dist_1, only_loaded_dist_1].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-init_per_suite(Config) ->
- case test_server:is_native(lists) of
- true ->
- {skip,"Native libs -- tracing doesn't work"};
- false ->
- %% We never know who messed up this node before this suite! :-)
- erlang:trace_pattern({'_','_','_'},[],[local]),
- erlang:trace_pattern({'_','_','_'},[],[global]),
- erlang:trace(all,false,[all]),
-
- ok=application:start(runtime_tools),
- Config
- end.
-
-end_per_suite(_Config) ->
- ?l ok=application:stop(runtime_tools).
-
-
-%% For each distributed testcase, we need two other distributed nodes to run the
-%% runtime components on. Since they are freshly started every time there is no
-%% need to clean them up first.
-init_per_testcase(_Case,Config) ->
- ?l TH=test_server:timetrap(100000),
- ?l {ok,Node1}=test_server:start_node(inviso1,peer,[]),
- ?l {ok,Node2}=test_server:start_node(inviso2,peer,[]),
- ?l SuiteDir=filename:dirname(code:which(?MODULE)),
-
- %% Otherwise peer nodes will not find this module!
- ?l true=rpc:call(Node1,code,add_patha,[SuiteDir]),
- ?l true=rpc:call(Node2,code,add_patha,[SuiteDir]),
-
- ?l start_side_effect_logger(node()),
- ?l start_side_effect_logger(Node1),
- ?l start_side_effect_logger(Node2),
-
-
- %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
-
-% %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT, windows.
-% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
-% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
-
- ?l ok=rpc:call(Node1,application,start,[runtime_tools]),
- ?l ok=rpc:call(Node2,application,start,[runtime_tools]),
- ?l timer:sleep(100), % Problem with autostarted runtime.
- %% The following is a test that the inviso_rt processes which are autostarted
- %% are now gone.
-
- ?l ok=poll(rpc,call,[Node1,erlang,whereis,[inviso_rt]],undefined,20),
- ?l ok=poll(rpc,call,[Node2,erlang,whereis,[inviso_rt]],undefined,20),
-
-% ?l ok=poll(rpc,call,[Node1,supervisor,which_children,[runtime_tools_sup]],[],20),
-% ?l ok=poll(rpc,call,[Node2,supervisor,which_children,[runtime_tools_sup]],[],20),
- NewConfig1=insert_remotenode_config(inviso1,Node1,Config),
- NewConfig2=insert_remotenode_config(inviso2,Node2,NewConfig1),
- insert_timetraphandle_config(TH,NewConfig2).
-%% -----------------------------------------------------------------------------
-
-end_per_testcase(Case,Config) ->
- ?l test_server:stop_node(get_remotenode_config(inviso1,Config)),
- ?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
-
- case whereis(inviso_c) of
- undefined -> % Should not exist.
- true;
- Pid when is_pid(Pid) -> % But if it exists...
- exit(Pid,kill), % Remove it!
- io:format("Had to kill the control component in end_per_testcase,~p.~n",[Case])
- end,
- case whereis(inviso_rt) of
- undefined -> % Should not exist.
- true;
- Pid2 when is_pid(Pid2) -> % But if it exists...
- exit(Pid2,kill), % Remove it!
- io:format("Had to kill local runtime component in end_per_testcase,~p.~n",[Case])
- end,
- ?l process_killer([inviso_test_proc,
- inviso_tab_proc,
- inviso_collector_proc,
- global_inviso_test_proc]),
- ?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
-
- NewConfig1=remove_remotenode_config(inviso1,Config),
- NewConfig2=remove_remotenode_config(inviso2,NewConfig1),
- remove_timetraphandle_config(NewConfig2).
-%% -----------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Testcases.
-%% ==============================================================================
-
-%% TEST CASE: Basic, distributed, trace only.
-basic_dist_trace_1(suite) -> [];
-basic_dist_trace_1(doc) ->
- ["Basic case, start of distributed tracing, using only trac."];
-basic_dist_trace_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
- "tf1_"++
- atom_to_list(N)
- ])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- activate_local_tracing(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: Basic, distributed, activate global tracing for functions in modules
-%% pointed out using a regexp. No tracing will be done.
-basic_dist_trace_2(suite) -> [];
-basic_dist_trace_2(doc) ->
- [""];
-basic_dist_trace_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
- "tf1a_"++
- atom_to_list(N)
- ])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- Funcs1=activate_global_tracing_regexp(Nodes),
- deactivate_global_tracing_regexp(Nodes,Funcs1),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Basic, distributed, activate global tracing for functions in modules
-%% pointed out using a dir-regexp. No tracing will be done.
-basic_dist_trace_3(suite) -> [];
-basic_dist_trace_3(doc) ->
- [""];
-basic_dist_trace_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
- "tf1b_"++
- atom_to_list(N)
- ])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- Funcs1=activate_global_tracing_regexp_dir(Nodes),
- deactivate_global_tracing_regexp_dir(Nodes,Funcs1),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Basic, distributed, trace and ti.
-basic_dist_trace_ti_1(suite) -> [];
-basic_dist_trace_ti_1(doc) ->
- [""];
-basic_dist_trace_ti_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- deactivate_meta_tracing(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
- ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3),
- stop(Nodes),
- timer:sleep(200), % Give it time to terminate.
- ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
- ?l undefined=whereis(inviso_rt_meta), % Still gone.
- ok.
-%% -----------------------------------------------------------------------------
-
-%% Test CASE: Testing that the tpm_tracer functionality works. That is appending
-%% {tracer,Tracer} to a meta match spec.
-basic_dist_trace_ti_2(suite) -> [];
-basic_dist_trace_ti_2(doc) ->
- [""];
-basic_dist_trace_ti_2(Config) when is_list(Config) ->
- case erlang:system_info(version) of
- "5.4"++_ -> % Perhaps not perfect, but work now :-)
- {skip,"Old emulator"};
- _ ->
- basic_dist_trace_ti_2_do(Config)
- end.
-
-basic_dist_trace_ti_2_do(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_deactivate_meta_tracing_tracer(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Basic, distributed, trace and ti, where we try to use ctp_all to
-%% check that all global and local patterns are removed but that meta patterns
-%% remain.
-%% This test also checks that if the meta tracer is terminated an error value
-%% is generated when trying to do meta tracing at that node.
-basic_dist_trace_ti_3(suite) -> [];
-basic_dist_trace_ti_3(doc) ->
- [""];
-basic_dist_trace_ti_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_global_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- ?l {ok,NodeResults1}=inviso:ctp_all(Nodes), % Removes local and global patterns.
- ?l true=check_noderesults(Nodes,ok,NodeResults1),
- ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,which,1},traced],{traced,false}),
- ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,get_path,0},traced],{traced,false}),
- %% But meta patters shall remain.
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{lists,module_info,0},meta_match_spec],
- fun({meta_match_spec,L})when length(L)>0 ->true end),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{lists,module_info,0},meta],
- fun({meta,P})when is_pid(P) ->
- P=rpc:call(node(P),erlang,whereis,[inviso_rt_meta]),
- true
- end),
- %% Now kill the meta tracer somewhere and try to activate meta tracing.
- ?l [ANode|_]=Nodes,
- ?l AMetaPid=rpc:call(ANode,erlang,whereis,[inviso_rt_meta]),
- ?l rpc:call(ANode,erlang,exit,[AMetaPid,kill]),
- ?l {ok,NodeResults2}=inviso:tpm(Nodes,math,pi,0,[],void),
- ?l {value,{ANode,{error,_}}}=lists:keysearch(ANode,1,NodeResults2),
-
- ?l stop_tracing(Nodes),
- ?l stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Test cases for SUSPEND
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: In this test case a trace with ti is started. Trace flags are set,
-%% trace patterns are set and meta trace patterns. We then check that the trace
-%% flags and the meta patterns are removed when tracing suspended.
-%% The suspension is cancelled and we check that it is possible to reactivate
-%% tracing by setting the process flags and meta patterns again.
-suspend_dist_trace_ti_1(suite) -> [];
-suspend_dist_trace_ti_1(doc) ->
- [""];
-suspend_dist_trace_ti_1(Config) when is_list(Config) ->
- ?l RemoteNodes=get_remotenodes_config(Config),
- ?l Nodes=[node()|RemoteNodes],
- ?l PrivDir=filename:join(?config(priv_dir,Config),""),
- ?l TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)++".ti"])}}]}
- end,
- ?l TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- %% Set some trace flags on some newly started test procs.
- activate_traceflags(Nodes),
-
- %% Now suspend the tracing on all nodes. That shall result in the removal
- %% of trace flags and meta trace patterns, but not local trace patterns.
- ?l {ok,NodeResults1}=inviso:suspend(Nodes,test),
- ?l true=check_noderesults(Nodes,ok,NodeResults1),
- %% Trace flags gone?
- ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_test_proc]) end,Nodes),
- ?l lists:foreach(fun(P)->
- {flags,[]}=
- rpc:call(node(P),erlang,trace_info,[P,flags])
- end,
- TestProcs),
- %% Meta patterns shall be gone too, but local functions still there.
- ?l lists:foreach(fun(N)->
- {meta,false}=
- rpc:call(N,
- erlang,
- trace_info,
- [{math,module_info,1},meta]),
- {traced,local}=
- rpc:call(N,
- erlang,
- trace_info,
- [{code,which,1},traced])
- end,
- Nodes),
-
- %% Try to activate trace flags, trace patterns and meta tracing while
- %% suspended. Should not succeed of course!
- ?l ThisNode=node(),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:tf([ThisNode],inviso_test_proc,[call]),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:tpl([ThisNode],math,module_info,1,[]),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:init_tpm([ThisNode],
- math,
- module_info,
- 1,
- {?MODULE,tpm_init_func2}, % Does not exist on purpose.
- {?MODULE,tpm_call_func2}, % Does not exist on purpose.
- {?MODULE,tpm_return_func2}, % Does not exist on purpose.
- {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
-
- %% Now we want to cancel suspension and see that we can reactivate tracing.
- ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
-
- ?l {ok,NodeResults3}=
- inviso:init_tpm(math,
- module_info,
- 1,
- {?MODULE,tpm_init_func2}, % Does not exist on purpose.
- {?MODULE,tpm_call_func2}, % Does not exist on purpose.
- {?MODULE,tpm_return_func2}, % Does not exist on purpose.
- {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l {ok,NodeResults5}=
- inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults5),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{math,module_info,1},meta_match_spec],
- {meta_match_spec,[{'_',[],[{return_trace}]}]}),
- ?l {ok,NodeResults6}=inviso:tf(Nodes,inviso_test_proc,[call]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults6),
-
- %deactivate_meta_tracing(Nodes),
- %deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
- ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3),
- stop(Nodes),
- ?l timer:sleep(200), % Give it time to terminate.
- ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
- ?l undefined=whereis(inviso_rt_meta), % Still gone.
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: In this test case a trace with ti is started. Trace flags are set,
-%% trace patterns are set and meta trace patterns. We then suspend tracing at
-%% all nodes, then stop tracing which shall be allowed. We then try to initiate
-%% tracing again which shall not be possible.
-suspend_dist_trace_ti_2(suite) -> [];
-suspend_dist_trace_ti_2(doc) ->
- [""];
-suspend_dist_trace_ti_2(Config) when is_list(Config) ->
- ?l RemoteNodes=get_remotenodes_config(Config),
- ?l Nodes=[node()|RemoteNodes],
- ?l PrivDir=filename:join(?config(priv_dir,Config),""),
- ?l TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)++".ti"])}}]}
- end,
- ?l TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- %% Set some trace flags on some newly started test procs.
- activate_traceflags(Nodes),
-
- %% Now suspend the tracing on all nodes. That shall result in the removal
- %% of trace flags and meta trace patterns, but not local trace patterns.
- ?l {ok,NodeResults1}=inviso:suspend(Nodes,test),
- ?l true=check_noderesults(Nodes,ok,NodeResults1),
-
- %% Now stop tracing.
- ?l {ok,NodeResults3}=inviso:stop_tracing(Nodes),
- ?l true=check_noderesults(Nodes,{ok,idle},NodeResults3),
- %% Now try to initiate tracing again.
- ThisNode=node(),
- ?l {ok,[{ThisNode,{error,suspended}}]}=
- inviso:init_tracing([ThisNode],
- [{trace,{file,filename:join([PrivDir,"tf_suspend3_"++
- atom_to_list(ThisNode)])}},
- {ti,{file,{filename:join([PrivDir,"tf_suspend3_"++
- atom_to_list(ThisNode)])}}}]),
-
- %% Cancel the suspension and initiate tracing again.
- ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l TracerDataFun2=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)++".ti"])}}]}
- end,
- ?l TracerDataList2=lists:map(TracerDataFun2,Nodes),
- ?l {ok,NodeResults4}=inviso:init_tracing(TracerDataList2),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok},{ti_log,ok}]},NodeResults4),
- stop_tracing(Nodes),
- ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
- stop(Nodes),
- ?l timer:sleep(200), % Give it time to terminate.
- ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
- ok.
-%% -----------------------------------------------------------------------------
-
-
-
-%% TEST CASE: This test case tests that the clean function removes (prosumed)
-%% expired data from the internal public-loopdata structure in the inviso_rt_meta
-%% process.
-meta_cleanfunc_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- %% Now initialize meta tracing, but the call_func is a bit "fixed".
- ?l {ok,NodeResults1}=
- inviso:tpm(Nodes,math,module_info,1,[],
- {?MODULE,meta_cleanfunc_initfunc_1},
- {?MODULE,meta_cleanfunc_callfunc_1},
- void,void),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults1),
- %% Nothing in the "our" part of the public loop data.
- ?l true=check_on_nodes(Nodes,
- inviso_rt_meta,get_state,[inviso_rt_meta],
- fun({ok,_LD,{{_,[]},_}})->true end),
- ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[exports]) end,Nodes),
- %% Check that it has been added to the public loopdata structure.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})->
- true;
- (_)->false
- end,
- 20],
- ok),
- %% While we wait for 60 seconds to pass, we test a few other things.
- ?l {ok,NodeResults2}=
- inviso:tpm(Nodes,?MODULE,slowfunction2,0,[{'_',[],[{return_trace}]}],
- {?MODULE,meta_cleanfunc_initfunc_2},
- {?MODULE,meta_cleanfunc_callfunc_2},
- {?MODULE,meta_cleanfunc_returnfunc_2},
- void),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults2),
- ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,slowfunction,[]) end,Nodes),
- %% Believe it or not but slowfunction is still running, in its own process,
- %% we are therefore free now to examine the meta tracer.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{[],Tuples},_}})->
- {value,_}=
- lists:keysearch(meta_cleanfunc_test2,
- 1,
- Tuples),
- {value,_}=
- lists:keysearch(meta_cleanfunc_test1,
- 1,
- Tuples),
- true;
- (_)->
- false
- end,
- 20],
- ok),
- %% Now we wait for slowfunction to return and that the meta_cleanfunc_test2
- %% to be removed from public loopdata strucuture.
- ?l timer:sleep(10000),
- %% The only thing remaining should be the meta_cleanfunc_test1 which will not
- %% go away for less than that the clean functionality removes it.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})->
- true;
- (_)->
- false
- end,
- 20],
- ok),
- %% Wait for the clean function to clean meta_cleanfunc_test1 away.
- ?l timer:sleep(51000), % Shall be gone after 5 seconds.
- ?l true=check_on_nodes(Nodes,
- ?MODULE,poll,[inviso_rt_meta,
- get_state,
- [inviso_rt_meta],
- fun({ok,_LD,{{_,[]},_}})->true;
- (_)->false
- end,
- 20],
- ok),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-
-%% This function acts as tpm initialization function when we are going to test
-%% that the clean function works. Note that we here assume standard public loop
-%% datastructure.
-meta_cleanfunc_initfunc_1(_M,_F,_Arity,{E1,_E2}) ->
- {ok,{E1,[]},void}.
-%% Function that is supposed to be called when the meta traced function is
-%% called.
-meta_cleanfunc_callfunc_1(_Pid,_Args,{{E1,E2},Global}) ->
- {ok,{{E1,[{meta_cleanfunc_test1,now()}|E2]},Global},void}.
-
-meta_cleanfunc_initfunc_2(_M,_F,_Arity,PublLD) ->
- {ok,PublLD,void}.
-meta_cleanfunc_callfunc_2(_Pid,_Args,{{E1,E2},Global}) ->
- {ok,{{E1,[{meta_cleanfunc_test2,now()}|E2]},Global},void}.
-meta_cleanfunc_returnfunc_2(_Pid,_,{{E1,E2},Global}) ->
- {value,_}=lists:keysearch(meta_cleanfunc_test2,1,E2),
- {ok,{{E1,lists:keydelete(meta_cleanfunc_test2,1,E2)},Global},void}.
-
-slowfunction() ->
- spawn(?MODULE,slowfunction1,[]).
-slowfunction1() ->
- slowfunction2(). % Meta trace on this function call.
-slowfunction2() ->
- timer:sleep(2000),
- true.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Testing that a runtime component can be started instructing it
-%% to use a handler fun. Checks that the handler fun is called if a trace
-%% message comes in.
-basic_handlerfun_dist_1(suite) -> [];
-basic_handlerfun_dist_1(doc) ->
- [""];
-basic_handlerfun_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l lists:foreach(fun(N)->rpc:call(N,ets,insert,[inviso_sideeffect_tab,{bhf1,0}]) end,
- Nodes),
- TracerDataFun=
- fun(N)->{N,{fun basic_handlerfun_dist_1_fun/2,inviso_sideeffect_tab}} end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_traceflags(Nodes),
- ?l lists:foreach(fun(N)->[{bhf1,0}]=
- rpc:call(N,ets,lookup,[inviso_sideeffect_tab,bhf1])
- end,
- Nodes),
- ?l inviso_test_proc ! {apply,code,which,[lists]},
- ok=poll(ets,lookup,[inviso_sideeffect_tab,bhf1],[{bhf1,1}],20),
- deactivate_traceflags(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- timer:sleep(100),
- ?l [{bhf1,1}]=ets:lookup(inviso_sideeffect_tab,bhf1),
- stop(Nodes),
- ok.
-
-%% Function used as handler fun for testcase above.
-basic_handlerfun_dist_1_fun(_Msg,TId) ->
- ets:update_counter(TId,bhf1,1),
- TId.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Here we test that delete_log removes the files at the involved
-%% runtime nodes. In this case we test that we remove logs according to last
-%% used tracer data.
-delete_log_dist_1(suite) -> [];
-delete_log_dist_1(doc) -> [""];
-delete_log_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- ?l Files=lists:map(fun({N,TD})->
- ?l {value,{_,{_,TraceFile}}}=lists:keysearch(trace,1,TD),
- ?l {value,{_,{_,TiFile}}}=lists:keysearch(ti,1,TD),
- ?l {N,{TraceFile,TiFile}}
- end,
- TracerDataList),
- io:format("The Files is:~w~n",[Files]),
- ?l {ok,NodeResults1}=inviso:delete_log(Nodes), % Should not work!
- ?l true=check_noderesults(Nodes,{error,tracing},NodeResults1),
- stop_tracing(Nodes),
- %% Files still here.
- ?l lists:foreach(fun({N,{F1,F2}})->
- ?l {ok,_}=rpc:call(N,file,read_file_info,[F1]),
- ?l {ok,_}=rpc:call(N,file,read_file_info,[F2])
- end,
- Files),
- ?l {ok,NodeResults2}=inviso:delete_log(Nodes),
- ?l true=check_noderesults(Nodes,
- fun({_N,{ok,LogInfos}})->
- ?l {value,{_,[{ok,_FName1}]}}=
- lists:keysearch(trace_log,1,LogInfos),
- ?l {value,{_,[{ok,_FName2}]}}=
- lists:keysearch(ti_log,1,LogInfos),
- true
- end,
- NodeResults2),
- %% The files shall be gone now.
- ?l lists:foreach(fun({N,{F1,F2}})->
- ?l {error,enoent}=rpc:call(N,file,read_file_info,[F1]),
- ?l {error,enoent}=rpc:call(N,file,read_file_info,[F2])
- end,
- Files),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: Test of the autostart behaviour of the runtime component.
-%% Here we test that a runtime component is started according to the autostart.conf
-%% file. Note that the repeat parameter is set to 2.
-autostart_dist_1(suite) -> [];
-autostart_dist_1(doc) ->
- [""];
-autostart_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- AutoConfFile=filename:join(PrivDir,"autostart1.conf"),
- [RNode|_]=RemoteNodes,
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
- inviso_autostart_conf,
- AutoConfFile]),
- ?l {ok,FD}=file:open(AutoConfFile,[write]),
- ?l ok=io:format(FD,"~w.~n~w.~n",[{repeat,2},{tag,c_ref}]),
- ?l file:close(FD),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- timer:sleep(1000),
- ?l P1=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ?l true=is_pid(P1),
- ?l rpc:call(RNode,erlang,exit,[P1,kill]),
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- timer:sleep(1000),
- ?l P2=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ?l true=is_pid(P2),
- ?l rpc:call(RNode,erlang,exit,[P2,kill]),
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- timer:sleep(1000),
- ?l undefined=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of autostart. Here we focus on that an autostarted
-%% runtime component actually follows the trace case command file and
-%% initiates tracing.
-autostart_dist_2(suite) -> [];
-autostart_dist_2(doc) ->
- [""];
-autostart_dist_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- AutoConfFile=filename:join(PrivDir,"autostart2.conf"),
- [RNode|_]=RemoteNodes,
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
- inviso_autostart_conf,
- AutoConfFile]),
- ?l CmdFileName=filename:join(PrivDir,"autostart_cmd_as1"),
- ?l {ok,FD}=file:open(CmdFileName,[write]),
- ?l ok=io:format(FD,
- "inviso:tpl(Nodes,M,F,Arity,[]).~n"
- "inviso:tf(Nodes,inviso_test_proc,[call]).~n",
- []),
- ?l file:close(FD),
- ?l TraceFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)]),
- ?l TiFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)++".ti"]),
- ?l inviso_as_lib:setup_autostart(RNode,
- 2,
- [],
- [{trace,{file,TraceFileName}},
- {ti,{file,TiFileName}}],
- [[CmdFileName]],
- [{'M',code},{'F',which},{'Arity',1}],
- [{{inviso,tpl,5},{inviso_rt,tpl,{erlang,tl}}},
- {{inviso,tf,3},{inviso_rt,tf,{erlang,tl}}}]),
- ?l TestP=spawn(RNode,?MODULE,test_proc_init,[]),
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- ?l timer:sleep(1000),
- ?l {ok,_}=file:read_file_info(TraceFileName),
- ?l {ok,_}=file:read_file_info(TiFileName),
- ?l true=is_pid(P=rpc:call(RNode,erlang,whereis,[inviso_rt])),
- ?l ok=poll(rpc,call,[RNode,erlang,trace_info,[{code,which,1},traced]],{traced,local},10),
- ?l {flags,[call]}=rpc:call(RNode,erlang,trace_info,[TestP,flags]),
- ?l rpc:call(RNode,erlang,exit,[P,kill]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Here we test that an autostarted runtime component with a dependency
-%% to a specific control component tries to connect to that control component
-%% during its start-up.
-autostart_dist_3(suite) -> [];
-autostart_dist_3(doc) ->
- [""];
-autostart_dist_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- PrivDir=filename:join(?config(priv_dir,Config),""),
- AutoConfFile=filename:join(PrivDir,"autostart3.conf"),
- [RNode|_]=RemoteNodes,
- ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
- ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
- inviso_autostart_conf,
- AutoConfFile]),
- ?l {ok,FD}=file:open(AutoConfFile,[write]),
- ?l ok=io:format(FD,"~w.~n~w.~n~w.~n",
- [{options,[{dependency,{infinity,node()}}]},{repeat,2},{tag,c_ref}]),
- ?l file:close(FD),
- %% Now start inviso at this node here for the runtime to connect.
- ?l {ok,_Pid}=inviso:start(),
- ?l ok=poll(erlang,whereis,[inviso_c],fun(P) when is_pid(P)->true;(_)->false end,10),
- %% Make the runtime component start.
- ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
- ?l ok=poll(rpc,call,[RNode,erlang,whereis,[inviso_rt]],
- fun(P) when is_pid(P)->true;(_)->false end,10),
- %% Check that the runtime component started.
- ?l ok=poll(inviso,get_status,[[RNode]],{ok,[{RNode,{ok,{new,running}}}]},20),
-% ?l {ok,[{RNode,{ok,{new,running}}}]}=inviso:get_status([RNode]),
- stop([RNode]),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Default behaviour is dependency=infinity, i.e the runtime components remains.
-%% We also test here that we can reconnect to the runtime.
-running_alone_dist_1(suite) -> [];
-running_alone_dist_1(doc) ->
- [""];
-running_alone_dist_1(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(3000), % How long shall we wait? :-)
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l {ok,_Pid2}=inviso:start(),
- ?l {ok,NodeResults2}=inviso:add_nodes(Nodes,b_ref,[]),
- ?l true=check_noderesults(Nodes,{ok,{adopted,new,running,a_ref}},NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates after the specified 5000 ms.
-running_alone_dist_2(suite) -> [];
-running_alone_dist_2(doc) ->
- [""];
-running_alone_dist_2(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,5000}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(2000),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- timer:sleep(4000), % Now they shall be dead!
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates after the specified 5000 ms.
-running_alone_dist_3(suite) -> [];
-running_alone_dist_3(doc) ->
- [""];
-running_alone_dist_3(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,1000}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:change_options(Nodes,[{dependency,5000}]),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(3000),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- timer:sleep(3000), % Now they shall be dead!
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates after the specified 5000 ms,
-%% like we did in running_alone_dist_2. But now we also start tracing and checks
-%% that all inviso processes actually disappears when the time-out is reached.
-running_alone_dist_4(suite) -> [];
-running_alone_dist_4(doc) ->
- [""];
-running_alone_dist_4(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- %% Start some tracing!
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra4"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_ra4_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,
- [{dependency,5000}],
- TracerDataList,
- {ok,[{trace_log,ok},{ti_log,ok}]}),
-
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
- Nodes),
- %% Stop control component and wait for the runtimes to terminate after
- %% running alone timer has expired.
- ?l shutdown=inviso:stop(), % Stop the control component!
- ?l undefined=whereis(inviso_c),
- timer:sleep(2000),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
- Nodes),
- timer:sleep(4000), % Now they shall be dead!
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the dependency mechanism in the runtime component.
-%% Test that the runtime components terminates imeediately when the control
-%% component is stopped. Check that all processes are gone.
-running_alone_dist_5(suite) -> [];
-running_alone_dist_5(doc) ->
- [""];
-running_alone_dist_5(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- %% Start some tracing!
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataFun=
- fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra5"++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,"tf_ra5_"++atom_to_list(N)++".ti"])}}]}
- end,
- TracerDataList=lists:map(TracerDataFun,Nodes),
- start_and_init_tracing2(Nodes,
- [{dependency,0}],
- TracerDataList,
- {ok,[{trace_log,ok},{ti_log,ok}]}),
-
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
- Nodes),
- ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
- Nodes),
- %% Stop control component and check that all runtime component processes have
- %% terminate more or less immediately afterwards, since dependency==0.
- ?l shutdown=inviso:stop(), % Stop the control component!
- timer:sleep(100),
- ?l undefined=whereis(inviso_c),
- timer:sleep(500),
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
- Nodes),
- ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,
- Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the overload protection mechanism. The mechanism checks
-%% for overload using the callback approximately at the interval specified.
-%% Check that it does not start protection until start of tracing.
-overload_dist_1(suite) -> [];
-overload_dist_1(doc) ->
- [""];
-overload_dist_1(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl1,0}]) end,
- Nodes), % Initiate the counter.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload1},500}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- timer:sleep(1000), % Give the loadcheck time to perform.
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl1), % Nothing should have happened.
-
- %% Overload check shall not start until we start tracing.
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl1."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- timer:sleep(1500), % Give the loadcheck time to perform.
- ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl1),
- ?l true=(N>=2), % After 1,5 seconds, at least 2 checks.
-
- %% Now change options and remove overload checking!
- ?l {ok,NodeResults3}=inviso:change_options(Nodes,[overload]),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1),
- timer:sleep(1000),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1), % No more loadchecks!
-
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the overload protection mechanism. In this case we focus
-%% in that the init and remove functions are carried out at change_options and
-%% when starting and stoping the runtime component.
-overload_dist_2(suite) -> [];
-overload_dist_2(doc) ->
- [""];
-overload_dist_2(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload2},
- 500,
- {?MODULE,overload2i,[]},
- {?MODULE,overload2r,[]}}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl2),
-
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl2."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- timer:sleep(1500), % Give the loadcheck time to perform.
- ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl2),
- io:format("� is:~p~n",[N]),
- ?l true=(N>=2), % After 1,5 seconds, at least 2 checks.
- ?l {ok,NodeResults3}=inviso:change_options(Nodes,[{overload,{{?MODULE,overload3},
- 500,
- {?MODULE,overload3i,[]},
- {?MODULE,overload3r,[]}}}]),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l []=ets:lookup(inviso_sideeffect_tab,ovl2),
- timer:sleep(1500),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl3),
- ?l true=(N2>=2), % After 1,5 seconds, at least 2 checks.
- stop_tracing(Nodes),
- ?l []=ets:lookup(inviso_sideeffect_tab,ovl3r), % Remove function shall not be called.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3),
- timer:sleep(1000), % Check that overloadchecking has stopped.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3),
- stop(Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl3r],[{ovl3r,done}],20),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Test of the overload protections mechanism. Here we focus on testing
-%% that if overload is reached tracing is really suspended.
-overload_dist_3(suite) -> [];
-overload_dist_3(doc) ->
- [""];
-overload_dist_3(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir,
- "tf_ovl3."++atom_to_list(N)])}},
- {ti,{file,filename:join([PrivDir,
- "tf_ovl3_ti."++atom_to_list(N)])}}]}
- end,
- Nodes),
- ?l lists:foreach(fun(N)->
- true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl4,0}])
- end,
- Nodes),
- start_and_init_tracing2(Nodes,
- [{overload,{{?MODULE,overload4},500}}],
- TracerDataList,
- {ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- activate_traceflags(Nodes),
- timer:sleep(600),
- ?l [{_,N1}]=ets:lookup(inviso_sideeffect_tab,ovl4),
- ?l true=(N1>=1), % Overload check has been done!
- ?l Node=node(),
- ?l {ok,[{Node,{ok,{tracing,running}}}]}=inviso:get_status([node()]),
- ?l true=ets:insert(inviso_sideeffect_tab,{ovl4_suspend,true}),
- timer:sleep(600),
- ?l {ok,[{Node,{ok,{tracing,{suspended,test}}}}]}=inviso:get_status([node()]),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4),
- ?l {flags,[]}=erlang:trace_info(whereis(inviso_test_proc),flags),
- ?l {meta,false}=erlang:trace_info({lists,module_info,0},meta),
- ?l {traced,local}=erlang:trace_info({code,which,1},traced),
- ?l true=(is_pid(whereis(inviso_rt_meta))),
- ?l true=ets:delete(inviso_sideeffect_tab,ovl4_suspend),
- timer:sleep(600),
- ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4), % No checking while suspended!
- ?l {ok,[{Node,ok}]}=inviso:cancel_suspension([node()]),
- ?l {ok,NodeResults1}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{tracing,running}},NodeResults1),
- timer:sleep(600),
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl4),
- ?l true=(N3>N2),
- ?l deactivate_local_tracing(Nodes),
- ?l stop_tracing(Nodes),
- ?l stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE. Test that the overload mechanism is triggered by to the runtime
-%% component incomming messages, and nothing else.
-overload_dist_4(suite) -> [];
-overload_dist_4(doc) ->
- [""];
-overload_dist_4(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload5},
- infinity,
- {?MODULE,overload5i,[]},
- {?MODULE,overload5r,[]}}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl5),
-
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl4."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- timer:sleep(2000), % Give the loadcheck time to perform.
- ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- ?l true=(N==0), % And nothing shall have happend!
- %% Now we send a message to the inviso_rt, then the load check function
- %% shall be called.
- ?l whereis(inviso_rt) ! test_of_loadcheck,
- timer:sleep(200), % Make sure the inviso_rt gets scheduled.
- ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- stop_tracing(Nodes),
- ?l []=ets:lookup(inviso_sideeffect_tab,ovl5r), % Remove function shall not be called.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- ?l whereis(inviso_rt) ! test_of_loadcheck,
- timer:sleep(1000), % Check that overloadchecking has stopped.
- ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5),
- stop(Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl5r],[{ovl5r,done}],20),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE. Test that the overload mechanism correctly calculates remaining time
-%% to next load check if a message comes into the runtime component "interupting"
-%% the waiting for loadcheck timeout. (Loadcheck timeout is implemented as an after
-%% in the receive).
-overload_dist_5(suite) -> [];
-overload_dist_5(doc) ->
- [""];
-overload_dist_5(Config) when is_list(Config) ->
- ?l {ok,_Pid1}=inviso:start(), % Start a control component.
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl6,0}]) end,
- Nodes), % Initiate the counter.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
- a_ref,
- [{overload,{{?MODULE,overload6},1000}}]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- %% Overload check shall not start until we start tracing.
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,
- {file,filename:join([PrivDir,
- "tf_ovl5."++atom_to_list(N)
- ])}}]}
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl6],[{ovl6,2}],25),
- %% Now we know that exactly 2 checks have been made. Try to Distract the runtime :-)
- ?l inviso_rt:state(whereis(inviso_rt)), % Make it have to receive a message.
- timer:sleep(500),
- ?l [{_,2}]=ets:lookup(inviso_sideeffect_tab,ovl6), % Should still be 2.
- timer:sleep(600),
- ?l [{_,3}]=ets:lookup(inviso_sideeffect_tab,ovl6), % We expect yet one check.
- timer:sleep(1100),
- ?l [{_,4}]=ets:lookup(inviso_sideeffect_tab,ovl6),
-
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: Test of the subscription mechanism.
-subscribe_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- Pid=spawn(?MODULE,inviso_msg_collector,[]),
- CtrlPid=whereis(inviso_c),
-
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l ok=inviso:subscribe(Pid),
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
- check_msg_collector(Nodes,
- fun({inviso_event,CP,_,{connected,N,{_Tag,{idle,running}}}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 13),
- TracerDataList=lists:map(fun(N)->{N,{file,
- filename:join([PrivDir,
- "tf_sub1"++atom_to_list(N)])}}
- end,
- Nodes),
- ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList),
- ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults3),
- check_msg_collector(Nodes,
- fun({inviso_event,CP,_,{state_change,N,{tracing,running}}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 13),
- ?l {ok,NodeResults4}=inviso:suspend(Nodes,test),
- ?l true=check_noderesults(Nodes,ok,NodeResults4),
- check_msg_collector(Nodes,
- fun({inviso_event,CP,_,{state_change,N,{tracing,{suspended,test}}}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 13),
- ?l [RNode|_]=RemoteNodes,
- ?l RInvisoPid=rpc:call(RNode,erlang,whereis,[inviso_rt]),
- ?l rpc:call(RNode,erlang,exit,[RInvisoPid,kill]),
- check_msg_collector([RNode],
- fun({inviso_event,CP,_,{disconnected,N,_Info}})
- when CP==CtrlPid ->
- {true,N};
- (_) ->
- false
- end,
- 11),
-
- ?l {ok,_NodeResults5}=inviso:stop_tracing(Nodes),
- ?l {ok,_NodeResults6}=inviso:stop_nodes(Nodes),
- ?l shutdown=inviso:stop(),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-%% TEST CASE: fetch_log test of single straight trace_log file in distributed
-%% environment.
-fetch_log_dist_trace_1(suite) -> [];
-fetch_log_dist_trace_1(doc) ->
- ["fetch_log test of single straight trace_log file in distributed"
- "environment."];
-fetch_log_dist_trace_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir,
- "testfile1."++
- atom_to_list(N)
- ])}}]} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
-
- %% Put some output in the logs.
- ?l inviso:tp(Nodes,math,module_info,0,[]),
- ?l inviso:tf(Nodes,all,[call]),
- ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[]) end,Nodes),
-
- stop_tracing(Nodes),
- {H,M,S}=time(),
- FetchToDir=filename:join([PrivDir,
- "fetch_log_test1_"++integer_to_list(H)++"_"++
- integer_to_list(M)++"_"++integer_to_list(S)]),
- ?l ok=file:make_dir(FetchToDir),
- ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"),
- io:format("~p~n",[NodeResults]),
- ?l true=check_noderesults(RemoteNodes,
- fun({N,{complete,[{trace_log,[{ok,File}]},{ti_log,[]}]}}) ->
- ?l File="p1testfile1."++atom_to_list(N),
- true;
- (_)->
- false
- end,
- NodeResults),
- ?l ON=filename:join(PrivDir,"testfile1."),
- ?l FN=filename:join(FetchToDir,"p1testfile1."),
- ?l lists:foreach(fun(N)->
- {ok,#file_info{size=Size}}=
- file:read_file_info(ON++atom_to_list(N)),
- {ok,#file_info{size=Size}}=
- file:read_file_info(FN++atom_to_list(N))
- end,
- RemoteNodes),
- %% Now we wish to see that we get an incomplete if we try to fetch to a
- %% directory that does not exist.
- ?l FetchToErrorDir=filename:join([PrivDir,nonexistingingdir]),
- ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,FetchToErrorDir,"p1"),
- ?l io:format("NodeResults2:~w~n",[NodeResults2]),
- ?l true=check_noderesults(RemoteNodes,
- fun({_,{incomplete,_}}) ->
- true;
- (_)->
- false
- end,
- NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_trace_2(suite) -> [];
-fetch_log_dist_trace_2(doc) ->
- [""];
-fetch_log_dist_trace_2(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- {H,M,S}=time(),
- ?l Name="wrap"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S),
- ?l BaseName=filename:join(PrivDir,Name),
- Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}},
- {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]}
- end,
- ?l TracerDataList=lists:map(Fun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes),
-
- stop_tracing(Nodes),
- FetchToDir=filename:join([PrivDir,
- "fetch_log_test2_"++integer_to_list(H)++"_"++
- integer_to_list(M)++"_"++integer_to_list(S)]),
- ?l ok=file:make_dir(FetchToDir),
- ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"),
- io:format("~p~n",[NodeResults]),
- CheckFun=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
- Fun2=fun({ok,File}) ->
- match=
- re:run(File,
- "^"++"p1"++Name++atom_to_list(N),
- [{capture,none}]),
- true;
- (_) ->
- false
- end,
- ?l true=lists:all(Fun2,FileResults1),
- ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti",
- true;
- (_)->
- false
- end,
- ?l true=check_noderesults(RemoteNodes,CheckFun,NodeResults),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_trace_3(suite) -> [];
-fetch_log_dist_trace_3(doc) ->
- [""];
-fetch_log_dist_trace_3(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
-
- {H,M,S}=time(),
- ?l Name="wrap2_"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S),
- ?l BaseName=filename:join(PrivDir,Name),
- Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}},
- {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]}
- end,
- ?l TracerDataList=lists:map(Fun,Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes),
-
- stop_tracing(Nodes),
- FetchToDir=filename:join([PrivDir,
- "fetch_log_test3_"++integer_to_list(H)++"_"++
- integer_to_list(M)++"_"++integer_to_list(S)]),
- ?l ok=file:make_dir(FetchToDir),
- ?l {ok,NodeResults1}=inviso:list_logs(Nodes),
- CheckFun=fun({N,{ok,[{trace_log,PrivDir2,[F1,F2]},{ti_log,PrivDir2,[F3]}]}})->
- PrivDir2=PrivDir,
- RegExp="^"++Name++atom_to_list(N)++"[0-9]+"++"\.log",
- match=re:run(F1,RegExp,[{capture,none}]),
- match=re:run(F2,RegExp,[{capture,none}]),
- F3=Name++"_ti_"++atom_to_list(N)++".ti",
- true;
- (_) ->
- false
- end,
- ?l true=check_noderesults(Nodes,CheckFun,NodeResults1),
- ?l NodeFileSpecList=lists:map(fun({N,{ok,L}})->{N,L} end,
- lists:keydelete(node(),1,NodeResults1)),
- ?l {ok,NodeResults2}=inviso:fetch_log(NodeFileSpecList,FetchToDir,"p1"),
-io:format("~p~n",[NodeResults2]),
- CheckFun2=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
- Fun2=fun({ok,File}) ->
- match=
- re:run(File,
- "^"++"p1"++Name++atom_to_list(N),
- [{capture,none}]),
- true;
- (_) ->
- false
- end,
- ?l true=lists:all(Fun2,FileResults1),
- ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti",
- true;
- (_)->
- false
- end,
- ?l true=check_noderesults(RemoteNodes,CheckFun2,NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_error_1(suite) -> [];
-fetch_log_dist_error_1(doc) ->
- [""];
-fetch_log_dist_error_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,"foo","bar"),
-io:format("~p~n",[NodeResults2]),
- ?l true=check_noderesults(RemoteNodes,
- fun({_N,{error,no_tracerdata}})->true;
- (_)->false
- end,
- NodeResults2),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-fetch_log_dist_error_2(suite) -> [];
-fetch_log_dist_error_2(doc) ->
- [""];
-fetch_log_dist_error_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- PrivDir=filename:join(?config(priv_dir,Config),""),
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l NodeLogList=lists:map(fun(N)->{N,[{trace_log,
- PrivDir,
- ["f1,fil","f2.fil"]},
- {ti_log,
- PrivDir,
- ["f.ti"]}]}
- end,
- RemoteNodes),
- ?l {ok,NodeResults2}=inviso:fetch_log(NodeLogList,"foo","bar"),
- io:format("~p~n",[NodeResults2]),
- ?l true=check_noderesults(RemoteNodes,
- fun({_N,{incomplete,_}}) ->
- true;
- (_) ->
- false
- end,
- NodeResults2),
- ?l NodeTracerData=lists:map(fun(N)->{N,
- [{trace,{file,filename:join(PrivDir,"foo")}},
- {ti,{file,filename:join(PrivDir,"bar.ti")}}]}
- end,
- RemoteNodes),
- {ok,NodeResults3}=inviso:fetch_log(NodeTracerData,"foo","bar"),
- io:format("~p~n",[NodeResults3]),
-%% This should work this way. Now it says complete [], which is not entirely
-%% incorrect. But to follow the sematics of when fetching named files should
-%% say incomplete.
-%% Must do some rework to make that work. No real danger leaving it this way
-%% for now.
-% ?l true=check_noderesults(RemoteNodes,
-% fun({_N,{incomplete,_}}) ->
-% true;
-% (_) ->
-% false
-% end,
-% NodeResults3),
- stop(Nodes),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: This case tests that the log file merger merges files in the
-%% correct order, based on the timestamps.
-lfm_trace_dist_1(suite) -> [];
-lfm_trace_dist_1(doc) ->
- [""];
-lfm_trace_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1,RNode2|_]=RemoteNodes,
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,{file,filename:join([PrivDir,"lfm1_"++atom_to_list(N)])}} end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_traceflags(Nodes),
-
- {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- inviso_test_proc ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
- timer:sleep(300),
- inviso_test_proc ! {apply,code,which,[lists]},
-
- deactivate_traceflags(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
-
- DestFile=filename:join(PrivDir,"lfm1_out.txt"),
- ?l {ok,6}=
- inviso_lfm:merge([{node(),
- [{trace_log,
- [filename:join(PrivDir,"lfm1_"++atom_to_list(node()))]}]},
- {RNode1,
- [{trace_log,
- [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode1))]}]},
- {RNode2,
- [{trace_log,
- [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode2))]}]}],
- DestFile),
- ?l {ok,FD}=file:open(DestFile,[read]),
- ?l S1=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2),S1),
- ?l S2=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1),S2),
- ?l S3=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1),S3),
- ?l S4=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node()),S4),
- ?l S5=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2),S5),
- ?l S6=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node()),S6),
- ?l file:close(FD),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: Testing to the full extent that pid-mappings work with both
-%% local and global registration. Also checks that pidmappings can be removed
-%% and that consequently the mappings in the resulting merged file stops.
-lfm_trace_ti_dist_2(suite) -> [];
-lfm_trace_ti_dist_2(doc) ->
- [""];
-lfm_trace_ti_dist_2(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1,RNode2|_]=RemoteNodes,
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"lfm2_"++atom_to_list(N))}},
- {ti,{file,filename:join(PrivDir,"lfm2_ti_"++atom_to_list(N))}}]}
- end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
- activate_local_tracing(Nodes),
- activate_meta_tracing(Nodes),
- activate_traceflags(Nodes),
-
- {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
- timer:sleep(300),
- inviso_test_proc ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- P2=spawn(RNode2,?MODULE,test_proc_loop,[]),
- P1=spawn(RNode1,?MODULE,test_proc_loop,[]),
- P0=spawn_link(?MODULE,test_proc_loop,[]),
- ThisNode=node(),
- ?l {ok,[{ThisNode,{ok,[1]}}]}=inviso:tf([node()],P0,[call,timestamp]),
- ?l {ok,[{RNode1,{ok,[1]}}]}=inviso:tf([RNode1],P1,[call,timestamp]),
- ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P2,[call,timestamp]),
- P2 ! {apply,code,which,[lists]},
- timer:sleep(300),
- P1 ! {apply,code,which,[lists]},
- timer:sleep(300),
- P0 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- P3=spawn(RNode2,?MODULE,test_proc_loop,[]),
- ?l yes=global:register_name(inviso_test_proc_globalname,P3),
- ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P3,[call,timestamp]),
- timer:sleep(300),
- P3 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- P4=rpc:call(RNode1,erlang,whereis,[inviso_test_proc]),
- ?l true=rpc:call(RNode1,erlang,unregister,[inviso_test_proc]),
- timer:sleep(300),
- P4 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- ?l true=rpc:call(RNode1,erlang,register,[inviso_test_proc,P4]),
-
- ?l global:unregister_name(inviso_test_proc_globalname),
- timer:sleep(300),
- ?l P3 ! {apply,code,which,[lists]},
- timer:sleep(300),
-
- deactivate_traceflags(Nodes),
- deactivate_local_tracing(Nodes),
- stop_tracing(Nodes),
- stop(Nodes),
-
- DestFile=filename:join(PrivDir,"lfm2_out.txt"),
- ?l {ok,10}=
- inviso_lfm:merge([
- {node(),
- [{trace_log,
- [filename:join(PrivDir,"lfm2_"++atom_to_list(node()))]},
- {ti_log,
- [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(node()))]}]},
- {RNode1,
- [{trace_log,
- [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode1))]},
- {ti_log,
- [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode1))]}]},
- {RNode2,
- [{trace_log,
- [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode2))]},
- {ti_log,
- [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode2))]}]}
- ],
- DestFile),
- ?l {ok,FD}=file:open(DestFile,[read]),
- ?l S1=io:get_line(FD,""),
-io:format("S1 is:~p~n",[S1]),
- ?l true=lists:prefix(atom_to_list(RNode2)++" [inviso_test_proc",S1),
- ?l S2=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S2),
- ?l S3=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S3),
- ?l S4=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node())++" [inviso_test_proc",S4),
- ?l S5=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2)++" []",S5),
- ?l S6=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" []",S6),
- ?l S7=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(node())++" []",S7),
- ?l S8=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2)++" [{global,inviso_test_proc_globalname}]",S8),
- ?l S9=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode1)++" []",S9),
- ?l S10=io:get_line(FD,""),
- ?l true=lists:prefix(atom_to_list(RNode2)++" []",S10),
- ?l file:close(FD),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: This tests that the wrapset sorter works.
-handle_logfile_sort_wrapset(suite) -> [];
-handle_logfile_sort_wrapset(doc) ->
- [""];
-handle_logfile_sort_wrapset(Config) when is_list(Config) ->
- File0="prefix10.fil",
- File1="prefix11.fil",
- File2="prefix12.fil",
- File3="prefix13.fil",
- ?l [File0,File1,File2,File3]=
- inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File1,File0,File3]),
- File5="prefix15.fil",
- ?l [File5,File0,File1,File2,File3]=
- inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File5,File1,File0,File3]),
- ok.
-%% -----------------------------------------------------------------------------
-
-%% TEST CASE: This case tests that the regexp mechanism in the inviso_rt_lib can
-%% find modules using regexps and that its only_loaded mechanism works.
-%% This test case can not be run when using cover because cover will make the
-%% modules no longer loaded from the path containing "runtime_tools".
-expand_regexp_dist_1(suite) -> [];
-expand_regexp_dist_1(doc) ->
- [""];
-expand_regexp_dist_1(Config) when is_list(Config) ->
- case ?t:is_cover() of
- true ->
- {skip,"Cover is running"};
- false ->
- expand_regexp_dist_1_nocover(Config)
- end.
-
-expand_regexp_dist_1_nocover(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1|_]=RemoteNodes,
- ?l NodeResults1=inviso_rt_lib:expand_regexp(Nodes,"^inviso_rt.*",[]),
- ?l L1=length(Nodes),
- ?l L1=length(NodeResults1),
- ?l true=lists:all(fun({_,Mods})->
- ?l 3=length(Mods),
- ?l true=lists:member(inviso_rt,Mods),
- ?l true=lists:member(inviso_rt_lib,Mods),
- ?l true=lists:member(inviso_rt_meta,Mods),
- true;
- (_) ->
- false
- end,
- NodeResults1),
- %% Check the dir-option. In the following inviso_tool_lib shall not be found.
- ?l NodeResults2=inviso_rt_lib:expand_regexp(Nodes,"runtime_tools","invi.*lib.*",[]),
-?l io:format("NodeResults2:~w~n",[NodeResults2]),
- ?l L1=length(NodeResults2), % Same number of nodes replying.
- ?l true=lists:all(fun({_,Mods})->
- 2=length(Mods),
- true=lists:member(inviso_as_lib,Mods),
- true=lists:member(inviso_rt_lib,Mods),
- true;
- (_) ->
- false
- end,
- NodeResults2),
- ?l [{RNode1,[]}]=
- inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[only_loaded]),
- ?l [{RNode1,[inviso_testmodule1_foo]}]=
- inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[]),
- ok.
-%% -----------------------------------------------------------------------------
-
-
-only_loaded_dist_1(suite) -> [];
-only_loaded_dist_1(doc) ->
- [""];
-only_loaded_dist_1(Config) when is_list(Config) ->
- RemoteNodes=get_remotenodes_config(Config),
- Nodes=[node()|RemoteNodes],
- [RNode1|_]=RemoteNodes,
- PrivDir=filename:join(?config(priv_dir,Config),""),
- TracerDataList=
- lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"ol_1_"++atom_to_list(N))}}]}
- end,
- Nodes),
- start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
- ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]),
- ?l {ok,[{RNode1,{ok,[0]}}]}=
- inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[only_loaded]),
- ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]),
- ?l {ok,[{RNode1,{ok,[3]}}]}=
- inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[]),
- stop_tracing(Nodes),
- stop(Nodes),
- ok.
-
-
-%% ==============================================================================
-%% Common functions setting up inviso.
-%% ==============================================================================
-
-%% Starts controlcomponent and adds runtime components on the nodes specified.
-%% Also initiates tracing on the nodes.
-start_and_init_tracing1(Nodes,Options,TracerData,Reply) when is_list(Nodes) ->
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options),
- io:format("~p~n",[NodeResults1]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
- ?l {ok,NodeResults3}=inviso:init_tracing(Nodes,TracerData),
- ?l true=check_noderesults(Nodes,Reply,NodeResults3),
- ok.
-start_and_init_tracing2(Nodes,Options,TracerDataList,Reply) ->
- ?l {ok,_Pid}=inviso:start(), % Start a control component.
- ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options),
- io:format("~p~n",[NodeResults1]),
- ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
- ?l {ok,NodeResults4}=inviso:get_tracerdata(Nodes),
- ?l true=check_noderesults(Nodes,{ok,no_tracerdata},NodeResults4),
- ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList),
- io:format("Tracerdatalist:~p~n",[TracerDataList]),
- ?l true=check_noderesults(Nodes,Reply,NodeResults3),
-
- ?l Fun1=fun({N,{ok,TD}}) when is_list(TD)->
- ?l {value,{trace,Trace}}=lists:keysearch(trace,1,TD),
- ?l {value,{N,TD2}}=lists:keysearch(N,1,TracerDataList),
- ?l true=lists:member({trace,Trace},TD2),
- %% Check that the trace file really exists.
- ?l case Trace of % Trace={file,FilePortParameters}
- {file,FileName1} when is_list(FileName1) ->
- ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName1]);
- _ -> % This should be extended with more cases.
- true
- end,
- ?l case lists:keysearch(ti,1,TD2) of
- {value,{_,Ti}} -> % Ok, we have ti too.
- ?l {value,{_,Ti}}=lists:keysearch(ti,1,TD),
- ?l FileName2=element(2,Ti),
- ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName2]),
- true;
- false -> % No ti, we are done now.
- true
- end;
- ({N,{ok,{file,FileName}}}) ->
- ?l {value,{N,{file,FileName}}}=lists:keysearch(N,1,TracerDataList),
- ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName]),
- true;
- ({N,{ok,LogTD}}) -> % The case using a fun.
- ?l {value,{N,LogTD}}=lists:keysearch(N,1,TracerDataList),
- true
- end,
- ?l {ok,NodeResults5}=inviso:get_tracerdata(Nodes),
- ?l true=check_noderesults(Nodes,Fun1,NodeResults5),
- ok.
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing on Nodes.
-stop_tracing(Nodes) when is_list(Nodes) ->
- ?l {ok,NodeResults1}=inviso:stop_tracing(Nodes),
- ?l true=check_noderesults(Nodes,{ok,idle},NodeResults1),
- ?l {ok,NodeResults2}=inviso:get_status(Nodes),
- ?l true=check_noderesults(Nodes,{ok,{idle,running}},NodeResults2),
- %% The implementation says that the meta tracer shall be stopped when
- %% tracing is stopped. Check that.
- ?l lists:foreach(fun(N)->
- ok=poll(erlang,whereis,[inviso_rt_meta],undefined,20)
- end,
- Nodes).
-%% ------------------------------------------------------------------------------
-
-%% Stops the runtime components on Nodes and stops the control component at this
-%% Erlang node.
-stop(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(P) when is_pid(P)->true end),
- ?l {ok,NodeResults}=inviso:stop_nodes(Nodes),
- ?l true=check_noderesults(Nodes,ok,NodeResults),
- ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(undefined)->true end),
- ?l true=is_pid(whereis(inviso_c)),
- ?l shutdown=inviso:stop(),
- ?l ok=poll(erlang,whereis,[inviso_c],undefined,20).
-%% ------------------------------------------------------------------------------
-
-%% Help function activating local tracing.
-activate_local_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,false}),
- ?l {ok,NodeResults}=inviso:tpl(Nodes,code,which,1,[]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,local}).
-%% ------------------------------------------------------------------------------
-
-%% Help function activating global tracing.
-activate_global_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,false}),
- ?l {ok,NodeResults}=inviso:tp(Nodes,code,get_path,0,[]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,global}).
-%% ------------------------------------------------------------------------------
-
-
-%% Help function activating local tracing and using a regexp to point out modules.
-%% Returns the structure of modules and functions that were activated. Must be used
-%% when deactivating.
-activate_global_tracing_regexp(Nodes) when is_list(Nodes) ->
- %% First find out which modules will be effected.
- ?l Mods1=inviso_rt_lib:expand_regexp("application.*",[]),
- ?l true=(length(Mods1)>1), % Should find more than one module!
- ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1),
- %% Check that these functions are not traced.
- io:format("Modules:~w~n",[Mods1]),
- ?l {ok,NodeResults}=inviso:tp(Nodes,"application.*",'_','_',[],[]),
- io:format("Here 2~w~n",[NodeResults]),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- io:format("Here 3~n",[]),
- %% Check again!
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- Funcs1.
-%% ------------------------------------------------------------------------------
-
-%% Help function as above but uses the dir feature as well.
-activate_global_tracing_regexp_dir(Nodes) when is_list(Nodes) ->
- %% First find out which modules will be effected.
- ?l Mods1=inviso_rt_lib:expand_regexp(".*kernel.*","application.*",[]),
- ?l true=(length(Mods1)>1), % Should find more than one module!
- ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1),
- %% Check that these functions are not traced.
- io:format("Modules:~w~n",[Mods1]),
- ?l {ok,NodeResults}=inviso:tp(Nodes,{".*kernel.*","application.*"},'_','_',[],[]),
- io:format("Here 2~w~n",[NodeResults]),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- io:format("Here 3~n",[]),
- %% Check again!
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- Funcs1.
-%% ------------------------------------------------------------------------------
-
-deactivate_local_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,local}),
- ?l {ok,NodeResults}=inviso:ctpl(Nodes,code,'_','_'),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,which,1},traced],
- {traced,false}).
-%% ------------------------------------------------------------------------------
-
-deactivate_global_tracing(Nodes) when is_list(Nodes) ->
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,global}),
- ?l {ok,NodeResults}=inviso:ctp(Nodes,code,'_','_'),
- ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults),
- ?l true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{code,get_path,0},traced],
- {traced,false}).
-%% ------------------------------------------------------------------------------
-
-
-%% Function deactivating the functions activated by activate_global_tracing_regexp/1.
-deactivate_global_tracing_regexp(Nodes,Funcs1) ->
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- ?l {ok,NodeResults}=inviso:ctp(Nodes,"application.*",'_','_'),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- io:format("Noderesult from deactivate;~w~n",[NodeResults]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,false})
- end,
- Funcs)
- end,
- Funcs1).
-%% ------------------------------------------------------------------------------
-
-%% Function deactivating the functions activated by activate_global_tracing_regexp_dir/1.
-deactivate_global_tracing_regexp_dir(Nodes,Funcs1) ->
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,global})
- end,
- Funcs)
- end,
- Funcs1),
- ?l {ok,NodeResults}=inviso:ctp(Nodes,{".*kernel.*","application.*"},'_','_'),
- ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
- io:format("Noderesult from deactivate;~w~n",[NodeResults]),
- ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
- ?l lists:foreach(fun({M,Funcs})->
- lists:foreach(fun({F,Arity})->
- true=check_on_nodes(Nodes,
- erlang,
- trace_info,
- [{M,F,Arity},traced],
- {traced,false})
- end,
- Funcs)
- end,
- Funcs1).
-%% ------------------------------------------------------------------------------
-
-%% Help function which starts the inviso_test_proc on all nodes and then sets
-%% the call flag on that process.
-activate_traceflags(Nodes) ->
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags])
- end,
- Nodes),
- ?l {ok,NodeResults}=inviso:tf(Nodes,inviso_test_proc,[call,timestamp]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults),
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]),
- true=lists:member(call,Flags),
- true=lists:member(timestamp,Flags)
- end,
- Nodes),
- %% Now try a globally registered process.
- ?l [ANode|_]=Nodes,
- ?l GPid=spawn(ANode,?MODULE,global_test_proc_init,[]),
- ?l ok=poll(global,whereis_name,[global_inviso_test_proc],
- fun(P) when is_pid(P)->true;(_)->false end,
- 10),
- ?l {ok,NodeResults2}=
- inviso:tf(Nodes,{global,global_inviso_test_proc},[call,timestamp]),
- ?l true=check_noderesults(Nodes,
- fun({N,{ok,[1]}}) when N==ANode->true;
- ({_,{ok,[0]}})->true;
- (_)->false
- end,
- NodeResults2),
- ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]),
- ?l 2=length(Flags2),
- ?l true=lists:member(call,Flags2),
- ?l true=lists:member(timestamp,Flags2),
- true.
-%% ------------------------------------------------------------------------------
-
-deactivate_traceflags(Nodes) ->
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]),
- true=lists:member(call,Flags),
- true=lists:member(timestamp,Flags)
- end,
- Nodes),
- ?l {ok,NodeResults}=inviso:ctf(Nodes,inviso_test_proc,[call,timestamp]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults),
- ?l lists:foreach(fun(N)->
- P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
- {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags])
- end,
- Nodes),
- ?l GPid=global:whereis_name(global_inviso_test_proc),
- ?l ANode=node(GPid),
- ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]),
- ?l 2=length(Flags2),
- ?l {ok,NodeResults2}=inviso:ctf(Nodes,{global,global_inviso_test_proc},[call,timestamp]),
- ?l true=check_noderesults(Nodes,
- fun({N,{ok,[1]}}) when N==ANode->true;
- ({_,{ok,[0]}})->true;
- (_)->false
- end,
- NodeResults2).
-%% ------------------------------------------------------------------------------
-
-
-activate_meta_tracing(Nodes) ->
- ?l {ok,NodeResults1}=inviso:tpm_localnames(),
- ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults1),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,register,2},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta])
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:tpm_globalnames(),
- ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults2),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,handle_call,3},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,delete_global_name,2},meta])
- end,
- Nodes),
-
- ?l lists:foreach(fun(N)->true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_init_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_call_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_return_func1,0}])
- end,
- Nodes),
- ?l {ok,NodeResults3}=
- inviso:init_tpm(lists,
- module_info,
- 0,
- {?MODULE,tpm_init_func1},
- {?MODULE,tpm_call_func1},
- {?MODULE,tpm_return_func1},
- {?MODULE,tpm_remove_func1}),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
- ?l {ok,NodeResults3a}=
- inviso:init_tpm(lists,
- module_info,
- 0,
- {?MODULE,tpm_init_func1},
- {?MODULE,tpm_call_func1},
- {?MODULE,tpm_return_func1},
- {?MODULE,tpm_remove_func1}),
- ?l true=check_noderesults(Nodes,{error,already_initiated},NodeResults3a),
-% %% Try more forbidden things. Wildcards not allowed in meta tracing!
-% ?l {ok,NodeResults3b}=inviso:tpm(Nodes,lists,'_',0,[{'_',[],[{return_trace}]}]),
-% io:format("The noderesults3b is:~w~n",[NodeResults3b]),
-% ?l true=check_noderesults(Nodes,{error,bad_mfa},NodeResults3b),
- ?l {ok,NodeResults3c}=inviso:tpm(Nodes,lists,module_info,0,[{'_',[],[{return_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults3c),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20),
- ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,2}],20),
-
- ?l {ok,NodeResults4}=
- inviso:init_tpm(math,
- module_info,
- 1,
- {?MODULE,tpm_init_func2}, % Does not exist on purpose.
- {?MODULE,tpm_call_func2}, % Does not exist on purpose.
- {?MODULE,tpm_return_func2}, % Does not exist on purpose.
- {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
- ?l true=check_noderesults(Nodes,ok,NodeResults4),
- ?l {ok,NodeResults5}=
- inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults5),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
-
- ?l {ok,NodeResults6}=inviso:tpm_ms(math,module_info,1,ms2,[{[exports],[],[]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults6),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{[exports],[],[]},{'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
- ?l {ok,NodeResults7}=inviso:tpm_ms(math,module_info,1,ms3,[{[attributes],[],[]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults7),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]},
- {[exports],[],[]},
- {'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
- ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms2),
- ?l true=check_noderesults(Nodes,ok,NodeResults8),
- ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]},
- {'_',[],[{return_trace}]}]}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
- ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]),
- ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms3),
- ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]),
- ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms1),
- ?l lists:foreach(fun(N)->{meta_match_spec,false}=
- rpc:call(N,erlang,trace_info,[{math,module_info,1},
- meta_match_spec])
- end,
- Nodes),
-
- %% Now try to do this with exception tracing instead.
- %% Reset the side effect tables.
- ?l lists:foreach(fun(N)->true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_init_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_call_func1,0}]),
- true=rpc:call(N,
- ets,
- insert,
- [inviso_sideeffect_tab,{tpm_return_func1,0}])
- end,
- Nodes),
- ?l {ok,NodeResults9}=
- inviso:init_tpm(?MODULE,
- failing_function,
- 1,
- {?MODULE,tpm_init_func1},
- {?MODULE,tpm_call_func1},
- {?MODULE,tpm_return_func1},
- {?MODULE,tpm_remove_func1}),
- ?l true=check_noderesults(Nodes,ok,NodeResults9),
- ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
- ?l {ok,NodeResults10}=inviso:tpm(Nodes,?MODULE,failing_function,1,[{'_',[],[{exception_trace}]}]),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults10),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{?MODULE,failing_function,1},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[nofailure]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20),
- ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[failure]) end,Nodes),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20),
- ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,3}],20),
-
- ok.
-%% ------------------------------------------------------------------------------
-
-%% This function is for testing that appending the tracer to a trace action term
-%% works.
-activate_deactivate_meta_tracing_tracer(Nodes) ->
- ?l {ok,NodeResults}=
- inviso:tpm_tracer(Nodes,lists,module_info,0,[{'_',[],[{trace,[all],[call]}]}],void),
- ?l true=check_noderesults(Nodes,{ok,1},NodeResults),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]),
- {meta_match_spec,[{'_',[],[{trace,[all],Enable}]}]}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},
- meta_match_spec]),
- true=list_search(Enable,fun({{tracer,P}}) when is_port(P)->true;
- (_) -> false
- end)
- end,
- Nodes),
- ?l {ok,NodeResults2}=
- inviso:ctpm(Nodes,lists,module_info,0),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta])
- end,
- Nodes),
- ok.
-%% ------------------------------------------------------------------------------
-
-deactivate_meta_tracing(Nodes) ->
- ?l lists:foreach(fun(N)->{meta,P}=
- rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]),
- true=is_pid(P)
- end,
- Nodes),
- ?l lists:foreach(fun(N)->{meta,P}=
- rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta]),
- true=is_pid(P)
- end,
- Nodes),
- ?l {ok,NodeResults1}=inviso:ctpm_localnames(),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]) end,
- Nodes),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta])
- end,
- Nodes),
- ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1),
-
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,handle_call,3},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
- {meta,P}=rpc:call(N,
- erlang,
- trace_info,
- [{global,delete_global_name,2},meta])
- end,
- Nodes),
- ?l {ok,NodeResults1b}=inviso:ctpm_globalnames(),
- ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1b),
- ?l lists:foreach(fun(N)->
- {meta,false}=rpc:call(N,
- erlang,
- trace_info,
- [{global,handle_call,3},meta])
- end,
- Nodes),
- ?l lists:foreach(fun(N)->
- {meta,false}=rpc:call(N,
- erlang,
- trace_info,
- [{global,delete_global_name,2},meta])
- end,
- Nodes),
-
- ?l lists:foreach(fun(N)->{meta,P}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]),
- true=is_pid(P)
- end,
- Nodes),
- ?l {ok,NodeResults2}=inviso:ctpm(lists,module_info,0),
- ?l true=check_noderesults(Nodes,ok,NodeResults2),
- ?l lists:foreach(fun(N)->{meta,false}=
- rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]) end,
- Nodes),
- ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
- ?l {ok,NodeResults3}=inviso:ctpm(math,module_info,1),
- ?l true=check_noderesults(Nodes,ok,NodeResults3),
- ok.
-%% ------------------------------------------------------------------------------
-
-%% Functions acting as callbacks for testing the meta tracing mechanisms.
-tpm_init_func1(_M,_F,_Arity,PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,1),
- {ok,PublLD,void}.
-tpm_call_func1(_Pid,{call,_Args,_TS},PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_call_func1,1),
- {ok,PublLD,void}.
-tpm_return_func1(_Pid,{return_from,_ReturnVal,_TS},PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
- {ok,PublLD,void};
-tpm_return_func1(_Pid,{exception_from,_ReturnVal,_TS},PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
- ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
- {ok,PublLD,void}.
-tpm_remove_func1(_M,_F,_Arity,PublLD) ->
- ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,-1),
- {ok,PublLD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Help function which traces on a function and makes function calls until there
-%% are two files in the wrap-set.
-fill_and_reach_two_wrapfiles(PrivDir,RegExp,Nodes) ->
- ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
- ?l {ok,NodeResults1}=inviso:tpl(Nodes,?MODULE,test_function,0,[]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults1),
- ?l {ok,NodeResults2}=inviso:tf(Nodes,inviso_test_proc,[call]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults2),
- fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Nodes),
- ?l {ok,NodeResults3}=inviso:ctf(Nodes,inviso_test_proc,[call]),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults3),
- ?l {ok,NodeResults4}=inviso:ctpl(Nodes,?MODULE,test_function,0),
- ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults4),
- ok.
-
-fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,[Node|Rest]) ->
- ?l ok=rpc:call(Node,?MODULE,fill_and_reach_two_wrapfiles_3,[PrivDir,RegExp]),
- fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Rest);
-fill_and_reach_two_wrapfiles_2(_,_,[]) ->
- ok.
-
-fill_and_reach_two_wrapfiles_3(Dir,RegExp) ->
- ok=send_to_test_proc({apply,?MODULE,test_function,[]},
- fun reach_two_wraps_stopfun/1,
- {Dir,RegExp++atom_to_list(node())},
- 100).
-
-%% Help function intended to be used as fun in a send_to_test_proc/4 call.
-%% The function lists the content of Dir and looks for occurancies of String.
-%% If two files containing the string String are found, 'done' is returned.
-%% Otherwise 'continue'.
-reach_two_wraps_stopfun({Dir,RegExp}) ->
- case file:list_dir(Dir) of
- {ok,FileNames} ->
- case how_many_files_regexp(FileNames,RegExp,0) of
- {ok,2} ->
- done;
- _ ->
- continue
- end;
- {error,_Reason} ->
- error
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help function for the overload tests. These functions are used as callbacks.
-%% ------------------------------------------------------------------------------
-
-overload1(_) ->
- ets:update_counter(inviso_sideeffect_tab,ovl1,1),
- ok.
-%% This function is used when timeout occurs inside the runtime component.
-%% That is it is time to check for overload.
-overload2({timeout,overload2i_data}) ->
- ets:update_counter(inviso_sideeffect_tab,ovl2,1),
- ok.
-overload2i() ->
- ets:insert(inviso_sideeffect_tab,{ovl2,0}),
- {ok,overload2i_data}.
-overload2r(overload2i_data) ->
- ets:delete(inviso_sideeffect_tab,ovl2).
-
-%% This function is used when timeout occurs inside the runtime component.
-%% That is it is time to check for overload.
-overload3({timeout,overload3i_data}) ->
- ets:update_counter(inviso_sideeffect_tab,ovl3,1),
- ok;
-overload3(_) -> % Must handle garbage too.
- ignore.
-overload3i() ->
- ets:insert(inviso_sideeffect_tab,{ovl3,0}),
- {ok,overload3i_data}.
-overload3r(overload3i_data) ->
- ets:insert(inviso_sideeffect_tab,{ovl3r,done}),
- ets:delete(inviso_sideeffect_tab,ovl3).
-
-overload4(_) ->
- case ets:lookup(inviso_sideeffect_tab,ovl4_suspend) of
- [] -> % We are supposed to be running.
- ets:update_counter(inviso_sideeffect_tab,ovl4,1),
- ok;
- [_] ->
- {suspend,test}
- end.
-
-%% This function is used when overload check is done by icomming message.
-overload5({msg,{test_of_loadcheck,overload5i_data}}) ->
- ets:update_counter(inviso_sideeffect_tab,ovl5,1),
- ok;
-overload5(_) ->
- ignore.
-overload5i() ->
- ets:insert(inviso_sideeffect_tab,{ovl5,0}),
- {ok,overload5i_data}.
-overload5r(overload5i_data) ->
- ets:delete(inviso_sideeffect_tab,ovl5),
- ets:insert(inviso_sideeffect_tab,{ovl5r,done});
-overload5r(X) ->
- erlang:display({'***',overload5r,X}).
-
-overload6(_) ->
- ets:update_counter(inviso_sideeffect_tab,ovl6,1),
- ok.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help function for the subscription tests. These function implements a collector
-%% process which will subscribe to inviso_events from the control component.
-%% ------------------------------------------------------------------------------
-
-%% Function which can be used to check if an inviso_event has arrived. The function
-%% takes a fun which tests the messages.
-check_msg_collector([],_,_) ->
- true;
-check_msg_collector(_,_,0) ->
- false;
-check_msg_collector(Nodes,Fun,T) ->
- Ref=make_ref(),
- inviso_collector_proc ! {fetch_message,self(),Ref,Fun},
- receive
- {inviso,Ref,{true,Node}} ->
- check_msg_collector(lists:delete(Node,Nodes),Fun,T-1);
- {inviso,Ref,false} ->
- timer:sleep(100),
- check_msg_collector(Nodes,Fun,T-1)
- end.
-
-%% Spawn on this function to get a subscriber.
-inviso_msg_collector() ->
- register(inviso_collector_proc,self()),
- inviso_msg_collector_loop([]).
-
-inviso_msg_collector_loop(Msgs) ->
- receive
- {fetch_message,From,Ref,Fun} ->
- {NewMsgs,Reply}=inviso_msg_collector_selector(Msgs,Fun,[]),
- From ! {inviso,Ref,Reply},
- inviso_msg_collector_loop(NewMsgs);
- Msg ->
- inviso_msg_collector_loop([Msg|Msgs])
- end.
-
-inviso_msg_collector_selector([M|Rest],Fun,Accum) ->
- case Fun(M) of
- {true,X} ->
- {Rest++Accum,{true,X}};
- _ ->
- inviso_msg_collector_selector(Rest,Fun,[M|Accum])
- end;
-inviso_msg_collector_selector([],_,Accum) ->
- {Accum,false}.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Help functions
-%% ==============================================================================
-
-list_search([E|Rest],Fun) ->
- case Fun(E) of
- true ->
- true;
- false ->
- list_search(Rest,Fun)
- end;
-list_search([],_Fun) ->
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function checking that there is a Result for each node in Nodes.
-%% Returns 'true' if successful.
-check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
- case Fun({Node,Result}) of
- true ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Fun,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
- _ ->
- illegal_result
- end;
-check_noderesults(Nodes,Result,[{Node,Result}|Rest]) ->
- case lists:member(Node,Nodes) of
- true ->
- check_noderesults(lists:delete(Node,Nodes),Result,Rest);
- false -> % Not good.
- unknown_node_in_returnvalue
- end;
-check_noderesults([],_,[]) ->
- true;
-check_noderesults(X,Y,Z) ->
- io:format("Bad arguments to check noderesults:~w~n~w~n~w~n",[X,Y,Z]),
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function doing rpc on all nodes in Nodes calling M:F. Returns 'true' if
-%% successful.
-check_on_nodes([Node|Rest],M,F,Args,Result) when Node==node() ->
- if
- is_function(Result) ->
- ?l true=Result(apply(M,F,Args));
- true ->
- ?l Result=apply(M,F,Args)
- end,
- check_on_nodes(Rest,M,F,Args,Result);
-check_on_nodes([Node|Rest],M,F,Args,Result) ->
- if
- is_function(Result) ->
- ?l true=Result(rpc:call(Node,M,F,Args));
- true ->
- ?l Result=rpc:call(Node,M,F,Args)
- end,
- check_on_nodes(Rest,M,F,Args,Result);
-check_on_nodes([],_,_,_,_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which given a list of files searches through it and returns
-%% how many satisfies the RegExp.
-%% Returns {ok,N}.
-how_many_files_regexp([],_,N) ->
- {ok,N};
-how_many_files_regexp([FName|Rest],RegExp,N) ->
- case re:run(FName,RegExp,[{capture,none}]) of
- match ->
- how_many_files_regexp(Rest,RegExp,N+1);
- nomatch ->
- how_many_files_regexp(Rest,RegExp,N);
- {error,Reason} ->
- test_server:fail(Reason)
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function killing a bunch of registered processes.
-process_killer([RegName|Rest]) ->
- case whereis(RegName) of
- undefined ->
- case global:whereis_name(RegName) of
- undefined ->
- process_killer(Rest);
- P when is_pid(P) ->
- if
- node()==node(P) ->
- exit(P,kill);
- true ->
- true
- end,
- process_killer(Rest)
- end;
- P when is_pid(P) ->
- exit(P,kill),
- process_killer(Rest)
- end;
-process_killer([]) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which waits for a function call to become Result. This is useful
-%% if what we are waiting for can happend independantly of indications we have
-%% access to.
-poll(_,_,_,_,0) ->
- error;
-poll(M,F,Args,Result,Times) ->
- try apply(M,F,Args) of
- What when is_function(Result) ->
- case Result(What) of
- true ->
- ok;
- _ ->
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end;
- Result ->
- ok;
- _ ->
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- catch
- error:Reason ->
- io:format("Apply in suite-function poll/5 failed, ~w~n",[Reason]),
- timer:sleep(100),
- poll(M,F,Args,Result,Times-1)
- end.
-%% ------------------------------------------------------------------------------
-
-insert_remotenode_config(Name,Node,Config) ->
- [{remotenode,{Name,Node}}|Config].
-%% ------------------------------------------------------------------------------
-
-insert_timetraphandle_config(Handle,Config) ->
- [{timetraphandle,Handle}|Config].
-%% ------------------------------------------------------------------------------
-
-get_remotenode_config(Name, [{remotenode, {Name, Node}}| _Cs]) ->
- Node;
-get_remotenode_config(Name, [_ | Cs]) ->
- get_remotenode_config(Name, Cs);
-get_remotenode_config(Name, []) ->
- exit({no_remotenode, Name}).
-
-%% ------------------------------------------------------------------------------
-
-get_timetraphandle_config(Config) ->
- {value,{_,Handle}}=lists:keysearch(timetraphandle,1,Config),
- Handle.
-%% ------------------------------------------------------------------------------
-
-get_remotenodes_config([{remotenode,{_Name,Node}}|Config]) ->
- [Node|get_remotenodes_config(Config)];
-get_remotenodes_config([_|Config]) ->
- get_remotenodes_config(Config);
-get_remotenodes_config([]) ->
- [].
-%% ------------------------------------------------------------------------------
-
-remove_remotenode_config(Name, [{remotenode, {Name, _}} | Cs]) ->
- Cs;
-remove_remotenode_config(Name, [C | Cs]) ->
- [C | remove_remotenode_config(Name, Cs)];
-remove_remotenode_config(_Name, []) ->
- [].
-
-%% ------------------------------------------------------------------------------
-
-remove_timetraphandle_config(Config) ->
- lists:keydelete(timetraphandle,1,Config).
-%% ------------------------------------------------------------------------------
-
-%% This function can be meta traced in order to check that exception_trace works.
-%% Must be exported.
-failing_function(nofailure) ->
- true;
-failing_function(failure) ->
- exit(failure).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Code for a test process which can be started.
-%% ==============================================================================
-
-test_proc_init() ->
- register(inviso_test_proc,self()),
- test_proc_loop().
-
-test_proc_loop() ->
- receive
- {apply,M,F,Args} ->
- apply(M,F,Args),
- test_proc_loop();
- X ->
- io:format("Got ~w~n",[X]),
- test_proc_loop()
- end.
-
-global_test_proc_init() ->
- global:register_name(global_inviso_test_proc,self()),
- test_proc_loop().
-%% ------------------------------------------------------------------------------
-
-send_to_test_proc(_,_,_,0) ->
- error;
-send_to_test_proc(Msg,Fun,FunArg,N) ->
- inviso_test_proc ! Msg,
- case Fun(FunArg) of
- done ->
- ok;
- error ->
- test_server:fail(send_to_test_proc);
- _ ->
- send_to_test_proc(Msg,Fun,FunArg,N-1)
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% This function is here to be traced on by the inviso_test_proc. Must be exported.
-test_function() ->
- 1+1.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Code for a test side effect table process.
-%% ==============================================================================
-
-%% The side effect logger is a process owning a public ETS table. The idea is that
-%% various callback functions can write in the table when called. In that way
-%% correct calling of the call-backs can be verified.
-start_side_effect_logger(Node) ->
- ?l true=is_pid(spawn(Node,?MODULE,side_effect_logger_proc,[])),
- ?l ok=poll(rpc,call,[Node,ets,lookup,[inviso_sideeffect_tab,foo]],[],20).
-
-%% This one must be exported.
-side_effect_logger_proc() ->
- register(inviso_tab_proc,self()), % So we can kill it later.
- ets:new(inviso_sideeffect_tab,[public,named_table]),
- side_effect_logger_proc_2().
-
-side_effect_logger_proc_2() ->
- receive
- _X -> % This process is not expecting anything!
- side_effect_logger_proc_2()
- end.
-%% ------------------------------------------------------------------------------
diff --git a/lib/runtime_tools/test/inviso_testmodule1_foo.erl b/lib/runtime_tools/test/inviso_testmodule1_foo.erl
deleted file mode 100644
index a7a22cad39..0000000000
--- a/lib/runtime_tools/test/inviso_testmodule1_foo.erl
+++ /dev/null
@@ -1,9 +0,0 @@
--module(inviso_testmodule1_foo).
-
--compile(export_all).
-
-%% The purpose of this module is simply to have a module that is
-%% guaranteed not loaded.
-
-foo() ->
- true.
diff --git a/lib/runtime_tools/test/runtime_tools_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 534c7508d8..d9c03b7e25 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.8.9
+RUNTIME_TOOLS_VSN = 1.8.10
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index 1333e375b1..f54b04c223 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,81 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 2.3.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Some updates are made to systools and release_handler for
+ handling of unicode.</p>
+ <p>
+ Own Id: OTP-10782</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SASL 2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ release_handler_SUITE:otp_9864 deleted parts of the
+ release_handler_SUITE_data directory so the test suite
+ could not be executed twice without re-installation. This
+ has been corrected.</p>
+ <p>
+ Own Id: OTP-10394 Aux Id: kunagi-187 [98] </p>
+ </item>
+ <item>
+ <p>
+ It is no longer possible to have <c>{Mod,Vsn}</c> in the
+ 'modules' list in a .app file.</p>
+ <p>
+ This was earlier possible, although never documented in
+ the .app file reference manual. It was however visible in
+ the documentation of <c>application:load/[1,2]</c>, where
+ the same term as in a .app file can be used as the first
+ argument.</p>
+ <p>
+ The possibility has been removed since the <c>Vsn</c>
+ part was never used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10417</p>
+ </item>
+ <item>
+ <p>
+ release_handler:upgrade_script and
+ release_handler:downgrade_script could not read appup
+ files with regexps. This has been corrected. (Thanks to
+ Ulf Wiger)</p>
+ <p>
+ Own Id: OTP-10463</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 2.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/sasl/src/Makefile b/lib/sasl/src/Makefile
index de0c45e6ae..ab2d3a3ecd 100644
--- a/lib/sasl/src/Makefile
+++ b/lib/sasl/src/Makefile
@@ -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
@@ -60,7 +60,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -I../../stdlib/include
+ERL_COMPILE_FLAGS += -I../../stdlib/include -Werror
# ----------------------------------------------------
@@ -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..c1b715b970 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.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
@@ -41,7 +41,8 @@
%% Internal exports, a client release_handler may call this functions.
-export([do_write_release/3, do_copy_file/2, do_copy_files/2,
do_copy_files/1, do_rename_files/1, do_remove_files/1,
- remove_file/1, do_write_file/2, do_ensure_RELEASES/1]).
+ remove_file/1, do_write_file/2, do_write_file/3,
+ do_ensure_RELEASES/1]).
-record(state, {unpurged = [],
root,
@@ -254,7 +255,7 @@ check_timeout(_Else) -> false.
new_emulator_upgrade(Vsn, Opts) ->
Result = call({install_release, Vsn, reboot, Opts}),
error_logger:info_msg(
- "~p:install_release(~p,~p) completed after node restart "
+ "~w:install_release(~p,~p) completed after node restart "
"with new emulator version~nResult: ~p~n",[?MODULE,Vsn,Opts,Result]),
Result.
@@ -494,10 +495,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} ->
@@ -1128,7 +1129,7 @@ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
{ok,[FC]} ->
FC;
{error,Error1} ->
- io:format("Warning: ~p can not read ~p: ~p~n",
+ io:format("Warning: ~w can not read ~p: ~p~n",
[?MODULE,FromFile,Error1]),
[]
end,
@@ -1138,7 +1139,7 @@ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) ->
{ok,[ToConfig]} ->
[lists:keyfind(App,1,ToConfig) || App <- [kernel,stdlib,sasl]];
{error,Error2} ->
- io:format("Warning: ~p can not read ~p: ~p~n",
+ io:format("Warning: ~w can not read ~p: ~p~n",
[?MODULE,ToFile,Error2]),
[false,false,false]
end,
@@ -1597,7 +1598,9 @@ remove_file(File) ->
end.
do_write_file(File, Str) ->
- case file:open(File, [write]) of
+ do_write_file(File, Str, []).
+do_write_file(File, Str, FileOpts) ->
+ case file:open(File, [write | FileOpts]) of
{ok, Fd} ->
io:put_chars(Fd, Str),
file:close(Fd),
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index 93d12cf609..b37ae2f944 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-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
@@ -624,7 +624,7 @@ get_proc_state(Proc) ->
maybe_supervisor_which_children(suspended, Name, Pid) ->
error_logger:error_msg("release_handler: a which_children call"
- " to ~p (~p) was avoided. This supervisor"
+ " to ~p (~w) was avoided. This supervisor"
" is suspended and should likely be upgraded"
" differently. Exiting ...~n", [Name, Pid]),
error(suspended_supervisor);
@@ -635,7 +635,7 @@ maybe_supervisor_which_children(State, Name, Pid) ->
Res;
Other ->
error_logger:error_msg("release_handler: ~p~nerror during"
- " a which_children call to ~p (~p)."
+ " a which_children call to ~p (~w)."
" [State: ~p] Exiting ... ~n",
[Other, Name, Pid, State]),
error(which_children_failed)
@@ -647,7 +647,7 @@ maybe_get_dynamic_mods(Name, Pid) ->
Res;
Other ->
error_logger:error_msg("release_handler: ~p~nerror during a"
- " get_modules call to ~p (~p),"
+ " get_modules call to ~p (~w),"
" there may be an error in it's"
" childspec. Exiting ...~n",
[Other, Name, Pid]),
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index ce4aa1f8f8..a4a38ee40a 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -1,7 +1,7 @@
%% -*- erlang -*-
%% %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
@@ -17,11 +17,13 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max two major revisions back
- [{<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04 (and later?)
- {<<"2\\.1\\.[6-9](\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13B-R14B03
+ [{<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04
+ {<<"2\\.1\\.9\\.[24](\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14B-R14B03
%% Down to - max two major revisions back
- [{<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04 (and later?)
- {<<"2\\.1\\.[6-9](\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13B-R14B03
+ [{<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"2\\.2(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.1\\.10(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14B04
+ {<<"2\\.1\\.9\\.[24](\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14B-R14B03
}.
diff --git a/lib/sasl/src/si_sasl_supp.erl b/lib/sasl/src/si_sasl_supp.erl
index 9c96d11c28..c4fc0c5f08 100644
--- a/lib/sasl/src/si_sasl_supp.erl
+++ b/lib/sasl/src/si_sasl_supp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. 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
@@ -360,7 +360,7 @@ ppi_impl(XPid) ->
print_info(Device, Pid, {Module, Func}, Opt, Data) ->
case erlang:function_exported(Module, Func, 2) of
true ->
- case catch apply({Module, Func}, [Opt, Data]) of
+ case catch apply(Module, Func, [Opt, Data]) of
Format when is_list(Format) ->
format_lib_supp:print_info(Device, 79,
add_pid_to_format(Pid, Format)),
diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl
index 1b6ea125d9..6618baa2aa 100644
--- a/lib/sasl/src/systools_lib.erl
+++ b/lib/sasl/src/systools_lib.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
@@ -42,7 +42,11 @@ file_term2binary(FileIn, FileOut) ->
%%______________________________________________________________________
%% read_term(File) -> {ok, Term} | Error
-
+%%
+%% This is really an own implementation of file:consult/1, except it
+%% returns one term and not a list of terms. Keeping the function
+%% instead of using file:consult - for backwards compatibility with
+%% error reasons.
read_term(File) ->
case file:open(File, [read]) of
{ok, Stream} ->
@@ -54,6 +58,7 @@ read_term(File) ->
end.
read_term_from_stream(Stream, File) ->
+ _ = epp:set_encoding(Stream),
R = io:request(Stream, {get_until,'',erl_scan,tokens,[1]}),
case R of
{ok,Toks,_EndLine} ->
@@ -176,11 +181,11 @@ add_dirs(RegName, Dirs, Root) ->
regexp_match(RegName, D0, Root) ->
case file:list_dir(D0) of
{ok, Files} when length(Files) > 0 ->
- case re:compile(RegName) of
+ case re:compile(RegName,[unicode]) of
{ok, MP} ->
FR = fun(F) ->
- case re:run(F, MP) of
- {match,[{0,N}]} when N == length(F) ->
+ case re:run(F, MP, [{capture,first,list}]) of
+ {match,[F]} -> % All of F matches
DirF = join(D0, F, Root),
case dir_p(DirF) of
true ->
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 61e660e918..193dbb64bf 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.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
@@ -156,10 +156,10 @@ return(ok,Warnings,Flags) ->
_ ->
case member(warnings_as_errors,Flags) of
true ->
- io:format("~s",[format_warning(Warnings, true)]),
+ io:format("~ts",[format_warning(Warnings, true)]),
error;
false ->
- io:format("~s",[format_warning(Warnings)]),
+ io:format("~ts",[format_warning(Warnings)]),
ok
end
end;
@@ -168,7 +168,7 @@ return({error,Mod,Error},_,Flags) ->
true ->
{error,Mod,Error};
_ ->
- io:format("~s",[Mod:format_error(Error)]),
+ io:format("~ts",[Mod:format_error(Error)]),
error
end.
@@ -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,
@@ -2007,17 +1970,11 @@ is_app_type(_) -> false.
% check if a term is a string.
-string_p([H|T]) when is_integer(H), H >= $ , H < 255 ->
- string_p(T);
-string_p([$\n|T]) -> string_p(T);
-string_p([$\r|T]) -> string_p(T);
-string_p([$\t|T]) -> string_p(T);
-string_p([$\v|T]) -> string_p(T);
-string_p([$\b|T]) -> string_p(T);
-string_p([$\f|T]) -> string_p(T);
-string_p([$\e|T]) -> string_p(T);
-string_p([]) -> true;
-string_p(_) -> false.
+string_p(S) ->
+ case unicode:characters_to_list(S) of
+ S -> true;
+ _ -> false
+ end.
% check if a term is a list of two tuples with the first
% element as an atom.
@@ -2026,14 +1983,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);
@@ -2248,31 +2197,31 @@ format_error({illegal_applications,Names}) ->
io_lib:format("Illegal applications in the release file: ~p~n",
[Names]);
format_error({missing_mandatory_app,Name}) ->
- io_lib:format("Mandatory application ~p must be specified in the release file~n",
+ io_lib:format("Mandatory application ~w must be specified in the release file~n",
[Name]);
format_error({mandatory_app,Name,Type}) ->
- io_lib:format("Mandatory application ~p must be of type 'permanent' in the release file. Is '~p'.~n",
+ io_lib:format("Mandatory application ~w must be of type 'permanent' in the release file. Is '~p'.~n",
[Name,Type]);
format_error({duplicate_register,Dups}) ->
- io_lib:format("Duplicated register names: ~n~s",
+ io_lib:format("Duplicated register names: ~n~ts",
[map(fun({{Reg,App1,_,_},{Reg,App2,_,_}}) ->
- io_lib:format("\t~p registered in ~p and ~p~n",
+ io_lib:format("\t~w registered in ~w and ~w~n",
[Reg,App1,App2])
end, Dups)]);
format_error({undefined_applications,Apps}) ->
io_lib:format("Undefined applications: ~p~n",[Apps]);
format_error({duplicate_modules,Dups}) ->
- io_lib:format("Duplicated modules: ~n~s",
+ io_lib:format("Duplicated modules: ~n~ts",
[map(fun({{Mod,_,App1,_,_},{Mod,_,App2,_,_}}) ->
- io_lib:format("\t~p specified in ~p and ~p~n",
+ io_lib:format("\t~w specified in ~w and ~w~n",
[Mod,App1,App2])
end, Dups)]);
format_error({included_and_used, Dups}) ->
io_lib:format("Applications both used and included: ~p~n",[Dups]);
format_error({duplicate_include, Dups}) ->
- io_lib:format("Duplicated application included: ~n~s",
+ io_lib:format("Duplicated application included: ~n~ts",
[map(fun({{Name,App1,_,_},{Name,App2,_,_}}) ->
- io_lib:format("\t~p included in ~p and ~p~n",
+ io_lib:format("\t~w included in ~w and ~w~n",
[Name,App1,App2])
end, Dups)]);
format_error({modules,ModErrs}) ->
@@ -2283,11 +2232,11 @@ format_error({not_found,File}) ->
io_lib:format("File not found: ~p~n",[File]);
format_error({parse,File,{Line,Mod,What}}) ->
Str = Mod:format_error(What),
- io_lib:format("~s:~p: ~s\n",[File, Line, Str]);
+ io_lib:format("~ts:~w: ~ts\n",[File, Line, Str]);
format_error({read,File}) ->
io_lib:format("Cannot read ~p~n",[File]);
format_error({open,File,Error}) ->
- io_lib:format("Cannot open ~p - ~s~n",
+ io_lib:format("Cannot open ~p - ~ts~n",
[File,file:format_error(Error)]);
format_error({tar_error,What}) ->
form_tar_err(What);
@@ -2303,24 +2252,21 @@ format_errors(ListOfErrors) ->
form_err({bad_application_name,{Name,Found}}) ->
io_lib:format("~p: Mismatched application id: ~p~n",[Name,Found]);
form_err({error_reading, {Name, What}}) ->
- io_lib:format("~p: ~s~n",[Name,form_reading(What)]);
+ io_lib:format("~p: ~ts~n",[Name,form_reading(What)]);
form_err({module_not_found,App,Mod}) ->
- io_lib:format("~p: Module (~p) not found~n",[App,Mod]);
-form_err({{vsn_diff,File},{Mod,Vsn,App,_,_}}) ->
- io_lib:format("~p: Module (~p) version (~p) differs in file ~p~n",
- [App,Mod,Vsn,File]);
+ io_lib:format("~w: Module (~w) not found~n",[App,Mod]);
form_err({error_add_appl, {Name, {tar_error, What}}}) ->
- io_lib:format("~p: ~s~n",[Name,form_tar_err(What)]);
+ io_lib:format("~p: ~ts~n",[Name,form_tar_err(What)]);
form_err(E) ->
io_lib:format("~p~n",[E]).
form_reading({not_found,File}) ->
io_lib:format("File not found: ~p~n",[File]);
form_reading({application_vsn, {Name,Vsn}}) ->
- io_lib:format("Application ~s with version ~p not found~n",[Name, Vsn]);
+ io_lib:format("Application ~ts with version ~p not found~n",[Name, Vsn]);
form_reading({parse,File,{Line,Mod,What}}) ->
Str = Mod:format_error(What),
- io_lib:format("~s:~p: ~s\n",[File, Line, Str]);
+ io_lib:format("~ts:~w: ~ts\n",[File, Line, Str]);
form_reading({read,File}) ->
io_lib:format("Cannot read ~p~n",[File]);
form_reading({{bad_param, P},_}) ->
@@ -2336,15 +2282,15 @@ form_reading({no_valid_version, {{_, SVsn}, {_, File, FVsn}}}) ->
io_lib:format("No valid version (~p) of .app file found. Found file ~p with version ~p~n",
[SVsn, File, FVsn]);
form_reading({parse_error, {File, Line, Error}}) ->
- io_lib:format("Parse error in file: ~p. Line: ~p Error: ~p; ~n", [File, Line, Error]);
+ io_lib:format("Parse error in file: ~p. Line: ~w Error: ~p; ~n", [File, Line, Error]);
form_reading(W) ->
io_lib:format("~p~n",[W]).
form_tar_err({open, File, Error}) ->
- io_lib:format("Cannot open tar file ~s - ~p~n",
+ io_lib:format("Cannot open tar file ~ts - ~ts~n",
[File, erl_tar:format_error(Error)]);
form_tar_err({add, File, Error}) ->
- io_lib:format("Cannot add file ~s to tar file - ~s~n",
+ io_lib:format("Cannot add file ~ts to tar file - ~ts~n",
[File, erl_tar:format_error(Error)]).
%% Format warning
@@ -2362,23 +2308,23 @@ format_warning(Warnings, Werror) ->
map(fun({warning,W}) -> form_warn(Prefix, W) end, Warnings).
form_warn(Prefix, {source_not_found,{Mod,_,App,_,_}}) ->
- io_lib:format("~s~p: Source code not found: ~p.erl~n",
+ io_lib:format("~ts~w: Source code not found: ~w.erl~n",
[Prefix,App,Mod]);
form_warn(Prefix, {{parse_error, File},{_,_,App,_,_}}) ->
- io_lib:format("~s~p: Parse error: ~p~n",
+ io_lib:format("~ts~w: Parse error: ~p~n",
[Prefix,App,File]);
form_warn(Prefix, {obj_out_of_date,{Mod,_,App,_,_}}) ->
- io_lib:format("~s~p: Object code (~p) out of date~n",
+ io_lib:format("~ts~w: Object code (~w) out of date~n",
[Prefix,App,Mod]);
form_warn(Prefix, {exref_undef, Undef}) ->
F = fun({M,F,A}) ->
- io_lib:format("~sUndefined function ~p:~p/~p~n",
+ io_lib:format("~tsUndefined function ~w:~w/~w~n",
[Prefix,M,F,A])
end,
map(F, Undef);
form_warn(Prefix, missing_sasl) ->
- io_lib:format("~s: Missing application sasl. "
+ io_lib:format("~ts: Missing application sasl. "
"Can not upgrade with this release~n",
[Prefix]);
form_warn(Prefix, What) ->
- io_lib:format("~s ~p~n", [Prefix,What]).
+ io_lib:format("~ts ~p~n", [Prefix,What]).
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index c16f6aa845..54c327410d 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-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
@@ -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}}).
@@ -916,7 +904,7 @@ format_error({bad_op_before_point_of_no_return, Instruction}) ->
io_lib:format("Bad instruction ~p~nbefore point_of_no_return~n",
[Instruction]);
format_error({no_object_code, Mod}) ->
- io_lib:format("No load_object_code found for module: ~p~n", [Mod]);
+ io_lib:format("No load_object_code found for module: ~w~n", [Mod]);
format_error({suspended_not_resumed, Mods}) ->
io_lib:format("Suspended but not resumed: ~p~n", [Mods]);
format_error({resumed_not_suspended, Mods}) ->
@@ -928,19 +916,19 @@ format_error({start_not_stop, Mods}) ->
format_error({stop_not_start, Mods}) ->
io_lib:format("Stopped but not started: ~p~n", [Mods]);
format_error({no_such_application, App}) ->
- io_lib:format("Started undefined application: ~p~n", [App]);
+ io_lib:format("Started undefined application: ~w~n", [App]);
format_error({removed_application_present, App}) ->
- io_lib:format("Removed application present: ~p~n", [App]);
+ io_lib:format("Removed application present: ~w~n", [App]);
format_error(dup_mnesia_backup) ->
io_lib:format("Duplicate mnesia_backup~n", []);
format_error(bad_mnesia_backup) ->
io_lib:format("mnesia_backup in bad position~n", []);
format_error({conflicting_versions, Lib, V1, V2}) ->
- io_lib:format("Conflicting versions for ~p, ~p and ~p~n", [Lib, V1, V2]);
+ io_lib:format("Conflicting versions for ~w, ~ts and ~ts~n", [Lib, V1, V2]);
format_error({no_appl_vsn, Appl}) ->
- io_lib:format("No version specified for application: ~p~n", [Appl]);
+ io_lib:format("No version specified for application: ~w~n", [Appl]);
format_error({no_such_module, Mod}) ->
- io_lib:format("No such module: ~p~n", [Mod]);
+ io_lib:format("No such module: ~w~n", [Mod]);
format_error(too_many_point_of_no_return) ->
io_lib:format("Too many point_of_no_return~n", []);
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 7fb623bb85..716dc2b5ff 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-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
@@ -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]).
%%-----------------------------------------------------------------
@@ -493,21 +494,18 @@ get_script_from_appup(Mode, TopApp, BaseVsn, Ws, RUs) ->
throw({error, ?MODULE, {no_relup, FName, TopApp, BaseVsn}})
end.
-appup_search_for_version(BaseVsn, VsnRUs) ->
- appup_search_for_version(BaseVsn, length(BaseVsn), VsnRUs).
-
-appup_search_for_version(BaseVsn,_,[{BaseVsn,RU}|_]) ->
+appup_search_for_version(BaseVsn,[{BaseVsn,RU}|_]) ->
{ok,RU};
-appup_search_for_version(BaseVsn,Size,[{Vsn,RU}|VsnRUs]) when is_binary(Vsn) ->
- case re:run(BaseVsn,Vsn,[unicode,{capture,first,index}]) of
- {match,[{0,Size}]} ->
+appup_search_for_version(BaseVsn,[{Vsn,RU}|VsnRUs]) when is_binary(Vsn) ->
+ case re:run(BaseVsn,Vsn,[unicode,{capture,first,list}]) of
+ {match,[BaseVsn]} ->
{ok, RU};
_ ->
- appup_search_for_version(BaseVsn,Size,VsnRUs)
+ appup_search_for_version(BaseVsn,VsnRUs)
end;
-appup_search_for_version(BaseVsn,Size,[_|VsnRUs]) ->
- appup_search_for_version(BaseVsn,Size,VsnRUs);
-appup_search_for_version(_,_,[]) ->
+appup_search_for_version(BaseVsn,[_|VsnRUs]) ->
+ appup_search_for_version(BaseVsn,VsnRUs);
+appup_search_for_version(_,[]) ->
error.
@@ -602,14 +600,15 @@ print_error(Other) ->
format_error({file_problem, {"relup", _Posix}}) ->
io_lib:format("Could not open file relup~n", []);
format_error({file_problem, {File, What}}) ->
- io_lib:format("Could not ~p file ~p~n", [get_reason(What), File]);
+ io_lib:format("Could not ~w file ~ts~n", [get_reason(What), File]);
format_error({no_relup, File, App, Vsn}) ->
- io_lib:format("No release upgrade script entry for ~p-~s to ~p-~s "
- "in file ~p~n",
+ io_lib:format("No release upgrade script entry for ~w-~ts to ~w-~ts "
+ "in file ~ts~n",
[App#application.name, App#application.vsn,
App#application.name, Vsn, File]);
format_error({missing_sasl,Release}) ->
- io_lib:format("No sasl application in release ~p, ~p. Can not be upgraded.",
+ io_lib:format("No sasl application in release ~ts, ~ts. "
+ "Can not be upgraded.",
[Release#release.name, Release#release.vsn]);
format_error(Error) ->
io:format("~p~n", [Error]).
@@ -628,16 +627,16 @@ print_warning(W, Opts) ->
"*WARNING* "
end,
S = format_warning(Prefix, W),
- io:format("~s", [S]).
+ io:format("~ts", [S]).
format_warning(W) ->
format_warning("*WARNING* ", W).
format_warning(Prefix, {erts_vsn_changed, {Rel1, Rel2}}) ->
- io_lib:format("~sThe ERTS version changed between ~p and ~p~n",
+ io_lib:format("~tsThe ERTS version changed between ~p and ~p~n",
[Prefix, Rel1, Rel2]);
format_warning(Prefix, What) ->
- io_lib:format("~s~p~n",[Prefix, What]).
+ io_lib:format("~ts~p~n",[Prefix, What]).
get_reason({error, {open, _, _}}) -> open;
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..82b7a738bb 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,[],
@@ -937,7 +938,7 @@ otp_9417(cleanup,_Conf) ->
%% OTP-9395 - performance problems when there are MANY processes
%% Test that the procedure of checking for old code before an upgrade
-%% can be started is "very much faster" when there is no old code in
+%% can be started is faster when there is no old code in
%% the system.
otp_9395_check_old_code(Conf) when is_list(Conf) ->
@@ -977,8 +978,8 @@ otp_9395_check_old_code(Conf) when is_list(Conf) ->
"\tAfter purge: ~.2f sec~n"
"\tT1/T2: ~.2f",
[NProcs,length(Modules),T1/1000000,T2/1000000,X]),
- if X < 1000 ->
- ct:fail({not_enough_improvement_after_purge,round(X)});
+ if X < 1 ->
+ ct:fail({no_improvement_after_purge,X});
true ->
ok
end;
@@ -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/P2B/a-2.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app
deleted file mode 100644
index 200cfcfe47..0000000000
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app
+++ /dev/null
@@ -1,8 +0,0 @@
-{application, a,
- [{description, "A CXC 138 11"},
- {vsn, "2.0"},
- {modules, [{a, 1}, {a_sup,1}]},
- {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/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl
deleted file mode 100644
index cfe38b55ce..0000000000
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl
+++ /dev/null
@@ -1,47 +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$
-%%
--module(a).
-
-
--behaviour(gen_server).
-
-%% External exports
--export([start_link/0, a/0]).
-%% Internal exports
--export([init/1, handle_call/3, handle_info/2, terminate/2]).
-
-start_link() -> gen_server:start_link({local, aa}, a, [], []).
-
-a() -> gen_server:call(aa, a).
-
-%%-----------------------------------------------------------------
-%% Callback functions from gen_server
-%%-----------------------------------------------------------------
-init([]) ->
- process_flag(trap_exit, true),
- {ok, state}.
-
-handle_call(a, _From, State) ->
- X = application:get_all_env(a),
- {reply, X, State}.
-
-handle_info(_, State) ->
- {noreply, State}.
-
-terminate(_Reason, _State) ->
- ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl
deleted file mode 100644
index a141c1767b..0000000000
--- a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl
+++ /dev/null
@@ -1,37 +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$
-%%
--module(a_sup).
-
-
--behaviour(supervisor).
-
-%% External exports
--export([start/2]).
-
-%% Internal exports
--export([init/1]).
-
-start(_, _) ->
- supervisor:start_link({local, a_sup}, a_sup, []).
-
-init([]) ->
- SupFlags = {one_for_one, 4, 3600},
- Config = {a,
- {a, start_link, []},
- permanent, 2000, worker, [a]},
- {ok, {SupFlags, [Config]}}.
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.erl b/lib/sasl/test/systools_SUITE.erl
index 878d582e6b..367cab1d77 100644
--- a/lib/sasl/test/systools_SUITE.erl
+++ b/lib/sasl/test/systools_SUITE.erl
@@ -1,7 +1,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
@@ -40,7 +40,8 @@
-export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]).
--export([script_options/1, normal_script/1, no_mod_vsn_script/1,
+-export([script_options/1, normal_script/1, unicode_script/1,
+ unicode_script/2, no_mod_vsn_script/1,
wildcard_script/1, variable_script/1, no_sasl_script/1,
abnormal_script/1, src_tests_script/1, crazy_script/1,
included_script/1, included_override_script/1,
@@ -75,7 +76,7 @@ all() ->
groups() ->
[{script, [],
- [script_options, normal_script, no_mod_vsn_script,
+ [script_options, normal_script, unicode_script, no_mod_vsn_script,
wildcard_script, variable_script, abnormal_script,
no_sasl_script, src_tests_script, crazy_script,
included_script, included_override_script,
@@ -250,6 +251,62 @@ normal_script(Config) when is_list(Config) ->
ok.
+%% make_script: Test make_script with unicode .app file
+unicode_script(Config) when is_list(Config) ->
+ UnicodeStr = [945,946], % alhpa beta in greek letters
+
+ {LatestDir, LatestName} = create_script({unicode,UnicodeStr},Config),
+
+ DataDir = filename:absname(?copydir),
+ UnicodeApp = fname([DataDir, "d_unicode", "lib", "ua-1.0"]),
+ TarFile = fname(?privdir, "unicode_app.tgz"),
+ {ok, Tar} = erl_tar:open(TarFile, [write, compressed]),
+ ok = erl_tar:add(Tar, UnicodeApp, "ua-1.0", [compressed]),
+ ok = erl_tar:close(Tar),
+
+ UnicodeLibDir = fname([DataDir, "d_unicode", UnicodeStr]),
+ P1 = fname([UnicodeLibDir, "ua-1.0", "ebin"]),
+
+ %% Need to do this on a separate node to make sure it has unicode
+ %% filename mode (+fnu*)
+ {ok,HostStr} = inet:gethostname(),
+ Host = list_to_atom(HostStr),
+ {ok,Node} = ct_slave:start(Host,unicode_script_node,[{erl_flags,"+fnui"}]),
+
+ ok = rpc:call(Node,erl_tar,extract,
+ [TarFile, [{cwd,UnicodeLibDir},compressed]]),
+
+ true = rpc:call(Node,code,add_patha,[P1]),
+
+ ok = rpc:call(Node,file,set_cwd,[LatestDir]),
+
+ ok = rpc:call(Node,systools,make_script,[filename:basename(LatestName),
+ [local]]),
+
+ {ok, Script} = rpc:call(Node,file,consult,[LatestName++".script"]),
+
+ %% For debug purpose - print script to log
+ io:format("~tp~n",[Script]),
+
+ %% check that script contains unicode strings in
+ %% 1. release version (set in ?MODULE:do_create_script)
+ [{script,{"Test release",UnicodeStr},Instr}] = Script,
+
+ %% 2. application description (set in ua.app in data dir)
+ [AppInfo] = [X || {apply,{application,load,[{application,ua,X}]}} <- Instr],
+ {description,UnicodeStr} = lists:keyfind(description,1,AppInfo),
+
+ %% 3. path (directory name where unicode_app.tgz is extracted)
+ true = lists:member({path,[P1]},Instr),
+
+ ok.
+
+unicode_script(cleanup,Config) ->
+ _ = ct_slave:stop(unicode_script_node),
+ file:delete(fname(?privdir, "unicode_app.tgz")),
+ ok.
+
+
%% make_script:
%% Modules specified without version in .app file (db-3.1).
%% Note that this is now the normal way - i.e. systools now ignores
@@ -2090,15 +2147,20 @@ create_script(current_all_future_erts,Config) ->
do_create_script(current_all_future_erts,Config,"99.99",Apps);
create_script(current_all_future_sasl,Config) ->
Apps = [{kernel,current},{stdlib,current},{sasl,"9.9"},{db,"2.1"},{fe,"3.1"}],
- do_create_script(current_all_future_sasl,Config,current,Apps).
+ do_create_script(current_all_future_sasl,Config,current,Apps);
+create_script({unicode,RelVsn},Config) ->
+ Apps = core_apps(current) ++ [{ua,"1.0"}],
+ do_create_script(unicode,RelVsn,Config,current,Apps).
do_create_script(Id,Config,ErtsVsn,AppVsns) ->
+ do_create_script(Id,string:to_upper(atom_to_list(Id)),Config,ErtsVsn,AppVsns).
+do_create_script(Id,RelVsn,Config,ErtsVsn,AppVsns) ->
PrivDir = ?privdir,
Name = fname(PrivDir, Id),
{ok,Fd} = file:open(Name++".rel",write),
RelfileContent =
- {release,{"Test release", string:to_upper(atom_to_list(Id))},
+ {release,{"Test release", RelVsn},
{erts,erts_vsn(ErtsVsn)},
app_vsns(AppVsns)},
io:format(Fd,"~p.~n",[RelfileContent]),
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_SUITE_data/d_unicode/lib/ua-1.0/ebin/ua.app b/lib/sasl/test/systools_SUITE_data/d_unicode/lib/ua-1.0/ebin/ua.app
new file mode 100644
index 0000000000..3d38a3dde4
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_unicode/lib/ua-1.0/ebin/ua.app
@@ -0,0 +1,9 @@
+%% -*- coding: utf-8 -*-
+{application, ua,
+ [{description, "αβ"},
+ {vsn, "1.0"},
+ {modules, [ua1]},
+ {registered, []},
+ {applications, []},
+ {env, []},
+ {start, {ua1, start, []}}]}.
diff --git a/lib/sasl/test/systools_SUITE_data/d_unicode/lib/ua-1.0/src/ua1.erl b/lib/sasl/test/systools_SUITE_data/d_unicode/lib/ua-1.0/src/ua1.erl
new file mode 100644
index 0000000000..e988e80f3d
--- /dev/null
+++ b/lib/sasl/test/systools_SUITE_data/d_unicode/lib/ua-1.0/src/ua1.erl
@@ -0,0 +1,2 @@
+-module(ua1).
+-vsn("1.0").
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/sasl/vsn.mk b/lib/sasl/vsn.mk
index 6e9e2c9ff8..0e52133166 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 2.2.1
+SASL_VSN = 2.3.1
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index 8820565124..6e55498669 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -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 8d280fb3a1..5222922848 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,10 +34,9 @@
<section>
- <title>SNMP Development Toolkit 4.22.2</title>
- <p>Version 4.22.2 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>
+ <title>SNMP Development Toolkit 4.23.1</title>
+ <p>Version 4.23.1 supports code replacement in runtime from/to
+ version 4.23. </p>
<section>
<title>Improvements and new features</title>
@@ -90,9 +89,99 @@
<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.1 -->
+
+
+ <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.22.2 -->
+ </section> <!-- 4.23 -->
<section>
diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml
index 8ab3be8e18..07fdd208ff 100644
--- a/lib/snmp/doc/src/snmpm.xml
+++ b/lib/snmp/doc/src/snmpm.xml
@@ -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/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in
index 5c1f855fad..e7ca540cc6 100644
--- a/lib/snmp/mibs/Makefile.in
+++ b/lib/snmp/mibs/Makefile.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/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
index 436f15eb9c..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) ->
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 f7c311b663..716add8b9e 100644
--- a/lib/snmp/src/app/Makefile
+++ b/lib/snmp/src/app/Makefile
@@ -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 39e154d463..4c5f14da90 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,590 +22,20 @@
%% ----- U p g r a d e -------------------------------------------------------
[
- {"4.22.1",
+ {"4.23",
[
]
- },
- {"4.22",
- [
- {load_module, snmpm, soft_purge, soft_purge, []},
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []}
- ]
- },
- {"4.21.7",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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.22.1",
- [
- ]
- },
- {"4.22",
- [
- {load_module, snmpm, soft_purge, soft_purge, []},
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []},
- {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []}
- ]
- },
- {"4.21.7",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
-
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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",
+ {"4.23",
[
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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_pdus, soft_purge, soft_purge, []},
-
- {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/depend.mk b/lib/snmp/src/compile/depend.mk
index 3ee8dc4bec..2f8a07aba7 100644
--- a/lib/snmp/src/compile/depend.mk
+++ b/lib/snmp/src/compile/depend.mk
@@ -2,7 +2,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
@@ -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/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl
index d94810bc0a..2f065dddac 100644
--- a/lib/snmp/src/compile/snmpc.erl
+++ b/lib/snmp/src/compile/snmpc.erl
@@ -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
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index f590892c66..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).
@@ -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/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 e1d7f33b3f..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%
%%
@@ -458,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}
+ ].
%%%-----------------------------------------------------------------
@@ -1305,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),
@@ -5770,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..d5ffabee0b 100644
--- a/lib/snmp/test/snmp_manager_user_old.erl
+++ b/lib/snmp/test/snmp_manager_user_old.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 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
@@ -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..2633793bce 100644
--- a/lib/snmp/test/snmp_manager_user_test.erl
+++ b/lib/snmp/test/snmp_manager_user_test.erl
@@ -1,7 +1,7 @@
%%
%% %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
@@ -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_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..1f3383a7a8 100644
--- a/lib/snmp/test/snmp_test_manager.erl
+++ b/lib/snmp/test/snmp_test_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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,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 5d3c393bcc..fb7aa52402 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 4.22.2
+SNMP_VSN = 4.23.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/inviso/doc/man3/.gitignore b/lib/ssh/doc/man6/.gitignore
index e69de29bb2..e69de29bb2 100644
--- a/lib/inviso/doc/man3/.gitignore
+++ b/lib/ssh/doc/man6/.gitignore
diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile
index da99c4ea0f..0e79d9979f 100644
--- a/lib/ssh/doc/src/Makefile
+++ b/lib/ssh/doc/src/Makefile
@@ -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..e76aa20d64
--- /dev/null
+++ b/lib/ssh/doc/src/introduction.xml
@@ -0,0 +1,55 @@
+<?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.
+
+ 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_principles:des_princ">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 d4acb2ef1a..f65b66a7c5 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,177 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 2.1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed timing rekeying bug.</p>
+ <p>
+ Own Id: OTP-10940</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 2.1.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Bug in rekeying for daemon fixed.</p>
+ <p>
+ Own Id: OTP-10911</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Enhanced error message and added test for ssh clients
+ trying to start non existing subsystems.</p>
+ <p>
+ Own Id: OTP-10714</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 2.1.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Better quality on the error messages for when key
+ exchange failed.</p>
+ <p>
+ Own Id: OTP-10553 Aux Id: seq12152 </p>
+ </item>
+ <item>
+ <p>
+ Fix link to documentation for ssh:connect/3,4. Thanks to
+ Martin H�ssler.</p>
+ <p>
+ Own Id: OTP-10862</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 2.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ It is now possible to send an empty binary using
+ ssh_connection:send/3, this corner case previously caused
+ ssh_connection:send to hang.</p>
+ <p>
+ Own Id: OTP-9478 Aux Id: kunagi-226 [137] </p>
+ </item>
+ <item>
+ <p>
+ Fix typo in keyboard-interactive string. Thanks to Daniel
+ Goertzen</p>
+ <p>
+ Own Id: OTP-10456</p>
+ </item>
+ <item>
+ <p>
+ ssh_connectino:send/3 will not return until all data has
+ been sent. Previously it could return too early,
+ resulting in things such premature close of the
+ connection. Also improved error handling of closed SSH
+ channels.</p>
+ <p>
+ Own Id: OTP-10467</p>
+ </item>
+ <item>
+ <p>Fixed ssh_cli.erl crashes because #state.buf is yet
+ 'undefined'.</p> <p>Fixed Client terminateing connections
+ due to channel_request message response is sent to the
+ wrong id.</p> <p>Affected SSH clients: - all clients
+ based on SSH-2.0-TrileadSSH2Java_213 (problem #1) - SSH
+ Term Pro (problem #2)</p> <p>Thanks to Stefan Zegenhagen
+ </p>
+ <p>
+ Own Id: OTP-10475</p>
+ </item>
+ <item>
+ <p>
+ Fixed various syntax errors in SSH appup file</p>
+ <p>
+ Own Id: OTP-10657</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ SSH_FX_FILE_IS_A_DIRECTORY message for sftp implemented</p>
+ <p>
+ Own Id: OTP-6406 Aux Id: kunagi-218 [129] </p>
+ </item>
+ <item>
+ <p>
+ SSH Rekeying fixed</p>
+ <p>
+ Own Id: OTP-7785 Aux Id: kunagi-220 [131] </p>
+ </item>
+ <item>
+ <p>
+ Added User Guide for the SSH application</p>
+ <p>
+ Own Id: OTP-7786 Aux Id: kunagi-221 [132] </p>
+ </item>
+ <item>
+ <p>
+ Documentation regarding failfun, connectfun and
+ disconnectfun provided</p>
+ <p>
+ Own Id: OTP-7792 Aux Id: kunagi-222 [133] </p>
+ </item>
+ <item>
+ <p>
+ SSH connection timer implementation</p>
+ <p>
+ New option, {idle_time, integer()}, sets a timeout on
+ connection when no channels are active, defaults to
+ infinity</p>
+ <p>
+ Own Id: OTP-10514 Aux Id: seq12020 </p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Fixed internal error on when client and server can not
+ agree o which authmethod to use.</p>
+ <p>
+ Own Id: OTP-10731 Aux Id: seq12237 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 2.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -780,7 +951,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>
@@ -833,7 +1004,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>
@@ -925,7 +1096,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 57f09c0cf0..bd0d3d49dd 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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,86 @@
<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
- by calling ssh_connect:session_channel/2.</p>
+ <p>Connects to an SSH server. No channel is started. This is done
+ by calling
+ <seealso marker="ssh_connection#session_channel/2">ssh_connection:session_channel/[2, 4]</seealso>.</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,25 +135,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_dsa available.
- Will override <c><![CDATA[{public_key_alg, ssh_rsa | ssh_dsa}]]></c></p>
+ <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>
@@ -165,23 +166,11 @@
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>
- </item>
- <tag><c><![CDATA[{key_cb, atom() = KeyCallbackModule}]]></c></tag>
+ <tag><c><![CDATA[{key_cb, atom()}]]></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>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[{quiet_mode, atom() = boolean()}]]></c></tag>
<item>
@@ -189,17 +178,27 @@
</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>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>Determines if SSH shall use IPv6 or not.</p></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>
@@ -214,7 +213,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>
@@ -225,30 +225,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
@@ -256,27 +258,30 @@
</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>
+ <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>
+ <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
@@ -290,13 +295,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
@@ -305,6 +316,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>
@@ -320,9 +343,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>
@@ -331,25 +354,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..66b3b8b656 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>2013</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> <p>When implementing a SSH subsystem use the
+ <c>-behaviour(ssh_daemon_channel).</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_daemon_channel
+ behaviour is limited version of the ssh_channel behaviour.
+ </p> </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 marker =
+ "#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 marker = "#Module:handle_cast-2">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 marker = "#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 marker = "#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>
+ <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="#Module:handle_msg-2">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>
@@ -334,18 +334,18 @@
</p>
<p>For more detailed information on timeouts see the section
<seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p>
- <marker id="handle_msg"></marker>
</desc>
</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,
+ <fsummary> Handle other messages than SSH connection protocol,
call or cast messages sent to the channel.</fsummary>
<type>
<v>Msg = timeout | term()</v>
+ <v>ChannelId = ssh_channel_id()</v>
<v>State = term() </v>
</type>
<desc>
@@ -359,136 +359,38 @@
<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-1">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,
- ssh_channel_id(), State}</name>
+ <name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop,
+ ChannelId, 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>ChannelId = ssh_channel_id()</v>
<v>State = term()</v>
</type>
<desc>
- <p> Handles ssh connection protocol messages that may need
+ <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>
+ 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 +399,7 @@
</func>
<func>
- <name>CallbackModule:terminate(Reason, State) -> _</name>
+ <name>Module:terminate(Reason, State) -> _</name>
<fsummary> </fsummary>
<type>
<v>Reason = term()</v>
@@ -505,12 +407,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..b9b1ec4efa
--- /dev/null
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <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.
+
+ 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#connect-3">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#connect-3">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#connect-3">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><p>The private key contains the public key</p></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..efd4865a6f 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>2013</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#Module:handle_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="ssh_connection#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="ssh_connection#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="ssh_connection#exit_status-3">
+ 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="ssh_connection#close-2">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>
+
+ <note> <p>Events that includes a <c> WantReply</c> expects the event handling
+ process to call <seealso marker="ssh_connection#reply_request-4">ssh_connection:reply_request/4</seealso>
+ with the boolean value of <c> WantReply</c> as the second
+ argument. </p></note>
+
+ <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="ssh_connection#setenv-5"> 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="ssh_connection#shell-2"> 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="ssh_connection#exec-4">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#Module:handle_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..c4562e1211
--- /dev/null
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <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.
+
+ 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-2">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-2">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..a73d6e52d4 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>2013</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="stdlib: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/inviso/doc/src/part_notes.xml b/lib/ssh/doc/src/usersguide.xml
index 1cd8b0f1a7..c818003090 100644
--- a/lib/inviso/doc/src/part_notes.xml
+++ b/lib/ssh/doc/src/usersguide.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?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>2006</year><year>2009</year>
+ <year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,23 +13,25 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
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>Inviso Release Notes</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
+ <title>SSH User's Guide</title>
+ <prepared>OTP Team</prepared>
+ <date>2012-10-11</date>
+ <file>usersguide.xml</file>
</header>
<description>
- <p><em>Inviso</em>, an Erlang trace tool.</p>
+ <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="notes.xml"/>
+ <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..c994c1c56c
--- /dev/null
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -0,0 +1,299 @@
+<?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-0"> 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. </p>
+
+ <note><p> 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>
+ </p></note>
+
+ <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/src/Makefile b/lib/ssh/src/Makefile
index b8eecd3fa2..93d0b54f57 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. 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
@@ -41,7 +41,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN)
BEHAVIOUR_MODULES= \
ssh_sftpd_file_api \
ssh_channel \
- ssh_key_api
+ ssh_daemon_channel \
+ 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..49707f3378 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -10,20 +10,22 @@
ssh_auth,
ssh_bits,
ssh_cli,
+ ssh_client_key_api,
ssh_channel,
ssh_channel_sup,
ssh_connection,
ssh_connection_handler,
ssh_connection_manager,
ssh_connection_sup,
+ ssh_daemon_channel,
ssh_shell,
sshc_sup,
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,
@@ -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 d99931c746..b25e0c9e37 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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,15 +19,23 @@
{"%VSN%",
[
+ {<<"2.1.4">>, [{load_module, ssh_sftp, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []}]},
+ {<<"2.1.3">>, [{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.1.4">>, [{load_module, ssh_sftp, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []}]},
+ {<<"2.1.3">>, [{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}]}
]
-}. \ No newline at end of file
+}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index e5c016eb3f..3a7aa79d16 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -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],
- [{user_pid, self()}, {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) ->
@@ -169,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(),
@@ -246,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 ->
@@ -257,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},
@@ -267,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}
@@ -318,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) ->
@@ -337,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]);
@@ -355,6 +364,10 @@ handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOption
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).
@@ -368,12 +381,16 @@ 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 check_pref_algs(Value) of
- true ->
- Opt;
+ case handle_pref_algs(Value, []) of
+ {true, NewOpts} ->
+ NewOpts;
_ ->
throw({error, {eoptions, Opt}})
end;
@@ -391,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) ->
@@ -411,7 +426,9 @@ 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 is_boolean(Value) ->
+
+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),
is_atom(Cb),
@@ -429,6 +446,10 @@ handle_ssh_option({shell, Value} = Opt) when is_function(Value) ->
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}}).
@@ -437,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) ->
@@ -447,14 +468,18 @@ handle_inet_option({reuseaddr, _} = Opt) ->
handle_inet_option(Opt) ->
Opt.
%% Check preferred algs
-check_pref_algs([]) ->
- true;
-check_pref_algs([H|T]) ->
+handle_pref_algs([], Acc) ->
+ {true, lists:reverse(Acc)};
+handle_pref_algs([H|T], Acc) ->
case H of
ssh_dsa ->
- check_pref_algs(T);
+ handle_pref_algs(T, ['ssh-dss'| Acc]);
ssh_rsa ->
- check_pref_algs(T);
+ 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.
@@ -473,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 c436793dc4..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);
@@ -120,8 +121,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
data = <<>>},
case proplists:get_value(pref_public_key_algs, Opts, false) of
false ->
- FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts,
- ?PREFERRED_PK_ALG)),
+ 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),
@@ -130,7 +130,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
userauth_methods = none,
service = "ssh-connection"});
Algs ->
- FirstAlg = algorithm(lists:nth(1, Algs)),
+ FirstAlg = lists:nth(1, Algs),
case length(Algs) =:= 2 of
true ->
SecondAlg = other_alg(FirstAlg),
@@ -358,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),
@@ -381,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) ->
@@ -457,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..062ed764ca 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.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
@@ -23,14 +23,32 @@
-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()) ->
+ {ok, State::term()} | {stop, ChannelId::integer(), State::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..7232cc482c 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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,7 +24,7 @@
-module(ssh_cli).
--behaviour(ssh_channel).
+-behaviour(ssh_daemon_channel).
-include("ssh.hrl").
-include("ssh_connect.hrl").
@@ -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..a17c7cbc77
--- /dev/null
+++ b/lib/ssh/src/ssh_client_key_api.erl
@@ -0,0 +1,35 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-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(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 c2a7c63cbe..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;
@@ -441,7 +452,7 @@ handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = 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,
@@ -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 d8950a7b67..1c4477aeb3 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.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
@@ -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}).
@@ -210,6 +213,29 @@ key_exchange(#ssh_msg_kexdh_init{} = Msg,
description = Desc,
language = "en"}, State)
end;
+
+key_exchange({#ssh_msg_kexinit{} = Kex, Payload},
+ #state{ssh_params = #ssh{role = Role} = Ssh0,
+ key_exchange_init_msg = OwnKex} =
+ State) ->
+ Ssh1 = ssh_transport:key_init(opposite_role(Role), Ssh0, Payload),
+ try ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
+ {ok, NextKexMsg, Ssh} when Role == client ->
+ send_msg(NextKexMsg, State),
+ {next_state, key_exchange,
+ next_packet(State#state{ssh_params = Ssh})};
+ {ok, Ssh} when Role == server ->
+ {next_state, key_exchange,
+ next_packet(State#state{ssh_params = Ssh})}
+ catch
+ #ssh_msg_disconnect{} = DisconnectMsg ->
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
+ end;
key_exchange(#ssh_msg_kexdh_reply{} = Msg,
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
@@ -220,11 +246,13 @@ key_exchange(#ssh_msg_kexdh_reply{} = Msg,
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
handle_disconnect(DisconnectMsg, State);
+ {ErrorToDisplay, #ssh_msg_disconnect{} = DisconnectMsg} ->
+ handle_disconnect(DisconnectMsg, State, ErrorToDisplay);
_:Error ->
Desc = log_error(Error),
handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = Desc,
- language = "en"}, State)
+ description = Desc,
+ language = "en"}, State)
end;
key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
@@ -419,11 +447,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) ->
@@ -447,7 +479,9 @@ userauth(#ssh_msg_userauth_banner{message = Msg},
{next_state, userauth, next_packet(State)}.
connected({#ssh_msg_kexinit{}, _Payload} = Event, State) ->
- kexinit(Event, State#state{renegotiate = true}).
+ kexinit(Event, State#state{renegotiate = true});
+connected({#ssh_msg_kexdh_init{}, _Payload} = Event, State) ->
+ key_exchange(Event, State#state{renegotiate = true}).
%%--------------------------------------------------------------------
%% Function:
@@ -500,7 +534,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) ->
+ {ok, [{send_oct,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),
@@ -651,6 +700,11 @@ terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName, #state{ssh_params
send_msg(SshPacket, State),
ssh_connection_manager:event(Pid, Msg),
terminate(normal, StateName, State#state{ssh_params = Ssh});
+terminate({shutdown, {#ssh_msg_disconnect{} = Msg, ErrorMsg}}, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
+ {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
+ send_msg(SshPacket, State),
+ ssh_connection_manager:event(Pid, Msg, ErrorMsg),
+ terminate(normal, StateName, State#state{ssh_params = Ssh});
terminate(Reason, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
log_error(Reason),
DisconnectMsg =
@@ -749,9 +803,9 @@ extract_algs([], NewList) ->
lists:reverse(NewList);
extract_algs([H|T], NewList) ->
case H of
- ssh_dsa ->
+ 'ssh-dss' ->
extract_algs(T, ["ssh-dss"|NewList]);
- ssh_rsa ->
+ 'ssh-rsa' ->
extract_algs(T, ["ssh-rsa"|NewList])
end.
available_host_key(KeyCb, "ssh-dss"= Alg, Opts) ->
@@ -928,6 +982,8 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
handle_disconnect(#ssh_msg_disconnect{} = Msg, State) ->
{stop, {shutdown, Msg}, State}.
+handle_disconnect(#ssh_msg_disconnect{} = Msg, State, ErrorMsg) ->
+ {stop, {shutdown, {Msg, ErrorMsg}}, State}.
counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl
index 5aa79f978c..99a0b6a7c8 100644
--- a/lib/ssh/src/ssh_connection_manager.erl
+++ b/lib/ssh/src/ssh_connection_manager.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
@@ -40,8 +40,7 @@
close/2, stop/1, send/5,
send_eof/2]).
--export([open_channel/6, request/6, request/7, global_request/4, event/2,
- cast/2]).
+-export([open_channel/6, reply_request/3, request/6, request/7, global_request/4, event/2, event/3, cast/2]).
%% Internal application API and spawn
-export([send_msg/1, ssh_channel_info_handler/3]).
@@ -62,6 +61,7 @@
latest_channel_id = 0,
opts,
channel_args,
+ idle_timer_ref, % timerref
connected
}).
@@ -95,6 +95,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
@@ -106,10 +109,11 @@ global_request(ConnectionManager, Type, true = Reply, Data) ->
global_request(ConnectionManager, Type, false = Reply, Data) ->
cast(ConnectionManager, {global_request, self(), Type, Reply, Data}).
-
+
+event(ConnectionManager, BinMsg, ErrorMsg) ->
+ call(ConnectionManager, {ssh_msg, self(), BinMsg, ErrorMsg}).
event(ConnectionManager, BinMsg) ->
call(ConnectionManager, {ssh_msg, self(), BinMsg}).
-
info(ConnectionManager) ->
info(ConnectionManager, {info, all}).
@@ -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) ->
@@ -247,8 +262,7 @@ handle_call({ssh_msg, Pid, Msg}, From,
%% To avoid that not all data sent by the other side is processes before
%% possible crash in ssh_connection_handler takes down the connection.
- gen_server:reply(From, ok),
-
+ gen_server:reply(From, ok),
ConnectionMsg = decode_ssh_msg(Msg),
try ssh_connection:handle_msg(ConnectionMsg, Connection0, Pid, Role) of
{{replies, Replies}, Connection} ->
@@ -279,7 +293,45 @@ handle_call({ssh_msg, Pid, Msg}, From,
disconnect_fun(Reason, SSHOpts),
{stop, {shutdown, Error}, State#state{connection_state = Connection}}
end;
+handle_call({ssh_msg, Pid, Msg, ErrorMsg}, From,
+ #state{connection_state = Connection0,
+ role = Role, opts = Opts, connected = IsConnected,
+ client = ClientPid}
+ = State) ->
+ %% To avoid that not all data sent by the other side is processes before
+ %% possible crash in ssh_connection_handler takes down the connection.
+ gen_server:reply(From, ok),
+ ConnectionMsg = decode_ssh_msg(Msg),
+ try ssh_connection:handle_msg(ConnectionMsg, Connection0, Pid, Role) of
+ {{replies, Replies}, Connection} ->
+ lists:foreach(fun send_msg/1, Replies),
+ {noreply, State#state{connection_state = Connection}};
+ {noreply, Connection} ->
+ {noreply, State#state{connection_state = Connection}};
+ {disconnect, {_, Reason}, {{replies, Replies}, Connection}}
+ when Role == client andalso (not IsConnected) ->
+ lists:foreach(fun send_msg/1, Replies),
+ ClientPid ! {self(), not_connected, {Reason, ErrorMsg}},
+ {stop, {shutdown, normal}, State#state{connection = Connection}};
+ {disconnect, Reason, {{replies, Replies}, Connection}} ->
+ lists:foreach(fun send_msg/1, Replies),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, {shutdown, normal}, State#state{connection_state = Connection}}
+ catch
+ _:Error ->
+ {disconnect, Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error",
+ language = "en"}, Connection0, undefined,
+ Role),
+ lists:foreach(fun send_msg/1, Replies),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, {shutdown, Error}, State#state{connection_state = Connection}}
+ end;
handle_call({global_request, Pid, _, _, _} = Request, From,
#state{connection_state =
#connection{channel_cache = Cache}} = State0) ->
@@ -295,6 +347,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 +407,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 +452,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 +502,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 +519,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 +536,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 +560,7 @@ 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(60000, self(), rekey_data),
{noreply, State#state{connection = Connection,
connection_state =
CState#connection{address = Address,
@@ -505,12 +577,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),
@@ -534,8 +611,15 @@ 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) ->
@@ -608,6 +692,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
@@ -655,6 +778,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,
@@ -703,7 +828,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_daemon_channel.erl b/lib/ssh/src/ssh_daemon_channel.erl
new file mode 100644
index 0000000000..ab3efbcaff
--- /dev/null
+++ b/lib/ssh/src/ssh_daemon_channel.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%
+%%
+
+%%
+%% Description: a gen_server implementing a simple
+%% terminal (using the group module) for a CLI
+%% over SSH
+
+-module(ssh_daemon_channel).
+
+%% 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()) ->
+ {ok, State::term()} | {stop, ChannelId::integer(), State::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_file.erl b/lib/ssh/src/ssh_file.erl
index a6b82a7a13..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") ->
@@ -261,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.
@@ -272,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} ->
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/stdlib/test/erl_eval_helper.erl b/lib/ssh/src/ssh_server_key.erl
index 6863b40108..8140114990 100644
--- a/lib/stdlib/test/erl_eval_helper.erl
+++ b/lib/ssh/src/ssh_server_key.erl
@@ -1,28 +1,33 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. 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(erl_eval_helper, [Base]).
-
--export([add/1]).
+-module(ssh_server_key).
-add(Arg) ->
- Base+Arg.
+-include_lib("public_key/include/public_key.hrl").
+-include("ssh.hrl").
+-type ssh_algorithm() :: string().
+-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/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl b/lib/ssh/src/ssh_server_key_api.erl
index 8f3477d3ac..4fd660ecb5 100644
--- a/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl
+++ b/lib/ssh/src/ssh_server_key_api.erl
@@ -1,59 +1,30 @@
%%
%% %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_api).
-func() ->
- hello,
- func1(),
- case func2() of
- ok ->
- helloagain,
- case func3() of
- ok ->
- ok;
- error ->
- error
- end;
- error ->
- error
- end,
- excluded(),
- func4().
+-include_lib("public_key/include/public_key.hrl").
+-include("ssh.hrl").
-func1() ->
- ok.
-func2() ->
- ok.
-func3() ->
- error.
-func4() ->
- ok.
+-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_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index f000558100..10167a9223 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -47,7 +47,7 @@
recv_window/2, list_dir/3, read_file/3, write_file/4]).
%% ssh_channel callbacks
--export([init/1, handle_call/3, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+-export([init/1, handle_call/3, handle_cast/2, code_change/3, handle_msg/2, handle_ssh_msg/2, terminate/2]).
%% TODO: Should be placed elsewhere ssh_sftpd should not call functions in ssh_sftp!
-export([info_to_attr/1, attr_to_info/1]).
@@ -403,7 +403,7 @@ init([Cm, ChannelId, Timeout]) ->
rep_buf = <<>>,
inf = new_inf()}};
failure ->
- {stop, {error, "server failed to start sftp subsystem"}};
+ {stop, "server failed to start sftp subsystem"};
Error ->
{stop, Error}
end.
@@ -436,6 +436,12 @@ handle_call({{timeout, Timeout}, Msg}, From, #state{req_id = Id} = State) ->
timer:send_after(Timeout, {timeout, Id, From}),
do_handle_call(Msg, From, State).
+handle_cast(_,State) ->
+ {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
do_handle_call({open, Async,FileName,Mode}, From, #state{xf = XF} = State) ->
{Access,Flags,Attrs} = open_mode(XF#ssh_xfer.vsn, Mode),
ReqID = State#state.req_id,
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index ec7b76b0b3..3d469d3c6e 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -23,8 +23,7 @@
-module(ssh_sftpd).
-%%-behaviour(gen_server).
--behaviour(ssh_channel).
+-behaviour(ssh_daemon_channel).
-include_lib("kernel/include/file.hrl").
@@ -36,7 +35,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 +118,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
@@ -169,7 +158,7 @@ handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State) ->
{stop, ChannelId, State}.
%%--------------------------------------------------------------------
-%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State}
+%% Function: handle_msg(Args) -> {ok, State} | {stop, ChannelId, State}
%%
%% Description: Handles other messages
%%--------------------------------------------------------------------
@@ -369,17 +358,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 +630,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_shell.erl b/lib/ssh/src/ssh_shell.erl
index 6590486a4c..8031450617 100644
--- a/lib/ssh/src/ssh_shell.erl
+++ b/lib/ssh/src/ssh_shell.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -23,7 +23,9 @@
-include("ssh_connect.hrl").
--behaviour(ssh_channel).
+%%% As this is an user interactive client it behaves like a daemon
+%%% channel inspite of it being a client.
+-behaviour(ssh_daemon_channel).
%% ssh_channel callbacks
-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
@@ -123,7 +125,7 @@ handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State) ->
{stop, ChannelId, State}.
%%--------------------------------------------------------------------
-%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State}
+%% Function: handle_msg(Args) -> {ok, State} | {stop, ChannelId, State}
%%
%% Description: Handles other channel messages
%%--------------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 7f6e7d9946..98d59d01de 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -356,12 +356,12 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, f = F,
{ok, SshPacket, Ssh#ssh{shared_secret = K,
exchanged_hash = H,
session_id = sid(Ssh, H)}};
- _Error ->
+ Error ->
Disconnect = #ssh_msg_disconnect{
code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
description = "Key exchange failed",
language = "en"},
- throw(Disconnect)
+ throw({Error, Disconnect})
end.
handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = _Min,
@@ -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..8dc9a40f92 100644
--- a/lib/ssh/src/ssh_xfer.hrl
+++ b/lib/ssh/src/ssh_xfer.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -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 2ceaa9daa5..80273420d9 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,8 +147,6 @@ 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),
@@ -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 200000 ->
+ %%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..ac93db8f2e 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,20 @@
-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},
+ sftp_nonexistent_subsystem
+ ].
+
+
init_per_suite(Config) ->
case (catch crypto:start()) of
ok ->
@@ -52,35 +55,67 @@ 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}]}]),
+ [{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(sftp_nonexistent_subsystem, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ SysDir = ?config(data_dir, Config),
+ Sftpd = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, PrivDir},
+ {subsystems, []},
+ {user_passwords,
+ [{?USER, ?PASSWD}]}
+ ]),
+ [{sftpd, Sftpd} | 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 +140,8 @@ 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(sftp_nonexistent_subsystem, Config) ->
+ Config;
end_per_testcase(rename_file, Config) ->
PrivDir = ?config(priv_dir, Config),
NewFileName = filename:join(PrivDir, "test.txt"),
@@ -124,69 +153,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 +171,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 +187,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 +282,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 +348,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 +362,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 +380,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 +393,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 +401,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 +427,27 @@ 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.
+%%--------------------------------------------------------------------
+sftp_nonexistent_subsystem() ->
+ [""].
+sftp_nonexistent_subsystem(Config) when is_list(Config) ->
+ {_,Host, Port} = ?config(sftpd, Config),
+ {error,"server failed to start sftp subsystem"} =
+ ssh_sftp:start_channel(Host, Port,
+ [{user_interaction, false},
+ {user, ?USER}, {password, ?PASSWD},
+ {silently_accept_hosts, true}]).
-%% 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..8ad383d8c9 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
@@ -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
@@ -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..75e73712f1 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 ->
@@ -116,12 +49,31 @@ groups() ->
erlang_client_openssh_server_setenv,
erlang_client_openssh_server_publickey_rsa,
erlang_client_openssh_server_publickey_dsa,
- erlang_client_openssh_server_password]},
+ erlang_client_openssh_server_password,
+ erlang_client_openssh_server_nonexistent_subsystem
+ ]},
{erlang_server, [], [erlang_server_openssh_client_exec,
erlang_server_openssh_client_exec_compressed,
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 +89,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 +118,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 +143,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 +159,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 +183,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 +202,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 +215,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 +224,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 +233,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 +243,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 +252,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 +276,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 +294,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 +321,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 +346,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 +355,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 +366,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 +383,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,25 +397,39 @@ 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.
%%--------------------------------------------------------------------
+
+erlang_client_openssh_server_nonexistent_subsystem() ->
+ [{doc, "Test client password option"}].
+erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) ->
+
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+ [{user_interaction, false},
+ silently_accept_hosts]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ failure = ssh_connection:subsystem(ConnectionRef, ChannelId, "foo", infinity).
+
+%%--------------------------------------------------------------------
%
%% Not possible to send password with openssh without user interaction
%%
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
-%%% 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 921ec2206a..d5ca1cb3fe 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.2
+SSH_VSN = 2.1.6
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 49bbd5d27d..a61f52b809 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>2012</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,15 +22,149 @@
</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.1.2</title>
+ <section><title>SSL 5.2.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Transport callback handling is changed so that gen_tcp is
+ treated as a special case where inet will be called
+ directly for functions such as setopts, as gen_tcp does
+ not have its own setopts. This will enable users to use
+ the transport callback for other customizations such as
+ websockets.</p>
+ <p>
+ Own Id: OTP-10847</p>
+ </item>
+ <item>
+ <p>
+ Follow up to OTP-10451 solved in ssl-5.2 R16A. Make sure
+ format_error return good strings. Replace confusing
+ legacy atoms with more descriptive atoms.</p>
+ <p>
+ Own Id: OTP-10864</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSL: TLS 1.2, advertise sha224 support, thanks to Andreas
+ Schultz.</p>
+ <p>
+ Own Id: OTP-10586</p>
+ </item>
+ <item>
+ <p>
+ If an ssl server is restarted with new options and a
+ client tries to reuse a session the server must make sure
+ that it complies to the new options before agreeing to
+ reuse it.</p>
+ <p>
+ Own Id: OTP-10595</p>
+ </item>
+ <item>
+ <p>
+ Now handles cleaning of CA-certificate database correctly
+ so that there will be no memory leek, bug was introduced
+ in ssl- 5.1 when changing implementation to increase
+ parallel execution.</p>
+ <p>
+ Impact: Improved memory usage, especially if you have
+ many different certificates and upgrade tcp-connections
+ to TLS-connections.</p>
+ <p>
+ Own Id: OTP-10710</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Support Next Protocol Negotiation in TLS, thanks to Ben
+ Murphy for the contribution.</p>
+ <p>
+ Impact: Could give performance benefit if used as it
+ saves a round trip.</p>
+ <p>
+ Own Id: OTP-10361 Aux Id: kunagi-214 [125] </p>
+ </item>
+ <item>
+ <p>
+ TLS 1.2 will now be the default TLS version if sufficient
+ crypto support is available otherwise TLS 1.1 will be
+ default.</p>
+ <p>
+ Impact: A default TLS connection will have higher
+ security and hence it may be perceived as slower then
+ before.</p>
+ <p>
+ Own Id: OTP-10425 Aux Id: kunagi-275 [186] </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to call controlling_process on a
+ listen socket, same as in gen_tcp.</p>
+ <p>
+ Own Id: OTP-10447</p>
+ </item>
+ <item>
+ <p>
+ Remove filter mechanisms that made error messages
+ backwards compatible with old ssl but hid information
+ about what actually happened.</p>
+ <p>
+ This does not break the documented API however other
+ reason terms may be returned, so code that matches on the
+ reason part of {error, Reason} may fail.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10451 Aux Id: kunagi-270 [181] </p>
+ </item>
+ <item>
+ <p>
+ Added missing dependencies to Makefile</p>
+ <p>
+ Own Id: OTP-10594</p>
+ </item>
+ <item>
+ <p>
+ Removed deprecated function ssl:pid/0, it has been
+ pointless since R14 but has been keep for backwards
+ compatibility.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10613 Aux Id: kunagi-331 [242] </p>
+ </item>
+ <item>
+ <p>
+ Refactor to simplify addition of key exchange methods,
+ thanks to Andreas Schultz.</p>
+ <p>
+ Own Id: OTP-10709</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -605,1285 +739,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 5098d26a3a..6979fb5b5e 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2012</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,8 +36,8 @@
<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, experimental
- support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</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>
@@ -79,13 +79,18 @@
{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}
- - defaults to {gen_tcp, tcp, tcp_closed}. Ssl may be
- run over any reliable transport protocol that has
- an equivalent API to gen_tcp's.</c></p>
+ <p><c>transportoption() = {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}}
+ - defaults to {gen_tcp, tcp, tcp_closed, tcp_error}. Can be used to customize
+ the transport layer. The callback module must implement a reliable transport
+ protocol and behave as gen_tcp and in addition have functions corresponding to
+ inet:setopts/2, inet:getopts/2, inet:peername/1, inet:sockname/1 and inet:port/1.
+ The callback gen_tcp is treated specially and will call inet directly.
+ </c></p>
<p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallbackModule =
atom()</c>
@@ -273,11 +278,18 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
<p> {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, unknown_ca},{bad_cert, selfsigned_peer}, {bad_cert, name_not_permitted}, {bad_cert, missing_basic_constraint}, {bad_cert, invalid_key_usage}</p>
</item>
+ <tag>{versions, [protocol()]}</tag>
+ <item>TLS protocol versions that will be supported by started clients and servers.
+ This option overrides the application environment option <c>protocol_version</c>. If the
+ environment option is not set it defaults to all versions supported by the SSL application. See also
+ <seealso marker="ssl:ssl_app">ssl(6)</seealso>
+ </item>
+
<tag>{hibernate_after, integer()|undefined}</tag>
- <item>When an integer-value is specified, the <code>ssl_connection</code>
+ <item>When an integer-value is specified, the <c>ssl_connection</c>
will go into hibernation after the specified number of milliseconds
of inactivity, thus reducing its memory footprint. When
- <code>undefined</code> is specified (this is the default), the process
+ <c>undefined</c> is specified (this is the default), the process
will never go into hibernation.
</item>
</taglist>
@@ -301,8 +313,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>
@@ -353,6 +386,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>
@@ -766,8 +807,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..0ee5b23e47 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE appref SYSTEM "appref.dtd">
<appref>
<header>
<copyright>
- <year>1999</year><year>2010</year>
+ <year>1999</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,10 +29,21 @@
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.
+ are defined for the SSL application. See <seealso
+ marker="kernel:application">application(3)</seealso>for more
+ information about configuration parameters.
</p>
<p>Note that the environment parameters can be set on the command line,
for instance,</p>
@@ -77,7 +88,7 @@
<section>
<title>SEE ALSO</title>
- <p>application(3)</p>
+ <p><seealso marker="kernel:application">application(3)</seealso></p>
</section>
</appref>
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/src/Makefile b/lib/ssl/src/Makefile
index c5c5bf593a..e61f415c84 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -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
@@ -52,11 +52,11 @@ MODULES= \
ssl_cipher \
ssl_connection \
ssl_connection_sup \
- ssl_debug \
ssl_handshake \
ssl_manager \
ssl_session \
ssl_session_cache \
+ ssl_socket \
ssl_record \
ssl_ssl2 \
ssl_ssl3 \
@@ -64,7 +64,6 @@ MODULES= \
ssl_tls_dist_proxy
INTERNAL_HRL_FILES = \
- ssl_debug.hrl \
ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \
ssl_record.hrl
@@ -108,10 +107,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 +129,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.app.src b/lib/ssl/src/ssl.app.src
index 13d5eaf4d7..897a097f73 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -13,10 +13,10 @@
ssl_session,
ssl_session_cache_api,
ssl_session_cache,
+ ssl_socket,
ssl_record,
ssl_manager,
ssl_handshake,
- ssl_debug,
ssl_connection_sup,
ssl_connection,
ssl_cipher,
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 9b1227fa7f..a8a494b2fc 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,24 +1,15 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"5.1.1", [{restart_application, ssl}]
- },
- {"5.1", [
- {load_module, ssl_connection, soft_purge, soft_purge, []}
- ]
- },
+ {<<"5.2">>, [{restart_application, ssl}]},
+ {<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
- {"5.1.1", [{restart_application, ssl}]
- },
- {"5.1", [
- {load_module, ssl_connection, soft_purge, soft_purge, []}
- ]
- },
- {"5.1", [{restart_application, ssl}]},
+ {<<"5.2">>, [{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 66ceb2a591..0ba59cede2 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.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
@@ -28,16 +28,15 @@
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, random_bytes/1]).
-
--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").
@@ -67,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().
@@ -75,7 +76,8 @@
string(). % (according to old API)
-type ssl_imp() :: new | old.
--type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}}.
+-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(),
+ ClosedTag::atom(), ErrTag::atom()}}.
-type prf_random() :: client_random | server_random.
%%--------------------------------------------------------------------
@@ -107,7 +109,8 @@ stop() ->
%%--------------------------------------------------------------------
-spec connect(host() | port(), [connect_option()]) -> {ok, #sslsocket{}} |
{error, reason()}.
--spec connect(host() | port(), [connect_option()] | inet:port_number(), timeout() | list()) ->
+-spec connect(host() | port(), [connect_option()] | inet:port_number(),
+ timeout() | list()) ->
{ok, #sslsocket{}} | {error, reason()}.
-spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
{ok, #sslsocket{}} | {error, reason()}.
@@ -119,12 +122,15 @@ connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions, infinity).
connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
+ {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
+ {gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = emulated_options(),
- {ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
- ok = inet:setopts(Socket, internal_inet_values()),
- try handle_options(SslOptions0 ++ InetValues, client) of
- {ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} ->
- case inet:peername(Socket) of
+ {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
+ try handle_options(SslOptions0 ++ SocketValues, client) of
+ {ok, #config{cb = CbInfo, ssl = SslOptions, emulated = EmOpts}} ->
+
+ ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()),
+ case ssl_socket:peername(Transport, Socket) of
{ok, {Address, Port}} ->
ssl_connection:connect(Address, Port, Socket,
{SslOptions, EmOpts},
@@ -156,14 +162,14 @@ connect(Host, Port, Options, Timeout) ->
%% Description: Creates an ssl listen socket.
%%--------------------------------------------------------------------
listen(_Port, []) ->
- {error, enooptions};
+ {error, nooptions};
listen(Port, Options0) ->
try
{ok, Config} = handle_options(Options0, server),
- #config{cb={CbModule, _, _, _},inet_user=Options} = Config,
- case CbModule:listen(Port, Options) of
+ #config{cb = {Transport, _, _, _}, inet_user = Options} = Config,
+ case Transport:listen(Port, Options) of
{ok, ListenSocket} ->
- {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}};
+ {ok, #sslsocket{pid = {ListenSocket, Config}}};
Err = {error, _} ->
Err
end
@@ -182,23 +188,23 @@ listen(Port, Options0) ->
transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
-transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}}}, Timeout) ->
+transport_accept(#sslsocket{pid = {ListenSocket, #config{cb = CbInfo, ssl = SslOpts}}}, Timeout) ->
%% The setopt could have been invoked on the listen socket
%% and options should be inherited.
EmOptions = emulated_options(),
- {ok, InetValues} = inet:getopts(ListenSocket, EmOptions),
- ok = inet:setopts(ListenSocket, internal_inet_values()),
- {CbModule,_,_, _} = CbInfo,
- case CbModule:accept(ListenSocket, Timeout) of
+ {Transport,_,_, _} = CbInfo,
+ {ok, SocketValues} = ssl_socket:getopts(Transport, ListenSocket, EmOptions),
+ ok = ssl_socket:setopts(Transport, ListenSocket, internal_inet_values()),
+ case Transport:accept(ListenSocket, Timeout) of
{ok, Socket} ->
- ok = inet:setopts(ListenSocket, InetValues),
- {ok, Port} = inet:port(Socket),
+ ok = ssl_socket:setopts(Transport, ListenSocket, SocketValues),
+ {ok, Port} = ssl_socket:port(Transport, Socket),
ConnArgs = [server, "localhost", Port, Socket,
- {SslOpts, socket_options(InetValues)}, self(), CbInfo],
+ {SslOpts, socket_options(SocketValues)}, self(), CbInfo],
case ssl_connection_sup:start_child(ConnArgs) of
{ok, Pid} ->
- ssl_connection:socket_control(Socket, Pid, CbModule);
+ ssl_connection:socket_control(Socket, Pid, Transport);
{error, Reason} ->
{error, Reason}
end;
@@ -208,9 +214,11 @@ transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}
%%--------------------------------------------------------------------
-spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}.
--spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option() | transport_option()]) ->
+-spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option()
+ | transport_option()]) ->
ok | {ok, #sslsocket{}} | {error, reason()}.
--spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}.
+-spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
@@ -225,12 +233,14 @@ ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(ListenSocket, SslOptions, infinity).
ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
+ {Transport,_,_,_} =
+ proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = emulated_options(),
- {ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
- ok = inet:setopts(Socket, internal_inet_values()),
- try handle_options(SslOptions ++ InetValues, server) of
- {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} ->
- {ok, Port} = inet:port(Socket),
+ {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
+ try handle_options(SslOptions ++ SocketValues, server) of
+ {ok, #config{cb = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
+ ok = ssl_socket:setopts(Transport, Socket, internal_inet_values()),
+ {ok, Port} = ssl_socket:port(Transport, Socket),
ssl_connection:ssl_accept(Port, Socket,
{SslOpts, EmOpts},
self(), CbInfo, Timeout)
@@ -243,18 +253,20 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
%%
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
-close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}) ->
- CbMod:close(ListenSocket);
-close(#sslsocket{pid = Pid}) ->
- ssl_connection:close(Pid).
+close(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ ssl_connection:close(Pid);
+close(#sslsocket{pid = {ListenSocket, #config{cb={Transport,_, _, _}}}}) ->
+ Transport: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={Transport, _, _, _}}}}, Data) ->
+ Transport:send(ListenSocket, Data). %% {error,enotconn}
%%--------------------------------------------------------------------
-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
@@ -264,8 +276,11 @@ 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={Transport, _, _, _}}}}, _,_) when is_port(Listen)->
+ Transport:recv(Listen, 0). %% {error,enotconn}
%%--------------------------------------------------------------------
-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}.
@@ -273,8 +288,13 @@ 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={Transport, _, _, _}}}},
+ NewOwner) when is_port(Listen),
+ is_pid(NewOwner) ->
+ Transport:controlling_process(Listen, NewOwner).
%%--------------------------------------------------------------------
-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} |
@@ -282,29 +302,35 @@ 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 = {Transport, Socket}}) when is_pid(Pid)->
+ ssl_socket:peername(Transport, Socket);
+peername(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}) ->
+ ssl_socket:peername(Transport, 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().
@@ -316,6 +342,14 @@ 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()].
@@ -340,18 +374,19 @@ cipher_suites(openssl) ->
%%--------------------------------------------------------------------
getopts(#sslsocket{pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) ->
ssl_connection:get_opts(Pid, OptionTags);
-getopts(#sslsocket{pid = {ListenSocket, _}}, OptionTags) when is_list(OptionTags) ->
- try inet:getopts(ListenSocket, OptionTags) of
+getopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}},
+ OptionTags) when is_list(OptionTags) ->
+ try ssl_socket:getopts(Transport, ListenSocket, OptionTags) of
{ok, _} = Result ->
Result;
{error, InetError} ->
- {error, {eoptions, {inet_options, OptionTags, InetError}}}
+ {error, {options, {socket_options, OptionTags, InetError}}}
catch
_:_ ->
- {error, {eoptions, {inet_options, OptionTags}}}
+ {error, {options, {socket_options, OptionTags}}}
end;
getopts(#sslsocket{}, OptionTags) ->
- {error, {eoptions, {inet_options, OptionTags}}}.
+ {error, {options, {socket_options, OptionTags}}}.
%%--------------------------------------------------------------------
-spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}.
@@ -365,29 +400,30 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
ssl_connection:set_opts(Pid, Options)
catch
_:_ ->
- {error, {eoptions, {not_a_proplist, Options0}}}
+ {error, {options, {not_a_proplist, Options0}}}
end;
-setopts(#sslsocket{pid = {ListenSocket, _}}, Options) when is_list(Options) ->
- try inet:setopts(ListenSocket, Options) of
+setopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}, Options) when is_list(Options) ->
+ try ssl_socket:setopts(Transport, ListenSocket, Options) of
ok ->
ok;
{error, InetError} ->
- {error, {eoptions, {inet_options, Options, InetError}}}
+ {error, {options, {socket_options, Options, InetError}}}
catch
_:Error ->
- {error, {eoptions, {inet_options, Options, Error}}}
+ {error, {options, {socket_options, Options, Error}}}
end;
setopts(#sslsocket{}, Options) ->
- {error, {eoptions,{not_a_proplist, Options}}}.
+ {error, {options,{not_a_proplist, Options}}}.
%%---------------------------------------------------------------
-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}.
%%
%% 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={Transport,_, _, _}}}},
+ How) when is_port(Listen) ->
+ Transport:shutdown(Listen, How);
shutdown(#sslsocket{pid = Pid}, How) ->
ssl_connection:shutdown(Pid, How).
@@ -396,11 +432,11 @@ shutdown(#sslsocket{pid = Pid}, How) ->
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
-sockname(#sslsocket{pid = {ListenSocket, _}}) ->
- inet:sockname(ListenSocket);
+sockname(#sslsocket{pid = {Listen, #config{cb={Transport,_, _, _}}}}) when is_port(Listen) ->
+ ssl_socket:sockname(Transport, Listen);
-sockname(#sslsocket{pid = Pid}) ->
- ssl_connection:sockname(Pid).
+sockname(#sslsocket{pid = Pid, fd = {Transport, Socket}}) when is_pid(Pid) ->
+ ssl_socket:sockname(Transport, Socket).
%%---------------------------------------------------------------
-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}.
@@ -408,12 +444,14 @@ 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.
%%--------------------------------------------------------------------
@@ -429,8 +467,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(),
@@ -439,10 +479,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.
@@ -462,26 +503,26 @@ format_error({error, Reason}) ->
format_error(Reason) when is_list(Reason) ->
Reason;
format_error(closed) ->
- "The connection is closed";
-format_error(ecacertfile) ->
- "Own CA certificate file is invalid.";
-format_error(ecertfile) ->
- "Own certificate file is invalid.";
-format_error(ekeyfile) ->
- "Own private key file is invalid.";
-format_error(esslaccept) ->
- "Server SSL handshake procedure between client and server failed.";
-format_error(esslconnect) ->
- "Client SSL handshake procedure between client and server failed.";
-format_error({eoptions, Options}) ->
- lists:flatten(io_lib:format("Error in options list: ~p~n", [Options]));
+ "TLS connection is closed";
+format_error({tls_alert, Description}) ->
+ "TLS Alert: " ++ Description;
+format_error({options,{FileType, File, Reason}}) when FileType == cacertfile;
+ FileType == certfile;
+ FileType == keyfile;
+ FileType == dhfile ->
+ Error = file_error_format(Reason),
+ file_desc(FileType) ++ File ++ ": " ++ Error;
+format_error({options, {socket_options, Option, Error}}) ->
+ lists:flatten(io_lib:format("Invalid transport socket option ~p: ~s", [Option, format_error(Error)]));
+format_error({options, {socket_options, Option}}) ->
+ lists:flatten(io_lib:format("Invalid socket option: ~p", [Option]));
+format_error({options, Options}) ->
+ lists:flatten(io_lib:format("Invalid TLS option: ~p", [Options]));
format_error(Error) ->
- case (catch inet:format_error(Error)) of
- "unkknown POSIX" ++ _ ->
- no_format(Error);
- {'EXIT', _} ->
- no_format(Error);
+ case inet:format_error(Error) of
+ "unknown POSIX" ++ _ ->
+ unexpected_format(Error);
Other ->
Other
end.
@@ -502,7 +543,6 @@ random_bytes(N) ->
crypto:rand_bytes(N)
end.
-
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -510,8 +550,8 @@ do_connect(Address, Port,
#config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts,
emulated=EmOpts,inet_ssl=SocketOpts},
Timeout) ->
- {CbModule, _, _, _} = CbInfo,
- try CbModule:connect(Address, Port, SocketOpts, Timeout) of
+ {Transport, _, _, _} = CbInfo,
+ try Transport:connect(Address, Port, SocketOpts, Timeout) of
{ok, Socket} ->
ssl_connection:connect(Address, Port, Socket, {SslOpts,EmOpts},
self(), CbInfo, Timeout);
@@ -519,11 +559,11 @@ do_connect(Address, Port,
{error, Reason}
catch
exit:{function_clause, _} ->
- {error, {eoptions, {cb_info, CbInfo}}};
+ {error, {options, {cb_info, CbInfo}}};
exit:badarg ->
- {error, {eoptions, {inet_options, UserOpts}}};
+ {error, {options, {socket_options, UserOpts}}};
exit:{badarg, _} ->
- {error, {eoptions, {inet_options, UserOpts}}}
+ {error, {options, {socket_options, UserOpts}}}
end.
handle_options(Opts0, _Role) ->
@@ -567,7 +607,7 @@ handle_options(Opts0, _Role) ->
{verify_peer, UserFailIfNoPeerCert,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
Value ->
- throw({error, {eoptions, {verify, Value}}})
+ throw({error, {options, {verify, Value}}})
end,
CertFile = handle_option(certfile, Opts, <<>>),
@@ -594,9 +634,13 @@ handle_options(Opts0, _Role) ->
reuse_sessions = handle_option(reuse_sessions, Opts, true),
secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
- debug = handle_option(debug, Opts, []),
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}),
@@ -604,8 +648,10 @@ handle_options(Opts0, _Role) ->
fail_if_no_peer_cert, verify_client_once,
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],
+ reuse_session, reuse_sessions, ssl_imp,
+ 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)
@@ -708,9 +754,9 @@ validate_option(ciphers, Value) when is_list(Value) ->
try cipher_suites(Version, Value)
catch
exit:_ ->
- throw({error, {eoptions, {ciphers, Value}}});
+ throw({error, {options, {ciphers, Value}}});
error:_->
- throw({error, {eoptions, {ciphers, Value}}})
+ throw({error, {options, {ciphers, Value}}})
end;
validate_option(reuse_session, Value) when is_function(Value) ->
Value;
@@ -724,18 +770,68 @@ validate_option(secure_renegotiate, Value) when Value == true;
validate_option(renegotiate_at, Value) when is_integer(Value) ->
erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT);
-validate_option(debug, Value) when is_list(Value); Value == true ->
- Value;
validate_option(hibernate_after, undefined) ->
undefined;
validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
Value;
-validate_option(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, {options, {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, {options, {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, {options, {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}}}).
-
+ throw({error, {options, {Opt, Value}}}).
+
+validate_npn_ordering(client) ->
+ ok;
+validate_npn_ordering(server) ->
+ ok;
+validate_npn_ordering(Value) ->
+ throw({error, {options, {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, {options, {Opt, {invalid_protocol, Bin}}}})
+ end, List).
+
validate_versions([], Versions) ->
Versions;
validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
@@ -744,23 +840,23 @@ validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
Version == sslv3 ->
validate_versions(Rest, Versions);
validate_versions([Ver| _], Versions) ->
- throw({error, {eoptions, {Ver, {versions, Versions}}}}).
+ throw({error, {options, {Ver, {versions, Versions}}}}).
validate_inet_option(mode, Value)
when Value =/= list, Value =/= binary ->
- throw({error, {eoptions, {mode,Value}}});
+ throw({error, {options, {mode,Value}}});
validate_inet_option(packet, Value)
when not (is_atom(Value) orelse is_integer(Value)) ->
- throw({error, {eoptions, {packet,Value}}});
+ throw({error, {options, {packet,Value}}});
validate_inet_option(packet_size, Value)
when not is_integer(Value) ->
- throw({error, {eoptions, {packet_size,Value}}});
+ throw({error, {options, {packet_size,Value}}});
validate_inet_option(header, Value)
when not is_integer(Value) ->
- throw({error, {eoptions, {header,Value}}});
+ throw({error, {options, {header,Value}}});
validate_inet_option(active, Value)
when Value =/= true, Value =/= false, Value =/= once ->
- throw({error, {eoptions, {active,Value}}});
+ throw({error, {options, {active,Value}}});
validate_inet_option(_, _) ->
ok.
@@ -839,16 +935,61 @@ cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
cipher_suites(Version, Ciphers).
-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.
+unexpected_format(Error) ->
+ lists:flatten(io_lib:format("Unexpected error: ~p", [Error])).
+
+file_error_format({error, Error})->
+ case file:format_error(Error) of
+ "unknown POSIX error" ->
+ "decoding error";
+ Str ->
+ Str
+ end;
+file_error_format(_) ->
+ "decoding error".
+
+file_desc(cacertfile) ->
+ "Invalid CA certificate file ";
+file_desc(certfile) ->
+ "Invalid certificate file ";
+file_desc(keyfile) ->
+ "Invalid key file ";
+file_desc(dhfile) ->
+ "Invalid DH params file ".
+
+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 222b3f1ad7..94e95d3cd3 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.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
@@ -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).
+ {tls_alert, description_txt(Description)}.
%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index 67d00f0da7..ff36b5ee26 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-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
@@ -18,10 +18,11 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: Storage for trused certificats
+%% Purpose: Storage for trusted certificates
%%----------------------------------------------------------------------
-module(ssl_certificate_db).
+
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
-include_lib("kernel/include/file.hrl").
@@ -37,7 +38,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec create() -> [db_handle()].
+-spec create() -> [db_handle(),...].
%%
%% Description: Creates a new certificate db.
%% Note: lookup_trusted_cert/4 may be called from any process but only
@@ -54,7 +55,7 @@ create() ->
].
%%--------------------------------------------------------------------
--spec remove([db_handle()]) -> term().
+-spec remove([db_handle()]) -> ok.
%%
%% Description: Removes database db
%%--------------------------------------------------------------------
@@ -114,8 +115,8 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) ->
new_trusted_cert_entry({MD5, File}, Db)
end.
%%--------------------------------------------------------------------
--spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term().
--spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term().
+-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> {ok, term()}.
+-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> {ok, term()}.
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
@@ -131,19 +132,25 @@ cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) ->
insert(MD5, {Content, Ref}, PemChache),
{ok, Content}.
+%%--------------------------------------------------------------------
+-spec remove_trusted_certs(reference(), db_handle()) -> ok.
+%%
+%% Description: Removes all trusted certificates refernced by <Ref>.
+%%--------------------------------------------------------------------
remove_trusted_certs(Ref, CertsDb) ->
remove_certs(Ref, CertsDb).
%%--------------------------------------------------------------------
--spec remove(term(), db_handle()) -> term().
+-spec remove(term(), db_handle()) -> ok.
%%
%% Description: Removes an element in a <Db>.
%%--------------------------------------------------------------------
remove(Key, Db) ->
- _ = ets:delete(Db, Key).
+ ets:delete(Db, Key),
+ ok.
%%--------------------------------------------------------------------
--spec lookup(term(), db_handle()) -> term() | undefined.
+-spec lookup(term(), db_handle()) -> [term()] | undefined.
%%
%% Description: Looks up an element in a <Db>.
%%--------------------------------------------------------------------
@@ -158,7 +165,7 @@ lookup(Key, Db) ->
[Pick(Data) || Data <- Contents]
end.
%%--------------------------------------------------------------------
--spec foldl(fun(), term(), db_handle()) -> term().
+-spec foldl(fun((_,_) -> term()), term(), db_handle()) -> term().
%%
%% Description: Calls Fun(Elem, AccIn) on successive elements of the
%% cache, starting with AccIn == Acc0. Fun/2 must return a new
@@ -178,12 +185,13 @@ ref_count(Key, Db, N) ->
ets:update_counter(Db,Key,N).
%%--------------------------------------------------------------------
--spec clear(db_handle()) -> term().
+-spec clear(db_handle()) -> ok.
%%
%% Description: Clears the cache
%%--------------------------------------------------------------------
clear(Db) ->
- ets:delete_all_objects(Db).
+ true = ets:delete_all_objects(Db),
+ ok.
%%--------------------------------------------------------------------
-spec db_size(db_handle()) -> integer().
@@ -194,30 +202,35 @@ db_size(Db) ->
ets:info(Db, size).
%%--------------------------------------------------------------------
-%%-spec insert(Key::term(), Data::term(), Db::db_handle()) -> no_return().
+-spec insert(Key::term(), Data::term(), Db::db_handle()) -> ok.
%%
%% Description: Inserts data into <Db>
%%--------------------------------------------------------------------
insert(Key, Data, Db) ->
- true = ets:insert(Db, {Key, Data}).
+ true = ets:insert(Db, {Key, Data}),
+ ok.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
update_counter(Key, Count, Db) ->
- true = ets:insert(Db, {Key, Count}).
+ true = ets:insert(Db, {Key, Count}),
+ ok.
remove_certs(Ref, CertsDb) ->
- ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
+ true = ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}),
+ ok.
add_certs_from_der(DerList, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
- [Add(Cert) || Cert <- DerList].
+ [Add(Cert) || Cert <- DerList],
+ ok.
add_certs_from_pem(PemEntries, Ref, CertsDb) ->
Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
- [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries].
-
+ [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries],
+ ok.
+
add_certs(Cert, Ref, CertsDb) ->
try ErlCert = public_key:pkix_decode_cert(Cert, otp),
TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index d4784604fd..8f4fd88d42 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]).
@@ -93,7 +92,9 @@
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,
@@ -177,10 +178,10 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
%%
%% Description: Set the ssl process to own the accept socket
%%--------------------------------------------------------------------
-socket_control(Socket, Pid, CbModule) ->
- case CbModule:controlling_process(Socket, Pid) of
+socket_control(Socket, Pid, Transport) ->
+ case Transport:controlling_process(Socket, Pid) of
ok ->
- {ok, sslsocket(Pid)};
+ {ok, ssl_socket:socket(Pid, Transport, Socket)};
{error, Reason} ->
{error, Reason}
end.
@@ -214,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()}.
%%
@@ -375,16 +371,28 @@ hello(#server_hello{cipher_suite = CipherSuite,
renegotiation = {Renegotiation, _},
ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- {Version, NewId, ConnectionStates} ->
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, 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 ->
@@ -392,13 +400,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, {shutdown, own_alert}, 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, _},
@@ -407,8 +412,8 @@ 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});
@@ -584,6 +589,7 @@ certify(#client_key_exchange{exchange_keys = Keys},
handle_own_alert(Alert, Version, certify, State)
end;
+
certify(timeout, State) ->
{ next_state, certify, State, hibernate };
@@ -650,6 +656,12 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
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,
@@ -670,6 +682,13 @@ cipher(#finished{verify_data = Data} = Finished,
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 };
@@ -829,25 +848,22 @@ handle_sync_event({new_user, User}, _From, StateName,
handle_sync_event({get_opts, OptTags}, _From, StateName,
#state{socket = Socket,
+ transport_cb = Transport,
socket_options = SockOpts} = State) ->
- OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []),
+ OptsReply = get_socket_opts(Transport, 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,
socket = Socket,
+ transport_cb = Transport,
user_data_buffer = Buffer} = State0) ->
- {Reply, Opts} = set_socket_opts(Socket, Opts0, Opts1, []),
+ {Reply, Opts} = set_socket_opts(Transport, Socket, Opts0, Opts1, []),
State1 = State0#state{socket_options = Opts},
if
Opts#socket_options.active =:= false ->
@@ -968,9 +984,10 @@ handle_info({CloseTag, Socket}, StateName,
{stop, {shutdown, transport_closed}, State};
handle_info({ErrorTag, Socket, econnaborted}, StateName,
- #state{socket = Socket, start_or_recv_from = StartFrom, role = Role,
+ #state{socket = Socket, transport_cb = Transport,
+ 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(Transport, Socket, StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
{stop, normal, State};
handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,
@@ -1119,9 +1136,8 @@ init_certificates(#ssl_options{cacerts = CaCerts,
end,
{ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role)
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile,
- erlang:get_stacktrace())
+ _:Reason ->
+ file_error(CACertFile, {cacertfile, Reason})
end,
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role).
@@ -1141,9 +1157,8 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan
[OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CertFile, ecertfile,
- erlang:get_stacktrace())
+ _:Reason ->
+ file_error(CertFile, {certfile, Reason})
end;
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) ->
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}.
@@ -1160,9 +1175,8 @@ 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,
- erlang:get_stacktrace())
+ _:Reason ->
+ file_error(KeyFile, {keyfile, Reason})
end;
%% First two clauses are for backwards compatibility
@@ -1188,18 +1202,14 @@ private_key(#'PrivateKeyInfo'{privateKeyAlgorithm =
private_key(Key) ->
Key.
--spec(handle_file_error(_,_,_,_,_,_) -> no_return()).
-handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) ->
- file_error(Line, Error, Reason, File, Throw, Stack);
-handle_file_error(Line, Error, Reason, File, Throw, Stack) ->
- file_error(Line, Error, Reason, File, Throw, Stack).
-
--spec(file_error(_,_,_,_,_,_) -> no_return()).
-file_error(Line, Error, Reason, File, Throw, Stack) ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [Line, Error, Reason, File, Stack]),
- error_logger:error_report(Report),
- throw(Throw).
+-spec(file_error(_,_) -> no_return()).
+file_error(File, Throw) ->
+ case Throw of
+ {Opt,{badmatch, {error, {badmatch, Error}}}} ->
+ throw({options, {Opt, binary_to_list(File), Error}});
+ _ ->
+ throw(Throw)
+ end.
init_diffie_hellman(_,Params, _,_) when is_binary(Params)->
public_key:der_decode('DHParameter', Params);
@@ -1217,9 +1227,8 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) ->
?DEFAULT_DIFFIE_HELLMAN_PARAMS
end
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason,
- DHParamFile, edhfile, erlang:get_stacktrace())
+ _:Reason ->
+ file_error(DHParamFile, {dhfile, Reason})
end.
sync_send_all_state_event(FsmPid, Event) ->
@@ -1291,17 +1300,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);
@@ -1551,12 +1561,33 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
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,
@@ -1591,78 +1622,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,
- hashsign = HashSign},
- #state{negotiated_version = Version,
- public_key_info = PubKeyInfo,
- connection_states = ConnectionStates} = State) ->
-
- PLen = size(P),
- GLen = size(G),
- YLen = size(ServerPublicDhKey),
- HashAlgo = connection_hash_algo(HashSign, State),
+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(HashAlgo,
- <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen),
- ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of
+ <<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({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams})
- when Minor >= 3 ->
- public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey);
-verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) ->
- case public_key:decrypt_public(Signed, PubKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes ->
- true;
- _ ->
- false
- end;
-verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) ->
- public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}).
+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(Prime, Base, PublicDhKey, undefined, State) ->
- PMpint = mpint_binary(Prime),
- GMpint = mpint_binary(Base),
- Keys = {_, PrivateDhKey} =
- crypto:dh_generate_key([PMpint,GMpint]),
- dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys});
-
-dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey,
- #state{session = Session,
- negotiated_version = Version, role = Role,
- connection_states = ConnectionStates0} = State) ->
- PremasterSecret =
- crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
- [PMpint, GMpint]),
+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} ->
@@ -1674,6 +1676,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,
@@ -1741,6 +1756,8 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
end.
read_application_data(Data, #state{user_application = {_Mon, Pid},
+ socket = Socket,
+ transport_cb = Transport,
socket_options = SOpts,
bytes_to_read = BytesToRead,
start_or_recv_from = RecvFrom,
@@ -1753,7 +1770,7 @@ 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(Transport, Socket, SOpts, ClientData, Pid, RecvFrom),
cancel_timer(Timer),
State = State0#state{user_data_buffer = Buffer,
start_or_recv_from = undefined,
@@ -1774,7 +1791,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
{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(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom),
{stop, normal, State0}
end.
@@ -1856,9 +1873,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(Transport, Socket, SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_reply(Transport, Socket, SOpts, Data)),
SO = case Data of
{P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
((Type =:= http) or (Type =:= http_bin)) ->
@@ -1877,31 +1894,31 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
SO
end.
-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,
+format_reply(_, _,#socket_options{active = false, mode = Mode, packet = Packet,
header = Header}, Data) ->
- {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}.
+ {ok, do_format_reply(Mode, Packet, Header, Data)};
+format_reply(Transport, Socket, #socket_options{active = _, mode = Mode, packet = Packet,
+ header = Header}, Data) ->
+ {ssl, ssl_socket:socket(self(), Transport, 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(Transport, Socket, SO= #socket_options{active = Active}, Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_packet_error(Transport, 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(Transport, Socket, #socket_options{active = _, mode = Mode}, Data) ->
+ {ssl_error, ssl_socket:socket(self(), Transport, 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, <<>>) ->
@@ -2016,8 +2033,9 @@ next_tls_record(Data, #state{tls_record_buffer = Buf0,
Alert
end.
-next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket} = State) ->
- inet:setopts(Socket, [{active,once}]),
+next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket,
+ transport_cb = Transport} = State) ->
+ ssl_socket:setopts(Transport, Socket, [{active,once}]),
{no_record, State};
next_record(#state{tls_packets = [], tls_cipher_texts = [CT | Rest],
connection_states = ConnStates0} = State) ->
@@ -2072,8 +2090,8 @@ next_state_is_connection(_, State =
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_history = ssl_handshake:init_handshake_history()}).
+ 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},
@@ -2130,64 +2148,58 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
send_queue = queue:new()
}.
-sslsocket(Pid) ->
- #sslsocket{pid = Pid, fd = new_ssl}.
-
-sslsocket() ->
- sslsocket(self()).
-
-get_socket_opts(_,[], _, Acc) ->
+get_socket_opts(_,_,[], _, Acc) ->
{ok, Acc};
-get_socket_opts(Socket, [mode | Tags], SockOpts, Acc) ->
- get_socket_opts(Socket, Tags, SockOpts,
+get_socket_opts(Transport, Socket, [mode | Tags], SockOpts, Acc) ->
+ get_socket_opts(Transport, Socket, Tags, SockOpts,
[{mode, SockOpts#socket_options.mode} | Acc]);
-get_socket_opts(Socket, [packet | Tags], SockOpts, Acc) ->
+get_socket_opts(Transport, Socket, [packet | Tags], SockOpts, Acc) ->
case SockOpts#socket_options.packet of
{Type, headers} ->
- get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]);
+ get_socket_opts(Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc]);
Type ->
- get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc])
+ get_socket_opts(Transport, Socket, Tags, SockOpts, [{packet, Type} | Acc])
end;
-get_socket_opts(Socket, [header | Tags], SockOpts, Acc) ->
- get_socket_opts(Socket, Tags, SockOpts,
+get_socket_opts(Transport, Socket, [header | Tags], SockOpts, Acc) ->
+ get_socket_opts(Transport, Socket, Tags, SockOpts,
[{header, SockOpts#socket_options.header} | Acc]);
-get_socket_opts(Socket, [active | Tags], SockOpts, Acc) ->
- get_socket_opts(Socket, Tags, SockOpts,
+get_socket_opts(Transport, Socket, [active | Tags], SockOpts, Acc) ->
+ get_socket_opts(Transport, Socket, Tags, SockOpts,
[{active, SockOpts#socket_options.active} | Acc]);
-get_socket_opts(Socket, [Tag | Tags], SockOpts, Acc) ->
- try inet:getopts(Socket, [Tag]) of
+get_socket_opts(Transport, Socket, [Tag | Tags], SockOpts, Acc) ->
+ try ssl_socket:getopts(Transport, Socket, [Tag]) of
{ok, [Opt]} ->
- get_socket_opts(Socket, Tags, SockOpts, [Opt | Acc]);
+ get_socket_opts(Transport, Socket, Tags, SockOpts, [Opt | Acc]);
{error, Error} ->
- {error, {eoptions, {inet_option, Tag, Error}}}
+ {error, {options, {socket_options, Tag, Error}}}
catch
%% So that inet behavior does not crash our process
- _:Error -> {error, {eoptions, {inet_option, Tag, Error}}}
+ _:Error -> {error, {options, {socket_options, Tag, Error}}}
end;
-get_socket_opts(_,Opts, _,_) ->
- {error, {eoptions, {inet_option, Opts, function_clause}}}.
+get_socket_opts(_, _,Opts, _,_) ->
+ {error, {options, {socket_options, Opts, function_clause}}}.
-set_socket_opts(_, [], SockOpts, []) ->
+set_socket_opts(_,_, [], SockOpts, []) ->
{ok, SockOpts};
-set_socket_opts(Socket, [], SockOpts, Other) ->
+set_socket_opts(Transport, Socket, [], SockOpts, Other) ->
%% Set non emulated options
- try inet:setopts(Socket, Other) of
+ try ssl_socket:setopts(Transport, Socket, Other) of
ok ->
{ok, SockOpts};
{error, InetError} ->
- {{error, {eoptions, {inet_options, Other, InetError}}}, SockOpts}
+ {{error, {options, {socket_options, Other, InetError}}}, SockOpts}
catch
_:Error ->
%% So that inet behavior does not crash our process
- {{error, {eoptions, {inet_options, Other, Error}}}, SockOpts}
+ {{error, {options, {socket_options, Other, Error}}}, SockOpts}
end;
-set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary ->
- set_socket_opts(Socket, Opts,
+set_socket_opts(Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary ->
+ set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{mode = Mode}, Other);
-set_socket_opts(_, [{mode, _} = Opt| _], SockOpts, _) ->
- {{error, {eoptions, {inet_opt, Opt}}}, SockOpts};
-set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw;
+set_socket_opts(_, _, [{mode, _} = Opt| _], SockOpts, _) ->
+ {{error, {options, {socket_options, Opt}}}, SockOpts};
+set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw;
Packet == 0;
Packet == 1;
Packet == 2;
@@ -2202,24 +2214,24 @@ set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet =
Packet == httph;
Packet == http_bin;
Packet == httph_bin ->
- set_socket_opts(Socket, Opts,
+ set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
-set_socket_opts(_, [{packet, _} = Opt| _], SockOpts, _) ->
- {{error, {eoptions, {inet_opt, Opt}}}, SockOpts};
-set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) ->
- set_socket_opts(Socket, Opts,
+set_socket_opts(_, _, [{packet, _} = Opt| _], SockOpts, _) ->
+ {{error, {options, {socket_options, Opt}}}, SockOpts};
+set_socket_opts(Transport, Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) ->
+ set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{header = Header}, Other);
-set_socket_opts(_, [{header, _} = Opt| _], SockOpts, _) ->
- {{error,{eoptions, {inet_opt, Opt}}}, SockOpts};
-set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once;
- Active == true;
- Active == false ->
- set_socket_opts(Socket, Opts,
+set_socket_opts(_, _, [{header, _} = Opt| _], SockOpts, _) ->
+ {{error,{options, {socket_options, Opt}}}, SockOpts};
+set_socket_opts(Transport, Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once;
+ Active == true;
+ Active == false ->
+ set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{active = Active}, Other);
-set_socket_opts(_, [{active, _} = Opt| _], SockOpts, _) ->
- {{error, {eoptions, {inet_opt, Opt}} }, SockOpts};
-set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) ->
- set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]).
+set_socket_opts(_, _, [{active, _} = Opt| _], SockOpts, _) ->
+ {{error, {options, {socket_options, Opt}} }, SockOpts};
+set_socket_opts(Transport, Socket, [Opt | Opts], SockOpts, Other) ->
+ set_socket_opts(Transport, Socket, Opts, SockOpts, [Opt | Other]).
handle_alerts([], Result) ->
Result;
@@ -2230,12 +2242,13 @@ 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, transport_cb = Transport,
+ 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(Transport, Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
@@ -2262,28 +2275,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(Transport, Socket, connection, Opts, Pid, From, Alert, Role) ->
+ alert_user(Transport,Socket, Opts#socket_options.active, Pid, From, Alert, Role);
+alert_user(Transport, Socket,_, _, _, From, Alert, Role) ->
+ alert_user(Transport, Socket, From, Alert, Role).
-alert_user(From, Alert, Role) ->
- alert_user(false, no_pid, From, Alert, Role).
+alert_user(Transport, Socket, From, Alert, Role) ->
+ alert_user(Transport, Socket, false, no_pid, From, Alert, Role).
-alert_user(false = Active, Pid, From, Alert, Role) ->
+alert_user(_,_, 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(Transport, 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, ssl_socket:socket(self(), Transport, Socket)});
ReasonCode ->
send_or_reply(Active, Pid, From,
- {ssl_error, sslsocket(), ReasonCode})
+ {ssl_error, ssl_socket:socket(self(), Transport, Socket), ReasonCode})
end.
log_alert(true, Info, Alert) ->
@@ -2313,13 +2326,18 @@ handle_own_alert(Alert, Version, StateName,
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,
+ transport_cb = Transport,
+ start_or_recv_from = StartFrom,
+ role = Role, renegotiation = {false, first}}) ->
+ alert_user(Transport, Socket, StartFrom, Alert, Role);
-handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts,
+handle_normal_shutdown(Alert, StateName, #state{socket = Socket,
+ socket_options = Opts,
+ transport_cb = Transport,
user_application = {_Mon, Pid},
start_or_recv_from = RecvFrom, role = Role}) ->
- alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role).
+ alert_user(Transport, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role).
handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
@@ -2403,7 +2421,7 @@ workaround_transport_delivery_problems(Socket, gen_tcp = Transport) ->
%% 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}]),
+ ssl_socket:setopts(Transport, Socket, [{active, false}]),
Transport:shutdown(Socket, write),
%% Will return when other side has closed or after 30 s
%% e.g. we do not want to hang if something goes wrong
@@ -2447,10 +2465,10 @@ 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_hash_algo({HashAlgo, _}, _State) ->
- HashAlgo;
-connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) ->
- HashAlgo.
+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
@@ -2488,15 +2506,16 @@ default_hashsign(_Version, KeyExchange)
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}).
+ erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).
cancel_timer(undefined) ->
ok;
cancel_timer(Timer) ->
- erlang:cancel_timer(Timer).
+ erlang:cancel_timer(Timer),
+ ok.
handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
- inet:setopts(Socket, [{active, false}]),
+ ssl_socket:setopts(Transport, Socket, [{active, false}]),
case Transport:recv(Socket, 0, 0) of
{error, closed} ->
ok;
diff --git a/lib/ssl/src/ssl_debug.erl b/lib/ssl/src/ssl_debug.erl
deleted file mode 100644
index 625889c43b..0000000000
--- a/lib/ssl/src/ssl_debug.erl
+++ /dev/null
@@ -1,99 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-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 : some debug utilities
-
--module(ssl_debug).
-
--export([unhex/1, hexd/1, hex_data/2, term_data/2, hex_data/4, term_data/4, make_binary/1]).
-
-%% external
-
-hex_data(Name, Data) ->
- io:format("~s\n~s", [Name, hex(Data)]).
-
-term_data(Name, Term) ->
- io:format("~s\n~p\n", [Name, Term]).
-
-hex_data(Name, Data, Mod, Line) ->
- io:format("~w:~p ~s\n~s", [Mod, Line, Name, hex(Data)]).
-
-term_data(Name, Term, Mod, Line) ->
- io:format("~w:~p ~s\n~p\n", [Mod, Line, Name, Term]).
-
-unhex(S) ->
- Lines = string:tokens(S, "\n"),
- H = [unhex(L, []) || L <- Lines],
- list_to_binary(H).
-
-make_binary(Size) ->
- crypto:rand_bytes(Size).
-
-%% internal
-
-is_hex_digit(C) when C >= $0, C =< $9 -> true;
-is_hex_digit(C) when C >= $A, C =< $F -> true;
-is_hex_digit(C) when C >= $a, C =< $f -> true;
-is_hex_digit(_) -> false.
-
-unhex([], Acc) ->
- list_to_binary(lists:reverse(Acc));
-unhex([_], Acc) ->
- unhex([], Acc);
-unhex([$ | Tl], Acc) ->
- unhex(Tl, Acc);
-unhex([D1, D2 | Tl], Acc) ->
- case {is_hex_digit(D1), is_hex_digit(D2)} of
- {true, true} ->
- unhex(Tl, [erlang:list_to_integer([D1, D2], 16) | Acc]);
- _ ->
- unhex([], Acc)
- end.
-
-hexd(B) ->
- io:format("~s\n", [hex(B)]).
-
-hex(B) -> hex(erlang:iolist_to_binary(B), []).
-
-hex_asc(B) ->
- L = binary_to_list(B),
- {hexify(L), asciify(L)}.
-
-hex(<<B:16/binary, Rest/binary>>, Acc) ->
- {HS, AS} = hex_asc(B),
- hex(Rest, ["\n", AS, " ", HS | Acc]);
-hex(<<>>, Acc) ->
- lists:reverse(Acc);
-hex(B, Acc) ->
- {HS, AS} = hex_asc(B),
- L = erlang:iolist_size(HS),
- lists:flatten(lists:reverse(Acc, [HS, lists:duplicate(3*16 - L, $ ), " ", AS, "\n"])).
-
-hexify(L) -> [[hex_byte(B), " "] || B <- L].
-
-hex_byte(B) when B < 16#10 -> ["0", erlang:integer_to_list(B, 16)];
-hex_byte(B) -> erlang:integer_to_list(B, 16).
-
-asciify(L) -> [ascii_byte(C) || C <- L].
-
-ascii_byte($") -> $.;
-ascii_byte(C) when C < 32; C >= 127 -> $.;
-ascii_byte(C) -> C.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index bb26302fff..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/6, certificate_verify/6,
+ 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, server_hello_done/0,
+ 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]).
+ 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,18 +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),
- hash_signs = default_hash_signs()
+ 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,
@@ -98,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)
}.
%%--------------------------------------------------------------------
@@ -113,20 +127,21 @@ 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,
- hash_signs = _HashSigns},
- #ssl_options{secure_renegotiate = SecureRenegotation},
+ hash_signs = _HashSigns} = Hello,
+ #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector},
ConnectionStates0, Renegotiation) ->
-%%TODO: select hash and signature algorigthm
+ %%TODO: select hash and signature algorigthm
case ssl_record:is_acceptable_version(Version) of
true ->
case handle_renegotiation_info(client, Info, ConnectionStates0,
@@ -135,7 +150,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
ConnectionStates =
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;
@@ -145,9 +165,8 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
hello(#client_hello{client_version = ClientVersion, random = Random,
cipher_suites = CipherSuites,
- renegotiation_info = Info,
- hash_signs = _HashSigns} = Hello,
- #ssl_options{versions = Versions,
+ renegotiation_info = Info} = Hello,
+ #ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
%% TODO: select hash and signature algorithm
@@ -173,7 +192,12 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
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
@@ -296,25 +320,36 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version,
- {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
- Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of
+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', PublicKey, PublicKeyParams}, Version,
- {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
- Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case public_key:verify({digest, Hashes}, sha, 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()) ->
@@ -358,31 +393,33 @@ key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
- {HashAlgo, SignAlgo}, 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},
+ 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_exchange{params = ServerDHParams,
- signed_params = <<>>,
- hashsign = {null, anon}};
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {null, anon},
+ signature = <<>>};
_ ->
Hash =
server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), PBin/binary,
- ?UINT16(GLen), GBin/binary,
- ?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
- #server_key_exchange{params = ServerDHParams,
- signed_params = Signed,
- hashsign = {HashAlgo, SignAlgo}}
+ 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.
%%--------------------------------------------------------------------
@@ -427,6 +464,11 @@ 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, integer(), binary(), tls_handshake_history()) ->
#finished{}.
@@ -494,6 +536,15 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
+-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().
%%
@@ -660,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)};
@@ -816,17 +918,21 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(_Version, ?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(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
+ ?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,
@@ -839,20 +945,22 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
Extensions/binary>>) ->
- HelloExtensions = dec_hello_extensions(Extensions),
- RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions,
- undefined),
- HashSigns = proplists:get_value(hash_signs, HelloExtensions,
- undefined),
+
+ 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
- };
+ 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,
@@ -868,7 +976,7 @@ dec_hs(_Version, ?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>>) ->
HelloExtensions = dec_hello_extensions(Extensions, []),
@@ -876,6 +984,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
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,
@@ -883,34 +993,12 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
renegotiation_info = RenegotiationInfo,
- hash_signs = HashSigns};
+ 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(_Version, ?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 = <<>>, hashsign = {null, anon}};
-dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?BYTE(HashAlgo), ?BYTE(SignAlgo),
- ?UINT16(Len), Sig:Len/binary>>)
- when Major == 3, Minor >= 3 ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = Sig,
- hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}};
-dec_hs(_Version, ?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, hashsign = undefined};
+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,
@@ -950,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>>) ->
@@ -959,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
@@ -982,6 +1109,7 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
%% 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.
@@ -1014,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},
@@ -1022,19 +1155,21 @@ enc_hs(#client_hello{client_version = {Major, Minor},
cipher_suites = CipherSuites,
compression_methods = CompMethods,
renegotiation_info = RenegotiationInfo,
- hash_signs = HashSigns}, _Version) ->
+ 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),
- Extensions0 = hello_extensions(RenegotiationInfo),
+ 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,
+
+ {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
?UINT16(CsLength), BinCipherSuites/binary,
?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
@@ -1044,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,
@@ -1055,18 +1191,12 @@ 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, hashsign = HashSign}, Version) ->
- PLen = byte_size(P),
- GLen = byte_size(G),
- YLen = byte_size(Y),
- Signature = enc_sign(HashSign, SignedParams, Version),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen), Y/binary,
- Signature/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},
@@ -1110,6 +1240,14 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
<<?UINT16(Len), DHPublic/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>>.
+
+enc_sign({_, anon}, _Sign, _Version) ->
+ <<>>;
enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
when Major == 3, Minor >= 3->
SignLen = byte_size(Signature),
@@ -1119,8 +1257,9 @@ enc_sign(_HashSign, Sign, _Version) ->
SignLen = byte_size(Sign),
<<?UINT16(SignLen), Sign/binary>>.
-hello_extensions(undefined) ->
- [];
+hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
+ hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
+
%% Renegotiation info
hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
[];
@@ -1129,6 +1268,11 @@ hello_extensions(#renegotiation_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) ->
enc_hello_extensions(Extensions, <<>>).
enc_hello_extensions([], <<>>) ->
@@ -1137,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>>);
@@ -1151,8 +1298,15 @@ enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest],
{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>>).
+ 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, []).
@@ -1211,8 +1365,8 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
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}, sha, 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}]).
@@ -1261,19 +1415,6 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
{unknown, {SslState, UserState}}
end.
-certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor})
- when Major == 3, Minor >= 3 ->
- public_key:verify({digest, Hashes}, sha, Signature, PublicKey);
-certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor})
- when Major == 3, Minor >= 3 ->
- public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey);
-certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) ->
- case public_key:decrypt_public(Signature, PublicKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes -> true;
- _ -> false
- end.
-
-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
@@ -1284,6 +1425,7 @@ default_hash_signs() ->
[?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 cc17dc2975..2414d5b666 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -33,6 +33,8 @@
-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).
@@ -97,7 +99,8 @@
cipher_suites, % cipher_suites<2..2^16-1>
compression_methods, % compression_methods<1..2^8-1>,
renegotiation_info,
- hash_signs % supported combinations of hashes/signature algos
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
-record(server_hello, {
@@ -107,7 +110,8 @@
cipher_suite, % cipher_suites
compression_method, % compression_method
renegotiation_info,
- hash_signs % supported combinations of hashes/signature algos
+ hash_signs, % supported combinations of hashes/signature algos
+ next_protocol_negotiation = undefined % [binary()]
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -137,9 +141,14 @@
}).
-record(server_key_exchange, {
+ exchange_keys
+ }).
+
+-record(server_key_params, {
params, %% #server_rsa_params{} | #server_dh_params{}
- signed_params, %% #signature{}
- hashsign %% term(atom(), atom())
+ params_bin,
+ hashsign, %% term(atom(), atom())
+ signature %% #signature{}
}).
%% enum { anonymous, rsa, dsa } SignatureAlgorithm;
@@ -234,6 +243,18 @@
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 b8f2ae3b51..ed0dc34adf 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -69,8 +69,8 @@
-define(TRUE, 0).
-define(FALSE, 1).
--define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16
-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]).
+-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
-record(ssl_options, {
versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3
@@ -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 0cf4f2ce33..aa9da65bb8 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,
@@ -144,8 +142,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
new_session_id(Port) ->
call({new_session_id, Port}).
+%%--------------------------------------------------------------------
+-spec clean_cert_db(reference(), binary()) -> ok.
+%%
+%% 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}),
+ ok.
%%--------------------------------------------------------------------
-spec register_session(inet:port_number(), #session{}) -> ok.
@@ -322,19 +327,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(PemCache, MD5) 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};
@@ -347,7 +345,7 @@ handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
--spec terminate(reason(), #state{}) -> term().
+-spec terminate(reason(), #state{}) -> ok.
%%
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
@@ -466,3 +464,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 8e93ce4634..173b9611c6 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -463,10 +463,9 @@ supported_protocol_versions() ->
supported_protocol_versions([]) ->
Vsns = case sufficient_tlsv1_2_crypto_support() of
true ->
- %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16
- ?DEFAULT_SUPPORTED_VERSIONS;
+ ?ALL_SUPPORTED_VERSIONS;
false ->
- ?DEFAULT_SUPPORTED_VERSIONS
+ ?MIN_SUPPORTED_VERSIONS
end,
application:set_env(ssl, protocol_version, Vsns),
Vsns;
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_socket.erl b/lib/ssl/src/ssl_socket.erl
new file mode 100644
index 0000000000..4778db2333
--- /dev/null
+++ b/lib/ssl/src/ssl_socket.erl
@@ -0,0 +1,35 @@
+-module(ssl_socket).
+
+-include("ssl_internal.hrl").
+
+-export([socket/3, setopts/3, getopts/3, peername/2, sockname/2, port/2]).
+
+socket(Pid, Transport, Socket) ->
+ #sslsocket{pid = Pid,
+ %% "The name "fd" is keept for backwards compatibility
+ fd = {Transport, Socket}}.
+
+setopts(gen_tcp, Socket, Options) ->
+ inet:setopts(Socket, Options);
+setopts(Transport, Socket, Options) ->
+ Transport:setopts(Socket, Options).
+
+getopts(gen_tcp, Socket, Options) ->
+ inet:getopts(Socket, Options);
+getopts(Transport, Socket, Options) ->
+ Transport:getopts(Socket, Options).
+
+peername(gen_tcp, Socket) ->
+ inet:peername(Socket);
+peername(Transport, Socket) ->
+ Transport:peername(Socket).
+
+sockname(gen_tcp, Socket) ->
+ inet:sockname(Socket);
+sockname(Transport, Socket) ->
+ Transport:sockname(Socket).
+
+port(gen_tcp, Socket) ->
+ inet:port(Socket);
+port(Transport, Socket) ->
+ Transport:port(Socket).
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index a8476b104f..a22af6b960 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -89,7 +89,7 @@ handle_call({connect, Ip, Port}, {From, _}, State) ->
ok ->
flush_old_controller(From, Socket),
{reply, Res, State}
- end;
+ end;
{Pid, Error} ->
{reply, Error, State}
end;
@@ -115,13 +115,13 @@ code_change(_OldVsn, St, _Extra) ->
get_tcp_address(Socket) ->
case inet:sockname(Socket) of
{ok, Address} ->
- {ok, Host} = inet:gethostname(),
+ {ok, Host} = inet:gethostname(),
NetAddress = #net_address{
- address = Address,
- host = Host,
- protocol = proxy,
- family = inet
- },
+ address = Address,
+ host = Host,
+ protocol = proxy,
+ family = inet
+ },
{ok, NetAddress};
{error, _} = Error -> Error
end.
@@ -129,17 +129,17 @@ get_tcp_address(Socket) ->
accept_loop(Proxy, erts = Type, Listen, Extra) ->
process_flag(priority, max),
case gen_tcp:accept(Listen) of
- {ok, Socket} ->
- Extra ! {accept,self(),Socket,inet,proxy},
- receive
- {_Kernel, controller, Pid} ->
- ok = gen_tcp:controlling_process(Socket, Pid),
- flush_old_controller(Pid, Socket),
- Pid ! {self(), controller};
- {_Kernel, unsupported_protocol} ->
- exit(unsupported_protocol)
- end;
- Error ->
+ {ok, Socket} ->
+ Extra ! {accept,self(),Socket,inet,proxy},
+ receive
+ {_Kernel, controller, Pid} ->
+ ok = gen_tcp:controlling_process(Socket, Pid),
+ flush_old_controller(Pid, Socket),
+ Pid ! {self(), controller};
+ {_Kernel, unsupported_protocol} ->
+ exit(unsupported_protocol)
+ end;
+ Error ->
exit(Error)
end,
accept_loop(Proxy, Type, Listen, Extra);
@@ -242,7 +242,7 @@ loop_conn(World, Erts) ->
ssl:close(World);
{ssl_closed, World} ->
gen_tcp:close(Erts)
- end.
+ end.
get_ssl_options(Type) ->
case init:get_argument(ssl_dist_opt) of
@@ -255,7 +255,7 @@ get_ssl_options(Type) ->
ssl_options(_,[]) ->
[];
ssl_options(server, ["client_" ++ _, _Value |T]) ->
- ssl_options(server,T);
+ ssl_options(server,T);
ssl_options(client, ["server_" ++ _, _Value|T]) ->
ssl_options(client,T);
ssl_options(server, ["server_certfile", Value|T]) ->
@@ -265,7 +265,7 @@ ssl_options(client, ["client_certfile", Value | T]) ->
ssl_options(server, ["server_cacertfile", Value|T]) ->
[{cacertfile, Value} | ssl_options(server,T)];
ssl_options(client, ["client_cacertfile", Value|T]) ->
- [{cacertfile, Value} | ssl_options(client,T)];
+ [{cacertfile, Value} | ssl_options(client,T)];
ssl_options(server, ["server_keyfile", Value|T]) ->
[{keyfile, Value} | ssl_options(server,T)];
ssl_options(client, ["client_keyfile", Value|T]) ->
@@ -277,7 +277,7 @@ ssl_options(client, ["client_password", Value|T]) ->
ssl_options(server, ["server_verify", Value|T]) ->
[{verify, atomize(Value)} | ssl_options(server,T)];
ssl_options(client, ["client_verify", Value|T]) ->
- [{verify, atomize(Value)} | ssl_options(client,T)];
+ [{verify, atomize(Value)} | ssl_options(client,T)];
ssl_options(server, ["server_reuse_sessions", Value|T]) ->
[{reuse_sessions, atomize(Value)} | ssl_options(server,T)];
ssl_options(client, ["client_reuse_sessions", Value|T]) ->
@@ -295,11 +295,11 @@ ssl_options(server, ["server_hibernate_after", Value|T]) ->
ssl_options(client, ["client_hibernate_after", Value|T]) ->
[{hibernate_after, list_to_integer(Value)} | ssl_options(client,T)];
ssl_options(server, ["server_ciphers", Value|T]) ->
- [{ciphers, Value} | ssl_options(server,T)];
+ [{ciphers, Value} | ssl_options(server,T)];
ssl_options(client, ["client_ciphers", Value|T]) ->
[{ciphers, Value} | ssl_options(client,T)];
ssl_options(server, ["server_dhfile", Value|T]) ->
- [{dhfile, Value} | ssl_options(server,T)];
+ [{dhfile, Value} | ssl_options(server,T)];
ssl_options(server, ["server_fail_if_no_peer_cert", Value|T]) ->
[{fail_if_no_peer_cert, atomize(Value)} | ssl_options(server,T)];
ssl_options(_,_) ->
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 343157b22e..847907cde8 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -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..5b92e551a5 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -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 112ed85ec5..b5c6a1da49 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
@@ -41,127 +41,10 @@
-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.
-%%--------------------------------------------------------------------
-init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- application:start(public_key),
-
- %% make rsa certs using oppenssl
- Result =
- (catch make_certs:all(?config(data_dir, Config0),
- ?config(priv_dir, Config0))),
- test_server:format("Make certs ~p~n", [Result]),
-
- Config1 = ssl_test_lib:make_dsa_cert(Config0),
- Config = ssl_test_lib:cert_options(Config1),
- [{watchdog, Dog} | Config]
- catch _:_ ->
- {skip, "Crypto did not start"}
- end.
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(_Config) ->
- ssl:stop(),
- application:stop(crypto).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initialization before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initialization before each test case
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-init_per_testcase(no_authority_key_identifier, Config) ->
- %% Clear cach so that root cert will not
- %% be found.
- ssl:clear_pem_cache(),
- Config;
-init_per_testcase(protocol_versions, Config) ->
- ssl:stop(),
- application:load(ssl),
- %% For backwards compatibility sslv2 should be filtered out.
- application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
- ssl:start(),
- Config;
-
-init_per_testcase(reuse_session_expired, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_lifetime, ?EXPIRE),
- application:set_env(ssl, session_delay_cleanup_time, 500),
- ssl:start(),
- Config;
-
-init_per_testcase(empty_protocol_versions, Config) ->
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, protocol_version, []),
- ssl:start(),
- Config;
-
-%% init_per_testcase(different_ca_peer_sign, Config0) ->
-%% ssl_test_lib:make_mix_cert(Config0);
-
-init_per_testcase(_TestCase, Config0) ->
- test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]),
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
- [{watchdog, Dog} | Config].
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(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) ->
- 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() ->
@@ -183,7 +66,6 @@ groups() ->
{'tlsv1', [], all_versions_groups() ++ rizzo_tests()},
{'sslv3', [], all_versions_groups() ++ rizzo_tests()},
{api,[], api_tests()},
- {certificate_verify, [], certificate_verify_tests()},
{session, [], session_tests()},
{renegotiate, [], renegotiate_tests()},
{ciphers, [], cipher_tests()},
@@ -192,36 +74,18 @@ groups() ->
all_versions_groups ()->
[{group, api},
- {group, certificate_verify},
{group, renegotiate},
{group, ciphers},
{group, error_handling_tests}].
-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.
basic_tests() ->
[app,
alerts,
send_close,
connect_twice,
- connect_dist
+ connect_dist,
+ clear_pem_cache
].
options_tests() ->
@@ -235,19 +99,21 @@ options_tests() ->
invalid_inet_set_option_not_list,
invalid_inet_set_option_improper_list,
dh_params,
- ecertfile,
- ecacertfile,
- ekeyfile,
- eoptions,
+ invalid_certfile,
+ invalid_cacertfile,
+ invalid_keyfile,
+ invalid_options,
protocol_versions,
empty_protocol_versions,
ipv6,
- reuseaddr].
+ reuseaddr,
+ tcp_reuseaddr].
api_tests() ->
[connection_info,
peername,
peercert,
+ peercert_with_client_cert,
sockname,
versions,
controlling_process,
@@ -258,41 +124,11 @@ api_tests() ->
shutdown_both,
shutdown_error,
hibernate,
+ listen_socket,
ssl_accept_timeout,
ssl_recv_timeout
].
-certificate_verify_tests() ->
- [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,
- extended_key_usage_verify_peer,
- extended_key_usage_verify_none,
- 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,
- unknown_server_ca_accept_verify_none,
- unknown_server_ca_accept_verify_peer,
- unknown_server_ca_accept_backwardscompatibility,
- no_authority_key_identifier
- ].
-
session_tests() ->
[reuse_session,
reuse_session_expired,
@@ -332,19 +168,109 @@ rizzo_tests() ->
[rizzo,
no_rizzo_rc4].
-%% Test cases starts here.
%%--------------------------------------------------------------------
-app(doc) ->
- "Test that the ssl app file is ok";
-app(suite) ->
- [];
+init_per_suite(Config0) ->
+ Dog = ct:timetrap(?LONG_TIMEOUT *2),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+
+ %% 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(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.
+
+%%--------------------------------------------------------------------
+init_per_testcase(no_authority_key_identifier, Config) ->
+ %% Clear cach so that root cert will not
+ %% be found.
+ ssl:clear_pem_cache(),
+ Config;
+
+init_per_testcase(protocol_versions, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ %% For backwards compatibility sslv2 should be filtered out.
+ application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
+ ssl:start(),
+ Config;
+
+init_per_testcase(reuse_session_expired, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ application:set_env(ssl, session_delay_cleanup_time, 500),
+ ssl:start(),
+ Config;
+
+init_per_testcase(empty_protocol_versions, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, []),
+ ssl:start(),
+ Config;
+
+%% init_per_testcase(different_ca_peer_sign, Config0) ->
+%% ssl_test_lib:make_mix_cert(Config0);
+
+init_per_testcase(_TestCase, Config0) ->
+ ct:print("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]),
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ct:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | 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) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% 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,
@@ -361,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),
@@ -387,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 =
@@ -400,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),
@@ -475,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
@@ -494,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),
@@ -556,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),
@@ -574,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,
@@ -594,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,
@@ -616,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),
@@ -665,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),
@@ -673,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),
@@ -703,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),
@@ -741,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),
@@ -749,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),
@@ -779,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),
@@ -789,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),
@@ -818,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),
@@ -830,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},
@@ -844,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),
@@ -897,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),
@@ -923,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),
@@ -957,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),
@@ -966,16 +822,13 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) ->
get_invalid_inet_option_not_list(Socket) ->
- {error, {eoptions, {inet_options, some_invalid_atom_here}}}
+ {error, {options, {socket_options, some_invalid_atom_here}}}
= ssl:getopts(Socket, some_invalid_atom_here),
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),
@@ -992,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),
@@ -1001,15 +854,12 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
get_invalid_inet_option_improper_list(Socket) ->
- {error, {eoptions, {inet_option, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
+ {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
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),
@@ -1026,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),
@@ -1034,17 +884,14 @@ invalid_inet_set_option(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
set_invalid_inet_option(Socket) ->
- {error, {eoptions, {inet_opt, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
- {error, {eoptions, {inet_opt, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
- {error, {eoptions, {inet_opt, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
- {error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
+ {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
+ {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
+ {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
+ {error, {options, {socket_options, {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),
@@ -1061,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),
@@ -1070,16 +917,13 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) ->
set_invalid_inet_option_not_list(Socket) ->
- {error, {eoptions, {not_a_proplist, some_invalid_atom_here}}}
+ {error, {options, {not_a_proplist, some_invalid_atom_here}}}
= ssl:setopts(Socket, some_invalid_atom_here),
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),
@@ -1096,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),
@@ -1104,16 +948,13 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
set_invalid_inet_option_improper_list(Socket) ->
- {error, {eoptions, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
+ {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
ssl:setopts(Socket, [{packet, 0} | {foo, 2}]),
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),
@@ -1125,23 +966,22 @@ misc_ssl_options(Config) when is_list(Config) ->
{key, undefined},
{password, []},
{reuse_session, fun(_,_,_,_) -> true end},
- {debug, []},
{cb_info, {gen_tcp, tcp, tcp_closed, tcp_error}}],
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),
@@ -1149,23 +989,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),
@@ -1173,17 +1006,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),
@@ -1192,12 +1025,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),
@@ -1205,7 +1034,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,
@@ -1213,7 +1042,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),
@@ -1221,11 +1050,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),
@@ -1235,7 +1061,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
@@ -1246,11 +1072,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),
@@ -1262,13 +1085,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]}]),
@@ -1279,11 +1102,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),
@@ -1307,7 +1127,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),
@@ -1331,11 +1151,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),
@@ -1360,7 +1177,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),
@@ -1369,11 +1186,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),
@@ -1389,22 +1203,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),
@@ -1420,7 +1231,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),
@@ -1430,24 +1241,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(),
@@ -1459,18 +1262,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),
@@ -1483,13 +1286,9 @@ ipv6(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ekeyfile(doc) ->
- ["Test what happens with an invalid key file"];
-
-ekeyfile(suite) ->
- [];
-
-ekeyfile(Config) when is_list(Config) ->
+invalid_keyfile() ->
+ [{doc,"Test what happens with an invalid key file"}].
+invalid_keyfile(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
BadOpts = ?config(server_bad_key, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1505,19 +1304,17 @@ ekeyfile(Config) when is_list(Config) ->
ssl_test_lib:start_client_error([{node, ClientNode},
{port, Port}, {host, Hostname},
{from, self()}, {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, {error, ekeyfile}, Client,
- {error, closed}).
-%%--------------------------------------------------------------------
+ File = proplists:get_value(keyfile,BadOpts),
+ ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client,
+ {error, closed}).
-ecertfile(doc) ->
- ["Test what happens with an invalid cert file"];
+%%--------------------------------------------------------------------
-ecertfile(suite) ->
- [];
+invalid_certfile() ->
+ [{doc,"Test what happens with an invalid cert file"}].
-ecertfile(Config) when is_list(Config) ->
+invalid_certfile(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerBadOpts = ?config(server_bad_cert, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1534,19 +1331,16 @@ ecertfile(Config) when is_list(Config) ->
{port, Port}, {host, Hostname},
{from, self()},
{options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, {error, ecertfile}, Client,
- {error, closed}).
+ File = proplists:get_value(certfile, ServerBadOpts),
+ ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}},
+ Client, {error, closed}).
%%--------------------------------------------------------------------
-ecacertfile(doc) ->
- ["Test what happens with an invalid cacert file"];
-
-ecacertfile(suite) ->
- [];
+invalid_cacertfile() ->
+ [{doc,"Test what happens with an invalid cacert file"}].
-ecacertfile(Config) when is_list(Config) ->
+invalid_cacertfile(Config) when is_list(Config) ->
ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)],
ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)],
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1564,11 +1358,12 @@ ecacertfile(Config) when is_list(Config) ->
{port, Port0}, {host, Hostname},
{from, self()},
{options, ClientOpts}]),
+
+ File0 = proplists:get_value(cacertfile, ServerBadOpts),
- ssl_test_lib:check_result(Server0, {error, ecacertfile},
+ ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}},
Client0, {error, closed}),
- File0 = proplists:get_value(cacertfile, ServerBadOpts),
File = File0 ++ "do_not_exit.pem",
ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)],
@@ -1585,34 +1380,32 @@ ecacertfile(Config) when is_list(Config) ->
{from, self()},
{options, ClientOpts}]),
- ssl_test_lib:check_result(Server1, {error, ecacertfile},
+
+ ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}},
Client1, {error, closed}),
ok.
%%--------------------------------------------------------------------
-eoptions(doc) ->
- ["Test what happens when we give invalid options"];
+invalid_options() ->
+ [{doc,"Test what happens when we give invalid options"}].
-eoptions(suite) ->
- [];
-
-eoptions(Config) when is_list(Config) ->
+invalid_options(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) ->
ssl_test_lib:check_result(Server,
- {error, {eoptions, {sslv2, Option}}},
+ {error, {options, {sslv2, Option}}},
Client,
- {error, {eoptions, {sslv2, Option}}});
+ {error, {options, {sslv2, Option}}});
(Client, Server, Option) ->
ssl_test_lib:check_result(Server,
- {error, {eoptions, Option}},
+ {error, {options, Option}},
Client,
- {error, {eoptions, Option}})
+ {error, {options, Option}})
end,
TestOpts = [{versions, [sslv2, sslv3]},
@@ -1630,7 +1423,6 @@ eoptions(Config) when is_list(Config) ->
{reuse_session, foo},
{reuse_sessions, 0},
{renegotiate_at, "10"},
- {debug, 1},
{mode, depech},
{packet, 8.0},
{packet_size, "2"},
@@ -1654,12 +1446,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),
@@ -1684,25 +1472,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),
@@ -1719,20 +1491,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),
@@ -1750,19 +1512,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()),
@@ -1772,141 +1524,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("~p erlang cipher suites ~p~n", [Version, Ciphers]),
+ ct:print("~p erlang cipher suites ~p~n", [Version, 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]),
+ 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("~p erlang cipher suites ~p~n", [Version, 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).
-
-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: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),
@@ -1925,16 +1596,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, {tls_alert, "insufficient security"}},
+ Client, {error, {tls_alert, "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),
@@ -1960,7 +1627,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},
@@ -1971,9 +1638,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, []}}},
@@ -1986,7 +1653,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
@@ -2016,7 +1683,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},
@@ -2026,10 +1693,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,
@@ -2041,12 +1708,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),
@@ -2072,7 +1735,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},
@@ -2083,15 +1746,15 @@ 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)),
+ ct:sleep((?EXPIRE+1)),
[{session_id, Id} |_] = SessionInfo,
make_sure_expired(Hostname, Port, Id),
@@ -2103,7 +1766,7 @@ reuse_session_expired(Config) when is_list(Config) ->
{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,
@@ -2125,17 +1788,13 @@ make_sure_expired(Host, Port, Id) ->
#session{is_resumable = false} ->
ok;
_ ->
- test_server:sleep(?SLEEP),
+ 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(suite) ->
- [];
-
+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),
@@ -2164,7 +1823,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 =
@@ -2174,7 +1833,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,
@@ -2183,512 +1842,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]}]),
@@ -2697,805 +1899,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),
@@ -3516,19 +2046,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),
@@ -3545,12 +2075,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),
@@ -3558,7 +2085,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),
@@ -3566,7 +2093,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},
@@ -3579,12 +2106,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),
@@ -3610,10 +2133,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()},
@@ -3627,7 +2155,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,
@@ -3635,12 +2163,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),
@@ -3670,7 +2195,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),
@@ -3690,7 +2215,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,
@@ -3698,11 +2223,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),
@@ -3720,20 +2242,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),
@@ -3741,14 +2262,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}]}]),
-hibernate(doc) ->
- ["Check that an SSL connection that is started with option "
- "{hibernate_after, 1000} indeed hibernates after 1000ms of "
- "inactivity"];
+ ssl_test_lib:check_result(Server1, ok, Client1, ok),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
-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),
@@ -3758,14 +2316,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),
@@ -3779,10 +2337,34 @@ hibernate(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-ssl_accept_timeout(doc) ->
- ["Test ssl:ssl_accept timeout"];
-ssl_accept_timeout(suite) ->
- [];
+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),
@@ -3806,10 +2388,9 @@ ssl_accept_timeout(Config) ->
end.
%%--------------------------------------------------------------------
-ssl_recv_timeout(doc) ->
- ["Test ssl:ssl_accept timeout"];
-ssl_recv_timeout(suite) ->
- [];
+ssl_recv_timeout() ->
+ [{doc,"Test ssl:ssl_accept timeout"}].
+
ssl_recv_timeout(Config) ->
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -3835,11 +2416,8 @@ ssl_recv_timeout(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-
-connect_twice(doc) ->
- [""];
-connect_twice(suite) ->
- [];
+connect_twice() ->
+ [{doc,""}].
connect_twice(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -3849,7 +2427,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),
@@ -3857,7 +2435,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,
@@ -3867,11 +2445,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),
@@ -3882,13 +2460,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),
@@ -3898,7 +2472,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),
@@ -3914,13 +2488,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),
@@ -3930,7 +2500,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),
@@ -3946,8 +2516,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),
@@ -3976,11 +2546,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),
@@ -4000,11 +2568,11 @@ 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],
@@ -4013,8 +2581,8 @@ rizzo(Config) when is_list(Config) ->
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],
@@ -4024,58 +2592,124 @@ no_rizzo_rc4(Config) when is_list(Config) ->
{?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),
@@ -4103,17 +2737,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"),
@@ -4132,26 +2755,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 ->
@@ -4162,7 +2772,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) ->
@@ -4178,9 +2788,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.
@@ -4199,27 +2809,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).
@@ -4361,9 +2955,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})
- after ?SLEEP * 3 ->
- test_server:fail({did_not_get, Data})
+ ct:fail({unexpected_message, Other})
+ after ?SLEEP * 3 * test_server:timetrap_scale_factor() ->
+ ct:fail({did_not_get, Data})
end.
receive_msg(_) ->
@@ -4371,3 +2965,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, {options, {socket_options, 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..26938bda50
--- /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, {tls_alert, "handshake failure"}},
+ Client, {error, {tls_alert, "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 {tls_alert,"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, {tls_alert, "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 {tls_alert, "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, {tls_alert, "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, {tls_alert, "certificate expired"}},
+ Client, {error, {tls_alert, "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, {tls_alert, "bad certificate"}},
+ Client, {error, {tls_alert, "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, {tls_alert, "bad certificate"}},
+ Client, {error, {tls_alert, "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, {options, {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, {tls_alert, "unknown ca"}},
+ Client, {error, {tls_alert, "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 ea1d9dc90c..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-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,16 +31,18 @@
-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 ->
@@ -48,81 +50,30 @@ 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;
- _ ->
- 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() ->
- [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11].
-
-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.
-%%--------------------------------------------------------------------
-aes_decipher_good(doc) ->
- ["Decipher a known cryptotext."];
+end_per_testcase(_TestCase, Config) ->
+ Config.
-aes_decipher_good(suite) ->
- [];
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+aes_decipher_good() ->
+ [{doc,"Decipher a known cryptotext."}].
aes_decipher_good(Config) when is_list(Config) ->
HashSz = 32,
@@ -142,11 +93,8 @@ aes_decipher_good(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-aes_decipher_good_tls11(doc) ->
- ["Decipher a known TLS 1.1 cryptotext."];
-
-aes_decipher_good_tls11(suite) ->
- [];
+aes_decipher_good_tls11() ->
+ [{doc,"Decipher a known TLS 1.1 cryptotext."}].
%% the fragment is actuall a TLS 1.1 record, with
%% Version = TLS 1.1, we get the correct NextIV in #cipher_state
@@ -169,11 +117,8 @@ aes_decipher_good_tls11(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-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) ->
@@ -196,11 +141,8 @@ aes_decipher_fail(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-aes_decipher_fail_tls11(doc) ->
- ["Decipher a known TLS 1.1 cryptotext."];
-
-aes_decipher_fail_tls11(suite) ->
- [];
+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
@@ -213,9 +155,11 @@ aes_decipher_fail_tls11(Config) when is_list(Config) ->
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),
+ #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),
+ #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 363a0be594..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-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
@@ -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,
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..862690cd7b
--- /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, {options, {next_protocols_advertised, {invalid_protocol, <<>>}}}}
+ = (catch ssl:listen(9443,
+ [{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])),
+ {error, {options, {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, {options, 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, {options, 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, {options, 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,
+ {options,
+ {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, {options, {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 8ce80cb725..4116bb39d1 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,84 +41,10 @@
-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.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-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
-%%--------------------------------------------------------------------
-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() ->
@@ -133,7 +59,8 @@ groups() ->
[{'tlsv1.2', [], packet_tests()},
{'tlsv1.1', [], packet_tests()},
{'tlsv1', [], packet_tests()},
- {'sslv3', [], packet_tests()}].
+ {'sslv3', [], packet_tests()}
+ ].
packet_tests() ->
active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++
@@ -208,6 +135,23 @@ active_packet_tests() ->
header_decode_two_bytes_one_sent_active
].
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ 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
@@ -228,1032 +172,262 @@ init_per_group(GroupName, 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_raw, ?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_raw, ?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_raw, ?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_raw, ?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, active_once_packet, ?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, active_once_packet, ?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, active_once_packet, ?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, 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, active_once_packet, ?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, active_once_packet, ?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, true).
%%--------------------------------------------------------------------
-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, true).
%%--------------------------------------------------------------------
-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, true).
%%--------------------------------------------------------------------
-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, active_raw, ?SOME, 0, true).
%%--------------------------------------------------------------------
-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, active_packet, ?MANY, 1, true).
%%--------------------------------------------------------------------
-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, active_packet, ?SOME, 1, true).
%%--------------------------------------------------------------------
-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, active_packet, ?MANY, 2, true).
%%--------------------------------------------------------------------
-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, active_packet, ?SOME, 2, true).
%%--------------------------------------------------------------------
-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, active_packet, ?MANY, 4, true).
%%--------------------------------------------------------------------
-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, active_packet, ?SOME, 4, true).
%%--------------------------------------------------------------------
-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),
@@ -1279,16 +453,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),
@@ -1320,11 +487,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),
@@ -1353,10 +517,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),
@@ -1381,18 +543,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),
@@ -1418,19 +577,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),
@@ -1455,17 +611,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),
@@ -1490,17 +645,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),
@@ -1532,10 +685,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),
@@ -1567,11 +718,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),
@@ -1652,11 +801,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),
@@ -1712,11 +859,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),
@@ -1803,11 +947,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),
@@ -1865,10 +1008,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),
@@ -1922,10 +1064,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),
@@ -1973,10 +1113,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),
@@ -2027,10 +1166,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),
@@ -2082,10 +1220,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),
@@ -2123,10 +1260,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),
@@ -2164,10 +1300,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),
@@ -2201,10 +1336,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),
@@ -2240,10 +1374,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),
@@ -2276,10 +1409,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),
@@ -2314,10 +1446,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),
@@ -2347,10 +1478,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),
@@ -2381,10 +1511,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),
@@ -2416,10 +1545,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),
@@ -2449,10 +1576,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),
@@ -2482,10 +1608,9 @@ packet_sunrm_decode_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-header_decode_one_byte_active(doc) ->
- ["Test setting the packet option {header, 1}"];
-header_decode_one_byte_active(suite) ->
- [];
+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),
@@ -2516,10 +1641,9 @@ header_decode_one_byte_active(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_active(doc) ->
- ["Test setting the packet option {header, 2}"];
-header_decode_two_bytes_active(suite) ->
- [];
+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),
@@ -2551,10 +1675,9 @@ header_decode_two_bytes_active(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(suite) ->
- [];
+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),
@@ -2586,10 +1709,9 @@ header_decode_two_bytes_two_sent_active(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(suite) ->
- [];
+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),
@@ -2620,10 +1742,9 @@ header_decode_two_bytes_one_sent_active(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_one_byte_passive(doc) ->
- ["Test setting the packet option {header, 1}"];
-header_decode_one_byte_passive(suite) ->
- [];
+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),
@@ -2654,10 +1775,9 @@ header_decode_one_byte_passive(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-header_decode_two_bytes_passive(doc) ->
- ["Test setting the packet option {header, 2}"];
-header_decode_two_bytes_passive(suite) ->
- [];
+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),
@@ -2689,10 +1809,9 @@ header_decode_two_bytes_passive(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-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(suite) ->
- [];
+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),
@@ -2724,10 +1843,9 @@ header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-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(suite) ->
- [];
+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),
@@ -2757,7 +1875,30 @@ header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-%% Internal functions
+%% 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, [{packet, Packet} | 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, <<>>),
@@ -2928,7 +2069,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
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index c97f97e70b..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-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
@@ -26,79 +26,8 @@
-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.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- application:start(public_key),
- ssl:start(),
- make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)),
- ssl_test_lib:cert_options(Config)
- catch _:_ ->
- {skip, "Crypto did not start"}
- end.
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% 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;
- _ ->
- 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
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -138,6 +67,21 @@ payload_tests() ->
client_echos_active_once_huge,
client_echos_active_huge].
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) of
@@ -157,15 +101,20 @@ init_per_group(GroupName, 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),
@@ -179,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),
@@ -198,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),
@@ -216,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),
@@ -234,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),
@@ -252,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),
@@ -271,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),
@@ -290,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),
@@ -309,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),
@@ -327,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),
@@ -345,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),
@@ -363,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),
@@ -381,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),
@@ -399,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),
@@ -417,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),
@@ -435,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),
@@ -452,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),
@@ -469,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),
@@ -486,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) ->
@@ -656,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 1d71efd40c..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-2012. 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 f1f5b9ae0a..76b302b1cb 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-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
@@ -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,8 +58,9 @@ 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).
@@ -81,14 +75,15 @@ do_run_server(_, {error, timeout} = Result, Opts) ->
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
@@ -97,15 +92,16 @@ do_run_server(ListenSocket, AcceptSocket, 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),
Timeout = proplists:get_value(timeout, Opts, infinity),
@@ -116,15 +112,21 @@ connect(ListenSocket, Opts) ->
_ ->
remove_close_msg(ReconnectTimes),
AcceptSocket
- end.
-
+ 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, _, Timeout) ->
- test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]),
+ ct:print("ssl:transport_accept(~p)~n", [ListenSocket]),
{ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
[ListenSocket]),
- test_server:format("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]),
+ ct:print("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]),
case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of
ok ->
@@ -157,45 +159,48 @@ 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", []),
+ 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, ServerMsg, Client, ClientMsg) ->
@@ -207,7 +212,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
@@ -216,7 +221,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]),
@@ -225,7 +230,7 @@ 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, Msg) ->
@@ -238,7 +243,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) ->
@@ -405,33 +410,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} ->
@@ -449,24 +454,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.
@@ -485,20 +490,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])
@@ -517,22 +522,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.
@@ -548,9 +562,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)->
@@ -577,7 +592,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;
@@ -670,7 +685,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"),
@@ -751,3 +766,33 @@ sufficient_crypto_support('tlsv1.2') ->
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 d446014f7b..4f53132d5d 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,125 +28,15 @@
-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.
-%%--------------------------------------------------------------------
-init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
- case os:find_executable("openssl") of
- false ->
- {skip, "Openssl not found"};
- _ ->
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- application:start(public_key),
- ssl:start(),
- Result =
- (catch make_certs:all(?config(data_dir, Config0),
- ?config(priv_dir, Config0))),
- test_server:format("Make certs ~p~n", [Result]),
- Config1 = ssl_test_lib:make_dsa_cert(Config0),
- Config = ssl_test_lib:cert_options(Config1),
- [{watchdog, Dog} | Config]
- catch _:_ ->
- {skip, "Crypto did not start"}
- end
- end.
-
-%%--------------------------------------------------------------------
-%% 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(expired_session, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_lifetime, ?EXPIRE),
- ssl:start(),
- [{watchdog, Dog} | Config];
-
-init_per_testcase(TestCase, Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = ssl_test_lib:timetrap(?TIMEOUT),
- special_init(TestCase, [{watchdog, Dog} | 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
- ->
- check_sane_openssl_renegotaite(Config);
-
-special_init(ssl2_erlang_server_openssl_client, Config) ->
- check_sane_openssl_sslv2(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
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-end_per_testcase(reuse_session_expired, Config) ->
- application:unset_env(ssl, session_lifetime),
- end_per_testcase(default_action, Config);
-
-end_per_testcase(default_action, Config) ->
- Dog = ?config(watchdog, Config),
- case Dog of
- undefined ->
- ok;
- _ ->
- test_server:timetrap_cancel(Dog)
- end;
-end_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() ->
@@ -161,9 +50,9 @@ all() ->
groups() ->
[{basic, [], basic_tests()},
- {'tlsv1.2', [], all_versions_tests()},
- {'tlsv1.1', [], all_versions_tests()},
- {'tlsv1', [], all_versions_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() ->
@@ -179,16 +68,54 @@ all_versions_tests() ->
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_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,
- ssl2_erlang_server_openssl_client
- ].
+ 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 = ct:timetrap(?LONG_TIMEOUT *2),
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ 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.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) of
@@ -208,13 +135,55 @@ init_per_group(GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(expired_session, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ct:timetrap(?EXPIRE * 1000 * 5),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ ssl:start(),
+ [{watchdog, Dog} | Config];
+
+init_per_testcase(TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ct:timetrap(?TIMEOUT),
+ special_init(TestCase, [{watchdog, Dog} | Config]).
+
+special_init(TestCase, Config)
+ when TestCase == erlang_client_openssl_server_renegotiate;
+ 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(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.
+
+end_per_testcase(reuse_session_expired, Config) ->
+ application:unset_env(ssl, session_lifetime),
+ Config;
+end_per_testcase(_, Config) ->
+ Config.
-%% Test cases starts here.
%%--------------------------------------------------------------------
-basic_erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-basic_erlang_client_openssl_server(suite) ->
- [];
+%% 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),
@@ -231,7 +200,7 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) ->
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]),
@@ -250,14 +219,11 @@ basic_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).
%%--------------------------------------------------------------------
-basic_erlang_server_openssl_client(doc) ->
- ["Test erlang server with openssl client"];
-basic_erlang_server_openssl_client(suite) ->
- [];
+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),
@@ -275,7 +241,7 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
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),
@@ -288,10 +254,8 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
-erlang_client_openssl_server(doc) ->
- ["Test erlang client with openssl server"];
-erlang_client_openssl_server(suite) ->
- [];
+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),
ServerOpts = ?config(server_opts, Config),
@@ -308,7 +272,7 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
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]),
@@ -327,15 +291,11 @@ 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() ->
+ [{doc,"Test erlang server with openssl client"}].
erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -354,7 +314,7 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
Cmd = "openssl s_client -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),
@@ -364,15 +324,12 @@ erlang_server_openssl_client(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_dsa_cert(doc) ->
- ["Test erlang server with openssl client"];
-erlang_client_openssl_server_dsa_cert(suite) ->
- [];
+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),
@@ -392,7 +349,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
++ " -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]),
@@ -415,10 +372,8 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
-erlang_server_openssl_client_dsa_cert(doc) ->
- ["Test erlang server with openssl client"];
-erlang_server_openssl_client_dsa_cert(suite) ->
- [];
+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),
@@ -441,7 +396,7 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
" -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
++ " -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]),
port_command(OpenSslPort, Data),
@@ -451,16 +406,13 @@ erlang_server_openssl_client_dsa_cert(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_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),
@@ -479,7 +431,7 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
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]),
@@ -495,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),
@@ -517,7 +467,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
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]),
@@ -531,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),
@@ -544,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),
@@ -568,7 +516,7 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config
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]),
@@ -587,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),
@@ -618,7 +562,7 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config
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]),
@@ -629,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),
@@ -655,7 +597,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
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]),
@@ -675,14 +617,11 @@ 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.
+ process_flag(trap_exit, false).
%%--------------------------------------------------------------------
-erlang_client_openssl_server_client_cert(doc) ->
- ["Test erlang client with openssl server when client sends cert"];
-erlang_client_openssl_server_client_cert(suite) ->
- [];
+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),
@@ -701,7 +640,7 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
" -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
++ " -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]),
@@ -720,15 +659,12 @@ 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).
%%--------------------------------------------------------------------
-erlang_server_openssl_client_client_cert(doc) ->
- ["Test erlang server with openssl client when client sends cert"];
-erlang_server_openssl_client_client_cert(suite) ->
- [];
+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),
@@ -755,7 +691,7 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
++ " -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),
@@ -765,16 +701,12 @@ 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).
%%--------------------------------------------------------------------
-erlang_server_erlang_client_client_cert(doc) ->
- ["Test erlang server with erlang client when client sends cert"];
-erlang_server_erlang_client_client_cert(suite) ->
- [];
+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),
@@ -807,30 +739,22 @@ erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
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(suite) ->
- [];
+%%--------------------------------------------------------------------
+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).
+%%--------------------------------------------------------------------
-
-ciphers_dsa_signed_certs(doc) ->
- ["Test cipher suites that uses dsa certs"];
-
-ciphers_dsa_signed_certs(suite) ->
- [];
-
+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([])),
@@ -838,90 +762,9 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) ->
Ciphers = ssl_test_lib:dsa_suites(),
run_suites(Ciphers, Version, Config, dsa).
-run_suites(Ciphers, Version, Config, Type) ->
- {ClientOpts, ServerOpts} =
- case Type of
- rsa ->
- {?config(client_opts, Config),
- ?config(server_opts, Config)};
- dsa ->
- {?config(client_opts, Config),
- ?config(server_dsa_opts, Config)}
- end,
-
- Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config, 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.
-
-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),
-
- 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]),
-
- wait_for_openssl_server(),
-
- ConnectionInfo = {ok, {Version, CipherSuite}},
-
- 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]}]),
-
- port_command(OpenSslPort, "Hello\n"),
-
- receive
- {Port, {data, _}} when is_port(Port) ->
- ok
- after 500 ->
- test_server:format("Time out on openssl port, check that"
- " the messages Hello and world are received"
- " during close of port" , []),
- ok
- end,
-
- port_command(OpenSslPort, " world\n"),
-
- Result = ssl_test_lib:wait_for_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- close_port(OpenSslPort),
- ssl_test_lib:close(Client),
-
- Return = case Result of
- ok ->
- [];
- Error ->
- [{CipherSuite, Error}]
- end,
- process_flag(trap_exit, false),
- Return.
-
%%--------------------------------------------------------------------
-erlang_client_bad_openssl_server(doc) ->
- [""];
-erlang_client_bad_openssl_server(suite) ->
- [];
+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),
@@ -936,7 +779,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
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]),
@@ -952,7 +795,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
%% Send garbage
port_command(OpensslPort, ?OPENSSL_GARBAGE),
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
Client0 ! server_sent_garbage,
@@ -976,13 +819,9 @@ erlang_client_bad_openssl_server(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(suite) ->
- [];
-
+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),
ClientOpts = ?config(client_opts, Config),
@@ -996,7 +835,7 @@ expired_session(Config) when is_list(Config) ->
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]),
@@ -1011,7 +850,7 @@ expired_session(Config) when is_list(Config) ->
ssl_test_lib:close(Client0),
%% Make sure session is registered
- test_server:sleep(?SLEEP),
+ ct:sleep(?SLEEP),
Client1 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -1021,7 +860,7 @@ expired_session(Config) when is_list(Config) ->
ssl_test_lib:close(Client1),
%% Make sure session is unregistered due to expiration
- test_server:sleep((?EXPIRE+1) * 1000),
+ ct:sleep((?EXPIRE+1) * 1000),
Client2 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -1035,10 +874,9 @@ expired_session(Config) when is_list(Config) ->
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() ->
+ [{doc,"Test that ssl v2 clients are rejected"}].
+
ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -1055,23 +893,339 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
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, {error,"protocol version"}),
-
+ receive
+ {'EXIT', OpenSslPort, _} ->
+ ok
+
+ end,
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "protocol version"}}),
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn() ->
+ [{doc,"Test erlang client with openssl server doing npn negotiation"}].
+
+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),
+
+ 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"}].
+
+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"}].
+
+erlang_server_openssl_client_npn(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, 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"}].
+
+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.
+
+%%--------------------------------------------------------------------------
+
+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
+ rsa ->
+ {?config(client_opts, Config),
+ ?config(server_opts, Config)};
+ dsa ->
+ {?config(client_opts, Config),
+ ?config(server_dsa_opts, Config)}
+ end,
+
+ Result = lists:map(fun(Cipher) ->
+ cipher(Cipher, Version, Config, 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.
+
+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),
+
+ 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 ++ "",
+
+ 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},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
+
+ port_command(OpenSslPort, "Hello\n"),
+
+ receive
+ {Port, {data, _}} when is_port(Port) ->
+ ok
+ after 500 ->
+ 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),
+
%% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
close_port(OpenSslPort),
+ ssl_test_lib:close(Client),
+
+ Return = case Result of
+ ok ->
+ [];
+ Error ->
+ [{CipherSuite, Error}]
+ end,
process_flag(trap_exit, false),
- ok.
+ Return.
-%%--------------------------------------------------------------------
+start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ 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 " ++ OpensslServerOpts ++ " -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(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ 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),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ 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(),
+
+ 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}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, 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_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ 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),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ 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} ->
@@ -1085,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}
@@ -1104,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).
@@ -1156,7 +1310,7 @@ 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) ->
@@ -1168,6 +1322,15 @@ version_flag('tlsv1.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" ++ _ ->
@@ -1179,11 +1342,27 @@ check_sane_openssl_renegotaite(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.
+
+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) ->
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index adfb29e639..1f3bef83c8 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.1.2
+SSL_VSN = 5.2.1
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 50f6427eaa..6f1e61e70c 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -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/base64.xml b/lib/stdlib/doc/src/base64.xml
index bfe8494a73..f90a82187d 100644
--- a/lib/stdlib/doc/src/base64.xml
+++ b/lib/stdlib/doc/src/base64.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,6 +37,11 @@
<datatype>
<name name="ascii_string"/>
</datatype>
+ <datatype>
+ <name name="ascii_binary"/>
+ <desc><p>A <c>binary()</c> with ASCII characters in the range 1 to 255.</p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
index 7b8e279788..06cfad0b0b 100644
--- a/lib/stdlib/doc/src/binary.xml
+++ b/lib/stdlib/doc/src/binary.xml
@@ -77,41 +77,30 @@
</datatypes>
<funcs>
<func>
- <name>at(Subject, Pos) -> byte()</name>
+ <name name="at" arity="2"/>
<fsummary>Returns the byte at a specific position in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pos = integer() >= 0</v>
- </type>
<desc>
- <p>Returns the byte at position <c>Pos</c> (zero-based) in the binary
- <c>Subject</c> as an integer. If <c>Pos</c> &gt;= <c>byte_size(Subject)</c>,
+ <p>Returns the byte at position <c><anno>Pos</anno></c> (zero-based) in the binary
+ <c><anno>Subject</anno></c> as an integer. If <c><anno>Pos</anno></c> &gt;= <c>byte_size(<anno>Subject</anno>)</c>,
a <c>badarg</c>
exception is raised.</p>
</desc>
</func>
<func>
- <name>bin_to_list(Subject) -> [byte()]</name>
+ <name name="bin_to_list" arity="1"/>
<fsummary>Convert a binary to a list of integers</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>The same as <c>bin_to_list(Subject,{0,byte_size(Subject)})</c>.</p>
+ <p>The same as <c>bin_to_list(<anno>Subject</anno>,{0,byte_size(<anno>Subject</anno>)})</c>.</p>
</desc>
</func>
<func>
- <name>bin_to_list(Subject, PosLen) -> [byte()]</name>
+ <name name="bin_to_list" arity="2"/>
<fsummary>Convert a binary to a list of integers</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>PosLen = part()</v>
- </type>
<desc>
- <p>Converts <c>Subject</c> to a list of <c>byte()</c>s, each representing
+ <p>Converts <c><anno>Subject</anno></c> to a list of <c>byte()</c>s, each representing
the value of one byte. The <c>part()</c> denotes which part of the
<c>binary()</c> to convert. Example:</p>
@@ -120,27 +109,19 @@
"rla"
%% or [114,108,97] in list notation.
</code>
- <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>bin_to_list(Subject, Pos, Len) -> [byte()]</name>
+ <name name="bin_to_list" arity="3"/>
<fsummary>Convert a binary to a list of integers</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pos = integer() >= 0</v>
- <v>Len = integer() >= 0</v>
- </type>
<desc>
- <p>The same as<c> bin_to_list(Subject,{Pos,Len})</c>.</p>
+ <p>The same as<c> bin_to_list(<anno>Subject</anno>,{<anno>Pos</anno>,<anno>Len</anno>})</c>.</p>
</desc>
</func>
<func>
- <name>compile_pattern(Pattern) -> cp()</name>
+ <name name="compile_pattern" arity="1"/>
<fsummary>Pre-compiles a binary search pattern</fsummary>
- <type>
- <v>Pattern = binary() | [ binary() ]</v>
- </type>
<desc>
<p>Builds an internal structure representing a compilation of a
@@ -155,7 +136,7 @@
<p>When a list of binaries is given, it denotes a set of
alternative binaries to search for. I.e if
<c>[&lt;&lt;"functional"&gt;&gt;,&lt;&lt;"programming"&gt;&gt;]</c>
- is given as <c>Pattern</c>, this
+ is given as <c><anno>Pattern</anno></c>, this
means "either <c>&lt;&lt;"functional"&gt;&gt;</c> or
<c>&lt;&lt;"programming"&gt;&gt;</c>". The pattern is a set of
alternatives; when only a single binary is given, the set has
@@ -163,32 +144,25 @@
<p>The list of binaries used for search alternatives shall be flat and proper.</p>
- <p>If <c>Pattern</c> is not a binary or a flat proper list of binaries with length &gt; 0,
+ <p>If <c><anno>Pattern</anno></c> is not a binary or a flat proper list of binaries with length &gt; 0,
a <c>badarg</c> exception will be raised.</p>
</desc>
</func>
<func>
- <name>copy(Subject) -> binary()</name>
+ <name name="copy" arity="1"/>
<fsummary>Creates a duplicate of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>The same as <c>copy(Subject, 1)</c>.</p>
+ <p>The same as <c>copy(<anno>Subject</anno>, 1)</c>.</p>
</desc>
</func>
<func>
- <name>copy(Subject,N) -> binary()</name>
+ <name name="copy" arity="2"/>
<fsummary>Duplicates a binary N times and creates a new</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>N = integer() >= 0</v>
- </type>
<desc>
- <p>Creates a binary with the content of <c>Subject</c> duplicated <c>N</c> times.</p>
+ <p>Creates a binary with the content of <c><anno>Subject</anno></c> duplicated <c><anno>N</anno></c> times.</p>
- <p>This function will always create a new binary, even if <c>N =
+ <p>This function will always create a new binary, even if <c><anno>N</anno> =
1</c>. By using <c>copy/1</c> on a binary referencing a larger binary, one
might free up the larger binary for garbage collection.</p>
@@ -201,32 +175,23 @@
large binaries are no longer used in any process, deliberate
copying might be a good idea.</p> </note>
- <p>If <c>N</c> &lt; <c>0</c>, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>N</anno></c> &lt; <c>0</c>, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>decode_unsigned(Subject) -> Unsigned</name>
+ <name name="decode_unsigned" arity="1"/>
<fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Unsigned = integer() >= 0</v>
- </type>
<desc>
- <p>The same as <c>decode_unsigned(Subject,big)</c>.</p>
+ <p>The same as <c>decode_unsigned(<anno>Subject</anno>, big)</c>.</p>
</desc>
</func>
<func>
- <name>decode_unsigned(Subject, Endianess) -> Unsigned</name>
+ <name name="decode_unsigned" arity="2"/>
<fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Endianess = big | little</v>
- <v>Unsigned = integer() >= 0</v>
- </type>
<desc>
<p>Converts the binary digit representation, in big or little
- endian, of a positive integer in <c>Subject</c> to an Erlang <c>integer()</c>.</p>
+ endian, of a positive integer in <c><anno>Subject</anno></c> to an Erlang <c>integer()</c>.</p>
<p>Example:</p>
@@ -237,22 +202,15 @@
</desc>
</func>
<func>
- <name>encode_unsigned(Unsigned) -> binary()</name>
+ <name name="encode_unsigned" arity="1"/>
<fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
- <type>
- <v>Unsigned = integer() >= 0</v>
- </type>
<desc>
- <p>The same as <c>encode_unsigned(Unsigned,big)</c>.</p>
+ <p>The same as <c>encode_unsigned(<anno>Unsigned</anno>, big)</c>.</p>
</desc>
</func>
<func>
- <name>encode_unsigned(Unsigned,Endianess) -> binary()</name>
+ <name name="encode_unsigned" arity="2"/>
<fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
- <type>
- <v>Unsigned = integer() >= 0</v>
- <v>Endianess = big | little</v>
- </type>
<desc>
<p>Converts a positive integer to the smallest possible
@@ -268,51 +226,39 @@
</desc>
</func>
<func>
- <name>first(Subject) -> byte()</name>
+ <name name="first" arity="1"/>
<fsummary>Returns the first byte of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>Returns the first byte of the binary <c>Subject</c> as an integer. If the
- size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+ <p>Returns the first byte of the binary <c><anno>Subject</anno></c> as an integer. If the
+ size of <c><anno>Subject</anno></c> is zero, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>last(Subject) -> byte()</name>
+ <name name="last" arity="1"/>
<fsummary>Returns the last byte of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- </type>
<desc>
- <p>Returns the last byte of the binary <c>Subject</c> as an integer. If the
- size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+ <p>Returns the last byte of the binary <c><anno>Subject</anno></c> as an integer. If the
+ size of <c><anno>Subject</anno></c> is zero, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>list_to_bin(ByteList) -> binary()</name>
+ <name name="list_to_bin" arity="1"/>
<fsummary>Convert a list of integers and binaries to a binary</fsummary>
- <type>
- <v>ByteList = iodata() (see module erlang)</v>
- </type>
<desc>
<p>Works exactly as <c>erlang:list_to_binary/1</c>, added for completeness.</p>
</desc>
</func>
<func>
- <name>longest_common_prefix(Binaries) -> integer() >= 0</name>
+ <name name="longest_common_prefix" arity="1"/>
<fsummary>Returns length of longest common prefix for a set of binaries</fsummary>
- <type>
- <v>Binaries = [ binary() ]</v>
- </type>
<desc>
<p>Returns the length of the longest common prefix of the
- binaries in the list <c>Binaries</c>. Example:</p>
+ binaries in the list <c><anno>Binaries</anno></c>. Example:</p>
<code>
1> binary:longest_common_prefix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"ergonomy"&gt;&gt;]).
@@ -321,19 +267,16 @@
0
</code>
- <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
+ <p>If <c><anno>Binaries</anno></c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>longest_common_suffix(Binaries) -> integer() >= 0</name>
+ <name name="longest_common_suffix" arity="1"/>
<fsummary>Returns length of longest common suffix for a set of binaries</fsummary>
- <type>
- <v>Binaries = [ binary() ]</v>
- </type>
<desc>
<p>Returns the length of the longest common suffix of the
- binaries in the list <c>Binaries</c>. Example:</p>
+ binaries in the list <c><anno>Binaries</anno></c>. Example:</p>
<code>
1> binary:longest_common_suffix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"fang"&gt;&gt;]).
@@ -347,35 +290,24 @@
</desc>
</func>
<func>
- <name>match(Subject, Pattern) -> Found | <c>nomatch</c></name>
+ <name name="match" arity="2"/>
<fsummary>Searches for the first match of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = part()</v>
- </type>
<desc>
- <p>The same as <c>match(Subject, Pattern, [])</c>.</p>
+ <p>The same as <c>match(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p>
</desc>
</func>
<func>
- <name>match(Subject,Pattern,Options) -> Found | <c>nomatch</c></name>
+ <name name="match" arity="3"/>
+ <type name="part"/>
<fsummary>Searches for the first match of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = part()</v>
- <v>Options = [ Option ]</v>
- <v>Option = {scope, part()}</v>
- </type>
<desc>
- <p>Searches for the first occurrence of <c>Pattern</c> in <c>Subject</c> and
+ <p>Searches for the first occurrence of <c><anno>Pattern</anno></c> in <c><anno>Subject</anno></c> and
returns the position and length.</p>
- <p>The function will return <c>{Pos,Length}</c> for the binary
- in <c>Pattern</c> starting at the lowest position in
- <c>Subject</c>, Example:</p>
+ <p>The function will return <c>{Pos, Length}</c> for the binary
+ in <c><anno>Pattern</anno></c> starting at the lowest position in
+ <c><anno>Subject</anno></c>, Example:</p>
<code>
1> binary:match(&lt;&lt;"abcde"&gt;&gt;, [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"cd"&gt;&gt;],[]).
@@ -391,16 +323,16 @@
<p>Summary of the options:</p>
<taglist>
- <tag>{scope, {Start, Length}}</tag>
+ <tag>{scope, {<anno>Start</anno>, <anno>Length</anno>}}</tag>
<item><p>Only the given part is searched. Return values still have
- offsets from the beginning of <c>Subject</c>. A negative <c>Length</c> is
- allowed as described in the <c>TYPES</c> section of this manual.</p></item>
+ offsets from the beginning of <c><anno>Subject</anno></c>. A negative <c>Length</c> is
+ allowed as described in the <c>DATA TYPES</c> section of this manual.</p></item>
</taglist>
<p>If none of the strings in
- <c>Pattern</c> is found, the atom <c>nomatch</c> is returned.</p>
+ <c><anno>Pattern</anno></c> is found, the atom <c>nomatch</c> is returned.</p>
- <p>For a description of <c>Pattern</c>, see
+ <p>For a description of <c><anno>Pattern</anno></c>, see
<seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
<p>If <c>{scope, {Start,Length}}</c> is given in the options
@@ -412,32 +344,21 @@
</desc>
</func>
<func>
- <name>matches(Subject, Pattern) -> Found</name>
+ <name name="matches" arity="2"/>
<fsummary>Searches for all matches of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = [ part() ] | []</v>
- </type>
<desc>
- <p>The same as <c>matches(Subject, Pattern, [])</c>.</p>
+ <p>The same as <c>matches(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p>
</desc>
</func>
<func>
- <name>matches(Subject,Pattern,Options) -> Found</name>
+ <name name="matches" arity="3"/>
+ <type name="part"/>
<fsummary>Searches for all matches of a pattern in a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pattern = binary() | [ binary() ] | cp()</v>
- <v>Found = [ part() ] | []</v>
- <v>Options = [ Option ]</v>
- <v>Option = {scope, part()}</v>
- </type>
<desc>
- <p>Works like match, but the <c>Subject</c> is searched until
+ <p>Works like <c>match/2</c>, but the <c><anno>Subject</anno></c> is searched until
exhausted and a list of all non-overlapping parts matching
- <c>Pattern</c> is returned (in order). </p>
+ <c><anno>Pattern</anno></c> is returned (in order). </p>
<p>The first and longest match is preferred to a shorter,
which is illustrated by the following example:</p>
@@ -458,26 +379,22 @@
<p>If none of the strings in pattern is found, an empty list is returned.</p>
- <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a
+ <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a
description of available options, see <seealso marker="#match-3">match/3</seealso>.</p>
- <p>If <c>{scope, {Start,Length}}</c> is given in the options such that
- <c>Start</c> is larger than the size of <c>Subject</c>, <c>Start + Length</c> is
- less than zero or <c>Start + Length</c> is larger than the size of
- <c>Subject</c>, a <c>badarg</c> exception is raised.</p>
+ <p>If <c>{scope, {<anno>Start</anno>,<anno>Length</anno>}}</c> is given in the options such that
+ <c><anno>Start</anno></c> is larger than the size of <c><anno>Subject</anno></c>, <c><anno>Start</anno> + <anno>Length</anno></c> is
+ less than zero or <c><anno>Start</anno> + <anno>Length</anno></c> is larger than the size of
+ <c><anno>Subject</anno></c>, a <c>badarg</c> exception is raised.</p>
</desc>
</func>
<func>
- <name>part(Subject, PosLen) -> binary()</name>
+ <name name="part" arity="2"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>PosLen = part()</v>
- </type>
<desc>
- <p>Extracts the part of the binary <c>Subject</c> described by <c>PosLen</c>.</p>
+ <p>Extracts the part of the binary <c><anno>Subject</anno></c> described by <c><anno>PosLen</anno></c>.</p>
<p>Negative length can be used to extract bytes at the end of a binary:</p>
@@ -494,25 +411,20 @@
<c>binary_part/3</c>. Those BIFs are allowed in guard tests.</p>
</note>
- <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception
+ <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception
is raised.</p>
</desc>
</func>
<func>
- <name>part(Subject, Pos, Len) -> binary()</name>
+ <name name="part" arity="3"/>
<fsummary>Extracts a part of a binary</fsummary>
- <type>
- <v>Subject = binary()</v>
- <v>Pos = integer() >= 0</v>
- <v>Len = integer()</v>
- </type>
<desc>
- <p>The same as <c>part(Subject, {Pos, Len})</c>.</p>
+ <p>The same as <c>part(<anno>Subject</anno>, {<anno>Pos</anno>, <anno>Len</anno>})</c>.</p>
</desc>
</func>
<func>
- <name>referenced_byte_size(binary()) -> integer() >= 0</name>
+ <name name="referenced_byte_size" arity="1"/>
<fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml
index 386ed89fe1..df7bf883fc 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>2012</year>
+ <year>1996</year><year>2013</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,65 @@
</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>,
+ <c>read_encoding_from_binary/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="read_encoding_from_binary" arity="1"/>
+ <name name="read_encoding_from_binary" arity="2"/>
+ <fsummary>Read the encoding from a binary</fsummary>
+ <desc>
+ <p>Read the <seealso marker="#encoding">encoding</seealso> from
+ a binary. 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 487b06473f..6db1ae3aa3 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2012</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -128,11 +128,17 @@
<datatypes>
<datatype>
+ <name name="access"/>
+ </datatype>
+ <datatype>
<name><marker id="type-continuation">continuation()</marker></name>
<desc>
<p>Opaque continuation used by <seealso marker="#select/1">
- <c>select/1</c></seealso> and <seealso marker="#select/3">
- <c>select/3</c></seealso>.</p>
+ <c>select/1,3</c></seealso>, <seealso marker="#select_reverse/1">
+ <c>select_reverse/1,3</c></seealso>, <seealso
+ marker="#match/1">
+ <c>match/1,3</c></seealso>, and <seealso marker="#match_object/1">
+ <c>match_object/1,3</c></seealso>.</p>
</desc>
</datatype>
<datatype>
@@ -140,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>
@@ -149,14 +159,14 @@
<name name="tid"/>
<desc><p>A table identifier, as returned by new/2.</p></desc>
</datatype>
+ <datatype>
+ <name name="type"/>
+ </datatype>
</datatypes>
<funcs>
<func>
- <name>all() -> [Tab]</name>
+ <name name="all" arity="0"/>
<fsummary>Return a list of all ETS tables.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
<p>Returns a list of all tables at the node. Named tables are
given by their names, unnamed tables are given by their
@@ -164,48 +174,34 @@
</desc>
</func>
<func>
- <name>delete(Tab) -> true</name>
+ <name name="delete" arity="1"/>
<fsummary>Delete an entire ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
- <p>Deletes the entire table <c>Tab</c>.</p>
+ <p>Deletes the entire table <c><anno>Tab</anno></c>.</p>
</desc>
</func>
<func>
- <name>delete(Tab, Key) -> true</name>
+ <name name="delete" arity="2"/>
<fsummary>Delete all objects with a given key from an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
- <p>Deletes all objects with the key <c>Key</c> from the table
- <c>Tab</c>.</p>
+ <p>Deletes all objects with the key <c><anno>Key</anno></c> from the table
+ <c><anno>Tab</anno></c>.</p>
</desc>
</func>
<func>
- <name>delete_all_objects(Tab) -> true</name>
+ <name name="delete_all_objects" arity="1"/>
<fsummary>Delete all objects in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
- <p>Delete all objects in the ETS table <c>Tab</c>.
+ <p>Delete all objects in the ETS table <c><anno>Tab</anno></c>.
The operation is guaranteed to be
<seealso marker="#concurrency">atomic and isolated</seealso>.</p>
</desc>
</func>
<func>
- <name>delete_object(Tab,Object) -> true</name>
+ <name name="delete_object" arity="2"/>
<fsummary>Deletes a specific from an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
- </type>
<desc>
- <p>Delete the exact object <c>Object</c> from the ETS table,
+ <p>Delete the exact object <c><anno>Object</anno></c> from the ETS table,
leaving objects with the same key but other differences
(useful for type <c>bag</c>). In a <c>duplicate_bag</c>, all
instances of the object will be deleted.</p>
@@ -257,14 +253,10 @@
</desc>
</func>
<func>
- <name>first(Tab) -> Key | '$end_of_table'</name>
+ <name name="first" arity="1"/>
<fsummary>Return the first key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
- <p>Returns the first key <c>Key</c> in the table <c>Tab</c>.
+ <p>Returns the first key <c><anno>Key</anno></c> in the table <c><anno>Tab</anno></c>.
If the table is of the <c>ordered_set</c> type, the first key
in Erlang term order will be returned. If the table is of any
other type, the first key according to the table's internal
@@ -336,7 +328,7 @@
the source file.</p>
<p>The fun is very restricted, it can take only a single
parameter (the object to match): a sole variable or a
- tuple. It needs to use the <c>is_</c>XXX guard tests.
+ tuple. It needs to use the <c>is_</c> guard tests.
Language constructs that have no representation
in a match_spec (like <c>if</c>, <c>case</c>, <c>receive</c>
etc) are not allowed.</p>
@@ -386,19 +378,14 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>give_away(Tab, Pid, GiftData) -> true</name>
+ <name name="give_away" arity="3"/>
<fsummary>Change owner of a table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pid = pid()</v>
- <v>GiftData = term()</v>
- </type>
<desc>
- <p>Make process <c>Pid</c> the new owner of table <c>Tab</c>.
+ <p>Make process <c><anno>Pid</anno></c> the new owner of table <c><anno>Tab</anno></c>.
If successful, the message
- <c>{'ETS-TRANSFER',Tab,FromPid,GiftData}</c> will be sent
+ <c>{'ETS-TRANSFER',<anno>Tab</anno>,FromPid,<anno>GiftData</anno>}</c> will be sent
to the new owner.</p>
- <p>The process <c>Pid</c> must be alive, local and not already the
+ <p>The process <c><anno>Pid</anno></c> must be alive, local and not already the
owner of the table. The calling process must be the table owner.</p>
<p>Note that <c>give_away</c> does not at all affect the
<seealso marker="#heir">heir</seealso> option of the table. A table
@@ -421,81 +408,72 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>info(Tab) -> [{Item, Value}] | undefined</name>
+ <name name="info" arity="1"/>
<fsummary>Return information about an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Item = atom(), see below</v>
- <v>Value = term(), see below</v>
- </type>
<desc>
- <p>Returns information about the table <c>Tab</c> as a list of
- <c>{Item, Value}</c> tuples. If <c>Tab</c> has the correct type
+ <p>Returns information about the table <c><anno>Tab</anno></c> as a list of
+ tuples. If <c><anno>Tab</anno></c> has the correct type
for a table identifier, but does not refer to an existing ETS
- table, <c>undefined</c> is returned. If <c>Tab</c> is not of the
+ table, <c>undefined</c> is returned. If <c><anno>Tab</anno></c> is not of the
correct type, this function fails with reason <c>badarg</c>.</p>
<list type="bulleted">
- <item><c>Item=memory, Value=integer()</c> <br></br>
+ <item><c>{compressed, boolean()}</c> <br></br>
- The number of words allocated to the table.</item>
- <item><c>Item=owner, Value=pid()</c> <br></br>
-
- The pid of the owner of the table.</item>
- <item><c>Item=heir, Value=pid()|none</c> <br></br>
+ Indicates if the table is compressed or not.</item>
+ <item><c>{heir, pid() | none}</c> <br></br>
The pid of the heir of the table, or <c>none</c> if no heir is set.</item>
- <item><c>Item=name, Value=atom()</c> <br></br>
+ <item><c>{keypos, integer() >= 1}</c> <br></br>
- The name of the table.</item>
- <item><c>Item=size, Value=integer()</c> <br></br>
+ The key position.</item>
+ <item><c>{memory, integer() >= 0</c> <br></br>
- The number of objects inserted in the table.</item>
- <item><c>Item=node, Value=atom()</c> <br></br>
+ The number of words allocated to the table.</item>
+ <item><c>{name, atom()}</c> <br></br>
- The node where the table is stored. This field is no longer
- meaningful as tables cannot be accessed from other nodes.</item>
- <item><c>Item=named_table, Value=true|false</c> <br></br>
+ The name of the table.</item>
+ <item><c>{named_table, boolean()}</c> <br></br>
Indicates if the table is named or not.</item>
- <item><c>Item=type, Value=set|ordered_set|bag|duplicate_bag</c> <br></br>
+ <item><c>{node, node()}</c> <br></br>
- The table type.</item>
- <item><c>Item=keypos, Value=integer()</c> <br></br>
+ The node where the table is stored. This field is no longer
+ meaningful as tables cannot be accessed from other nodes.</item>
+ <item><c>{owner, pid()}</c> <br></br>
- The key position.</item>
- <item><c>Item=protection, Value=public|protected|private</c> <br></br>
+ The pid of the owner of the table.</item>
+ <item><c>{protection, <seealso marker="#type-access">access()</seealso>}</c> <br></br>
The table access rights.</item>
- <item><c>Item=compressed, Value=true|false</c> <br></br>
+ <item><c>{size, integer() >= 0</c> <br></br>
- Indicates if the table is compressed or not.</item>
+ The number of objects inserted in the table.</item>
+ <item><c>{type, <seealso marker="#type-type">type()</seealso>}</c> <br></br>
+
+ The table type.</item>
</list>
</desc>
</func>
<func>
- <name>info(Tab, Item) -> Value | undefined</name>
+ <name name="info" arity="2"/>
<fsummary>Return the information associated with given item for an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Item, Value - see below</v>
- </type>
<desc>
<p>Returns the information associated with <c>Item</c> for
- the table <c>Tab</c>, or returns <c>undefined</c> if <c>Tab</c>
+ the table <c><anno>Tab</anno></c>, or returns <c>undefined</c> if <c>Tab</c>
does not refer an existing ETS table.
- If <c>Tab</c> is not of the correct type, or if <c>Item</c> is not
+ If <c><anno>Tab</anno></c> is not of the correct type, or if <c><anno>Item</anno></c> is not
one of the allowed values, this function fails with reason <c>badarg</c>.</p>
<warning><p>In R11B and earlier, this function would not fail but return
<c>undefined</c> for invalid values for <c>Item</c>.</p>
</warning>
- <p>In addition to the <c>{Item,Value}</c>
+ <p>In addition to the <c>{<anno>Item</anno>,<anno>Value</anno>}</c>
pairs defined for <c>info/1</c>, the following items are
allowed:</p>
<list type="bulleted">
- <item><c>Item=fixed, Value=true|false</c> <br></br>
+ <item><c>Item=fixed, Value=boolean()</c> <br></br>
Indicates if the table is fixed by any process or not.</item>
<item>
@@ -547,15 +525,11 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>insert(Tab, ObjectOrObjects) -> true</name>
+ <name name="insert" arity="2"/>
<fsummary>Insert an object into an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>ObjectOrObjects = tuple() | [tuple()]</v>
- </type>
<desc>
<p>Inserts the object or all of the objects in the list
- <c>ObjectOrObjects</c> into the table <c>Tab</c>.
+ <c><anno>ObjectOrObjects</anno></c> into the table <c><anno>Tab</anno></c>.
If the table is a <c>set</c> and the key of the inserted
objects <em>matches</em> the key of any object in the table,
the old object will be replaced. If the table is an
@@ -572,19 +546,15 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>insert_new(Tab, ObjectOrObjects) -> boolean()</name>
+ <name name="insert_new" arity="2"/>
<fsummary>Insert an object into an ETS table if the key is not already present.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>ObjectOrObjects = tuple() | [tuple()]</v>
- </type>
<desc>
<p>This function works exactly like <c>insert/2</c>, with the
exception that instead of overwriting objects with the same
key (in the case of <c>set</c> or <c>ordered_set</c>) or
adding more objects with keys already existing in the table
(in the case of <c>bag</c> and <c>duplicate_bag</c>), it
- simply returns <c>false</c>. If <c>ObjectOrObjects</c> is a
+ simply returns <c>false</c>. If <c><anno>ObjectOrObjects</anno></c> is a
list, the function checks <em>every</em> key prior to
inserting anything. Nothing will be inserted if not
<em>all</em> keys present in the list are absent from the
@@ -593,11 +563,8 @@ Error: fun containing local Erlang function calls
</desc>
</func>
<func>
- <name>is_compiled_ms(Term) -> boolean()</name>
+ <name name="is_compiled_ms" arity="1"/>
<fsummary>Checks if an Erlang term is the result of ets:match_spec_compile</fsummary>
- <type>
- <v>Term = term()</v>
- </type>
<desc>
<p>This function is used to check if a term is a valid
compiled <seealso marker="#match_spec">match_spec</seealso>.
@@ -626,14 +593,10 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>last(Tab) -> Key | '$end_of_table'</name>
+ <name name="last" arity="1"/>
<fsummary>Return the last key in an ETS table of type<c>ordered_set</c>.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
- <p>Returns the last key <c>Key</c> according to Erlang term
+ <p>Returns the last key <c><anno>Key</anno></c> according to Erlang term
order in the table <c>Tab</c> of the <c>ordered_set</c> type.
If the table is of any other type, the function is synonymous
to <c>first/2</c>. If the table is empty,
@@ -642,16 +605,11 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>lookup(Tab, Key) -> [Object]</name>
+ <name name="lookup" arity="2"/>
<fsummary>Return all objects with a given key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- <v>Object = tuple()</v>
- </type>
<desc>
- <p>Returns a list of all objects with the key <c>Key</c> in
- the table <c>Tab</c>.</p>
+ <p>Returns a list of all objects with the key <c><anno>Key</anno></c> in
+ the table <c><anno>Tab</anno></c>.</p>
<p>In the case of <c>set, bag and duplicate_bag</c>, an object
is returned only if the given key <em>matches</em> the key
of the object in the table. If the table is an
@@ -681,22 +639,16 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>lookup_element(Tab, Key, Pos) -> Elem</name>
+ <name name="lookup_element" arity="3"/>
<fsummary>Return the <c>Pos</c>:th element of all objects with a given key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- <v>Pos = integer()</v>
- <v>Elem = term() | [term()]</v>
- </type>
<desc>
- <p>If the table <c>Tab</c> is of type <c>set</c> or
- <c>ordered_set</c>, the function returns the <c>Pos</c>:th
- element of the object with the key <c>Key</c>.</p>
+ <p>If the table <c><anno>Tab</anno></c> is of type <c>set</c> or
+ <c>ordered_set</c>, the function returns the <c><anno>Pos</anno></c>:th
+ element of the object with the key <c><anno>Key</anno></c>.</p>
<p>If the table is of type <c>bag</c> or <c>duplicate_bag</c>,
- the functions returns a list with the <c>Pos</c>:th element of
- every object with the key <c>Key</c>.</p>
- <p>If no object with the key <c>Key</c> exists, the function
+ the functions returns a list with the <c><anno>Pos</anno></c>:th element of
+ every object with the key <c><anno>Key</anno></c>.</p>
+ <p>If no object with the key <c><anno>Key</anno></c> exists, the function
will exit with reason <c>badarg</c>.</p>
<p>The difference between <c>set</c>, <c>bag</c> and
<c>duplicate_bag</c> on one hand, and <c>ordered_set</c> on
@@ -708,16 +660,11 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match(Tab, Pattern) -> [Match]</name>
+ <name name="match" arity="2"/>
<fsummary>Match the objects in an ETS table against a pattern.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = tuple()</v>
- <v>Match = [term()]</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> against the
- pattern <c>Pattern</c>.</p>
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> against the
+ pattern <c><anno>Pattern</anno></c>.</p>
<p>A pattern is a term that may contain:</p>
<list type="bulleted">
<item>bound parts (Erlang terms),</item>
@@ -744,18 +691,12 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match" arity="3"/>
<fsummary>Match the objects in an ETS table against a pattern and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = tuple()</v>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>ets:match/2</c> but only returns a limited
- (<c>Limit</c>) number of matching objects. The
- <c>Continuation</c> term can then be used in subsequent calls
+ (<c><anno>Limit</anno></c>) number of matching objects. The
+ <c><anno>Continuation</anno></c> term can then be used in subsequent calls
to <c>ets:match/1</c> to get the next chunk of matching
objects. This is a space efficient way to work on objects in a
table which is still faster than traversing the table object
@@ -764,16 +705,12 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match" arity="1"/>
<fsummary>Continues matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with <c>ets:match/3</c>. The next
chunk of the size given in the initial <c>ets:match/3</c>
- call is returned together with a new <c>Continuation</c>
+ call is returned together with a new <c><anno>Continuation</anno></c>
that can be used in subsequent calls to this function.</p>
<p><c>'$end_of_table'</c> is returned when there are no more
objects in the table.</p>
@@ -789,15 +726,11 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_object(Tab, Pattern) -> [Object]</name>
+ <name name="match_object" arity="2"/>
<fsummary>Match the objects in an ETS table against a pattern.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = Object = tuple()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> against the
- pattern <c>Pattern</c>. See <c>match/2</c> for a description
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> against the
+ pattern <c><anno>Pattern</anno></c>. See <c>match/2</c> for a description
of patterns. The function returns a list of all objects which
match the pattern.</p>
<p>If the key is specified in the pattern, the match is very
@@ -809,18 +742,12 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_object(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match_object" arity="3"/>
<fsummary>Match the objects in an ETS table against a pattern and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Pattern = tuple()</v>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>ets:match_object/2</c> but only returns a
- limited (<c>Limit</c>) number of matching objects. The
- <c>Continuation</c> term can then be used in subsequent calls
+ limited (<c><anno>Limit</anno></c>) number of matching objects. The
+ <c><anno>Continuation</anno></c> term can then be used in subsequent calls
to <c>ets:match_object/1</c> to get the next chunk of matching
objects. This is a space efficient way to work on objects in a
table which is still faster than traversing the table object
@@ -829,29 +756,21 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_object(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="match_object" arity="1"/>
<fsummary>Continues matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = [term()]</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with <c>ets:match_object/3</c>.
The next chunk of the size given in the initial
<c>ets:match_object/3</c> call is returned together with a
- new <c>Continuation</c> that can be used in subsequent calls
+ new <c><anno>Continuation</anno></c> that can be used in subsequent calls
to this function.</p>
<p><c>'$end_of_table'</c> is returned when there are no more
objects in the table.</p>
</desc>
</func>
<func>
- <name>match_spec_compile(MatchSpec) -> CompiledMatchSpec</name>
+ <name name="match_spec_compile" arity="1"/>
<fsummary>Compiles a match specification into its internal representation</fsummary>
- <type>
- <v>MatchSpec = match_spec()</v>
- <v>CompiledMatchSpec = comp_match_spec()</v>
- </type>
<desc>
<p>This function transforms a
<seealso marker="#match_spec">match_spec</seealso> into an
@@ -863,7 +782,7 @@ ets:is_compiled_ms(Broken).</code>
valid compiled match_spec, nor can it be stored on disk).
The validity of a compiled match_spec can be checked using
<c>ets:is_compiled_ms/1</c>.</p>
- <p>If the term <c>MatchSpec</c> can not be compiled (does not
+ <p>If the term <c><anno>MatchSpec</anno></c> can not be compiled (does not
represent a valid match_spec), a <c>badarg</c> fault is
thrown.</p>
<note>
@@ -873,25 +792,21 @@ ets:is_compiled_ms(Broken).</code>
</desc>
</func>
<func>
- <name>match_spec_run(List,CompiledMatchSpec) -> list()</name>
+ <name name="match_spec_run" arity="2"/>
<fsummary>Performs matching, using a compiled match_spec, on a list of tuples</fsummary>
- <type>
- <v>List = [ tuple() ]</v>
- <v>CompiledMatchSpec = comp_match_spec()</v>
- </type>
<desc>
<p>This function executes the matching specified in a
compiled <seealso marker="#match_spec">match_spec</seealso> on
- a list of tuples. The <c>CompiledMatchSpec</c> term should be
+ a list of tuples. The <c><anno>CompiledMatchSpec</anno></c> term should be
the result of a call to <c>ets:match_spec_compile/1</c> and
is hence the internal representation of the match_spec one
wants to use.</p>
- <p>The matching will be executed on each element in <c>List</c>
+ <p>The matching will be executed on each element in <c><anno>List</anno></c>
and the function returns a list containing all results. If an
- element in <c>List</c> does not match, nothing is returned
+ element in <c><anno>List</anno></c> does not match, nothing is returned
for that element. The length of the result list is therefore
equal or less than the the length of the parameter
- <c>List</c>. The two calls in the following example will give
+ <c><anno>List</anno></c>. The two calls in the following example will give
the same result (but certainly not the same execution
time...):</p>
<code type="none">
@@ -910,37 +825,23 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>member(Tab, Key) -> true | false</name>
+ <name name="member" arity="2"/>
<fsummary>Tests for occurrence of a key in an ETS table</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- </type>
<desc>
<p>Works like <c>lookup/2</c>, but does not return the objects.
The function returns <c>true</c> if one or more elements in
- the table has the key <c>Key</c>, <c>false</c> otherwise.</p>
+ the table has the key <c><anno>Key</anno></c>, <c>false</c> otherwise.</p>
</desc>
</func>
<func>
- <name>new(Name, Options) -> tid() | atom()</name>
+ <name name="new" arity="2"/>
<fsummary>Create a new ETS table.</fsummary>
- <type>
- <v>Name = atom()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v>
- <v>&nbsp;&nbsp;Type = set | ordered_set | bag | duplicate_bag</v>
- <v>&nbsp;&nbsp;Access = public | protected | private</v>
- <v>&nbsp;&nbsp;Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed</v>
- <v>&nbsp;&nbsp;Pos = integer()</v>
- <v>&nbsp;&nbsp;HeirData = term()</v>
- </type>
<desc>
<p>Creates a new table and returns a table identifier which can
be used in subsequent operations. The table identifier can be
sent to other processes so that a table can be shared between
different processes within a node.</p>
- <p>The parameter <c>Options</c> is a list of atoms which
+ <p>The parameter <c><anno>Options</anno></c> is a list of atoms which
specifies table type, access rights, key position and if the
table is named or not. If one or more options are left out,
the default values are used. This means that not specifying
@@ -997,27 +898,27 @@ ets:select(Table,MatchSpec),</code>
</item>
<item>
<p><c>named_table</c>
- If this option is present, the name <c>Name</c> is
+ If this option is present, the name <c><anno>Name</anno></c> is
associated with the table identifier. The name can then
be used instead of the table identifier in subsequent
operations.</p>
</item>
<item>
- <p><c>{keypos,Pos}</c>
+ <p><c>{keypos,<anno>Pos</anno>}</c>
Specfies which element in the stored tuples should be
used as key. By default, it is the first element, i.e.
- <c>Pos=1</c>. However, this is not always appropriate. In
+ <c><anno>Pos</anno>=1</c>. However, this is not always appropriate. In
particular, we do not want the first element to be the
key if we want to store Erlang records in a table.</p>
<p>Note that any tuple stored in the table must have at
- least <c>Pos</c> number of elements.</p>
+ least <c><anno>Pos</anno></c> number of elements.</p>
</item>
<item>
<marker id="heir"></marker>
- <p><c>{heir,Pid,HeirData} | {heir,none}</c><br></br>
+ <p><c>{heir,<anno>Pid</anno>,<anno>HeirData</anno>} | {heir,none}</c><br></br>
Set a process as heir. The heir will inherit the table if
the owner terminates. The message
- <c>{'ETS-TRANSFER',tid(),FromPid,HeirData}</c> will be sent to
+ <c>{'ETS-TRANSFER',tid(),FromPid,<anno>HeirData</anno>}</c> will be sent to
the heir when that happens. The heir must be a local process.
Default heir is <c>none</c>, which will destroy the table when
the owner terminates.</p>
@@ -1031,7 +932,8 @@ ets:select(Table,MatchSpec),</code>
If set to <c>true</c>, the table is optimized towards concurrent
write access. Different objects of the same table can be mutated
(and read) by concurrent processes. This is achieved to some degree
- at the expense of sequential access and concurrent reader performance.
+ at the expense of memory consumption and the performance of
+ sequential access and concurrent reading.
The <c>write_concurrency</c> option can be combined with the
<seealso marker="#new_2_read_concurrency">read_concurrency</seealso>
option. You typically want to combine these when large concurrent
@@ -1043,8 +945,11 @@ ets:select(Table,MatchSpec),</code>
<seealso marker="#concurrency">atomicy and isolation</seealso>.
Functions that makes such promises over several objects (like
<c>insert/2</c>) will gain less (or nothing) from this option.</p>
- <p>Table type <c>ordered_set</c> is not affected by this option in current
- implementation.</p>
+ <p>In current implementation, table type <c>ordered_set</c> is not
+ affected by this option. Also, the memory consumption inflicted by
+ both <c>write_concurrency</c> and <c>read_concurrency</c> is a
+ constant overhead per table. This overhead can be especially large
+ when both options are combined.</p>
</item>
<item>
<marker id="new_2_read_concurrency"></marker>
@@ -1082,15 +987,11 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>next(Tab, Key1) -> Key2 | '$end_of_table'</name>
+ <name name="next" arity="2"/>
<fsummary>Return the next key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key1 = Key2 = term()</v>
- </type>
<desc>
- <p>Returns the next key <c>Key2</c>, following the key
- <c>Key1</c> in the table <c>Tab</c>. If the table is of the
+ <p>Returns the next key <c><anno>Key2</anno></c>, following the key
+ <c><anno>Key1</anno></c> in the table <c><anno>Tab</anno></c>. If the table is of the
<c>ordered_set</c> type, the next key in Erlang term order is
returned. If the table is of any other type, the next key
according to the table's internal order is returned. If there
@@ -1105,16 +1006,12 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>prev(Tab, Key1) -> Key2 | '$end_of_table'</name>
+ <name name="prev" arity="2"/>
<fsummary>Return the previous key in an ETS table of type<c>ordered_set</c>.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key1 = Key2 = term()</v>
- </type>
<desc>
- <p>Returns the previous key <c>Key2</c>, preceding the key
- <c>Key1</c> according the Erlang term order in the table
- <c>Tab</c> of the <c>ordered_set</c> type. If the table is of
+ <p>Returns the previous key <c><anno>Key2</anno></c>, preceding the key
+ <c><anno>Key1</anno></c> according the Erlang term order in the table
+ <c><anno>Tab</anno></c> of the <c>ordered_set</c> type. If the table is of
any other type, the function is synonymous to <c>next/2</c>.
If there is no previous key, <c>'$end_of_table'</c> is
returned.</p>
@@ -1122,14 +1019,11 @@ ets:select(Table,MatchSpec),</code>
</desc>
</func>
<func>
- <name>rename(Tab, Name) -> Name</name>
+ <name name="rename" arity="2"/>
<fsummary>Rename a named ETS table.</fsummary>
- <type>
- <v>Tab = Name = atom()</v>
- </type>
<desc>
- <p>Renames the named table <c>Tab</c> to the new name
- <c>Name</c>. Afterwards, the old name can not be used to
+ <p>Renames the named table <c><anno>Tab</anno></c> to the new name
+ <c><anno>Name</anno></c>. Afterwards, the old name can not be used to
access the table. Renaming an unnamed table has no effect.</p>
</desc>
</func>
@@ -1186,18 +1080,15 @@ ets:select(ets:repair_continuation(Broken,MS)).</code>
</desc>
</func>
<func>
- <name>safe_fixtable(Tab, true|false) -> true</name>
+ <name name="safe_fixtable" arity="2"/>
<fsummary>Fix an ETS table for safe traversal.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- </type>
<desc>
<p>Fixes a table of the <c>set</c>, <c>bag</c> or
<c>duplicate_bag</c> table type for safe traversal.</p>
<p>A process fixes a table by calling
- <c>safe_fixtable(Tab,true)</c>. The table remains fixed until
+ <c>safe_fixtable(<anno>Tab</anno>, true)</c>. The table remains fixed until
the process releases it by calling
- <c>safe_fixtable(Tab,false)</c>, or until the process
+ <c>safe_fixtable(<anno>Tab</anno>, false)</c>, or until the process
terminates.</p>
<p>If several processes fix a table, the table will remain fixed
until all processes have released it (or terminated).
@@ -1242,15 +1133,10 @@ clean_all_with_value(Tab,X,Key) ->
</desc>
</func>
<func>
- <name>select(Tab, MatchSpec) -> [Match]</name>
+ <name name="select" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
<seealso marker="#match_spec">match_spec</seealso>. This is a
more general call than the <c>ets:match/2</c> and
<c>ets:match_object/2</c> calls. In its simplest forms the
@@ -1337,18 +1223,12 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select" arity="3"/>
<fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>ets:select/2</c> but only returns a limited
- (<c>Limit</c>) number of matching objects. The
- <c>Continuation</c> term can then be used in subsequent calls
+ (<c><anno>Limit</anno></c>) number of matching objects. The
+ <c><anno>Continuation</anno></c> term can then be used in subsequent calls
to <c>ets:select/1</c> to get the next chunk of matching
objects. This is a space efficient way to work on objects in a
table which is still faster than traversing the table object
@@ -1357,33 +1237,23 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select" arity="1"/>
<fsummary>Continue matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = term()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with
<c>ets:select/3</c>. The next
chunk of the size given in the initial <c>ets:select/3</c>
- call is returned together with a new <c>Continuation</c>
+ call is returned together with a new <c><anno>Continuation</anno></c>
that can be used in subsequent calls to this function.</p>
<p><c>'$end_of_table'</c> is returned when there are no more
objects in the table.</p>
</desc>
</func>
<func>
- <name>select_count(Tab, MatchSpec) -> NumMatched</name>
+ <name name="select_count" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
- <v>MatchSpec = match_spec()</v>
- <v>NumMatched = integer()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
<seealso marker="#match_spec">match_spec</seealso>. If the
match_spec returns <c>true</c> for an object, that object
considered a match and is counted. For any other result from
@@ -1396,16 +1266,10 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_delete(Tab, MatchSpec) -> NumDeleted</name>
+ <name name="select_delete" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec and deletes objects where the match_spec returns 'true'</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
- <v>MatchSpec = match_spec()</v>
- <v>NumDeleted = integer()</v>
- </type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
<seealso marker="#match_spec">match_spec</seealso>. If the
match_spec returns <c>true</c> for an object, that object is
removed from the table. For any other result from the
@@ -1422,13 +1286,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_reverse(Tab, MatchSpec) -> [Match]</name>
+ <name name="select_reverse" arity="2"/>
<fsummary>Match the objects in an ETS table against a match_spec.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- </type>
<desc>
<p>Works like <c>select/2</c>, but returns the list in reverse
@@ -1438,14 +1297,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select_reverse" arity="3"/>
<fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Match = term()</v>
- <v>MatchSpec = match_spec()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Works like <c>select/3</c>, but for the <c>ordered_set</c>
@@ -1456,18 +1309,14 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
<p>Note that this is <em>not</em> equivalent to
reversing the result list of a <c>select/3</c> call, as the result list
- is not only reversed, but also contains the last <c>Limit</c>
+ is not only reversed, but also contains the last <c><anno>Limit</anno></c>
matching objects in the table, not the first.</p>
</desc>
</func>
<func>
- <name>select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <name name="select_reverse" arity="1"/>
<fsummary>Continue matching objects in an ETS table.</fsummary>
- <type>
- <v>Match = term()</v>
- <v>Continuation = term()</v>
- </type>
<desc>
<p>Continues a match started with
@@ -1477,7 +1326,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
returned list will also contain objects with keys in reverse
order.</p>
- <p>For all other table types, the behaviour is exatly that of <c>select/1</c>.</p>
+ <p>For all other table types, the behaviour is exactly that of <c>select/1</c>.</p>
<p>Example:</p>
<code>
1> T = ets:new(x,[ordered_set]).
@@ -1501,14 +1350,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>setopts(Tab, Opts) -> true</name>
+ <name name="setopts" arity="2"/>
<fsummary>Set table options.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Opts = Opt | [Opt]</v>
- <v>Opt = {heir,pid(),HeirData} | {heir,none}</v>
- <v>HeirData = term()</v>
- </type>
<desc>
<p>Set table options. The only option that currently is allowed to be
set after the table has been created is
@@ -1517,28 +1360,23 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>slot(Tab, I) -> [Object] | '$end_of_table'</name>
+ <name name="slot" arity="2"/>
<fsummary>Return all objects in a given slot of an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>I = integer()</v>
- <v>Object = tuple()</v>
- </type>
<desc>
<p>This function is mostly for debugging purposes, Normally
one should use <c>first/next</c> or <c>last/prev</c> instead.</p>
- <p>Returns all objects in the <c>I</c>:th slot of the table
- <c>Tab</c>. A table can be traversed by repeatedly calling
- the function, starting with the first slot <c>I=0</c> and
+ <p>Returns all objects in the <c><anno>I</anno></c>:th slot of the table
+ <c><anno>Tab</anno></c>. A table can be traversed by repeatedly calling
+ the function, starting with the first slot <c><anno>I</anno>=0</c> and
ending when <c>'$end_of_table'</c> is returned.
The function will fail with reason <c>badarg</c> if the
- <c>I</c> argument is out of range.</p>
+ <c><anno>I</anno></c> argument is out of range.</p>
<p>Unless a table of type <c>set</c>, <c>bag</c> or
<c>duplicate_bag</c> is protected using
<c>safe_fixtable/2</c>, see above, a traversal may fail if
concurrent updates are made to the table. If the table is of
type <c>ordered_set</c>, the function returns a list
- containing the <c>I</c>:th object in Erlang term order.</p>
+ containing the <c><anno>I</anno></c>:th object in Erlang term order.</p>
</desc>
</func>
<func>
@@ -1754,16 +1592,16 @@ true</pre>
</desc>
</func>
<func>
- <name>update_counter(Tab, Key, UpdateOp) -> Result</name>
- <name>update_counter(Tab, Key, [UpdateOp]) -> [Result]</name>
- <name>update_counter(Tab, Key, Incr) -> Result</name>
+ <name name="update_counter" arity="3" clause_i="1"/>
+ <name name="update_counter" arity="3" clause_i="2"/>
+ <name name="update_counter" arity="3" clause_i="3"/>
+ <type variable="Tab"/>
+ <type variable="Key"/>
+ <type variable="UpdateOp" name_i="1"/>
+ <type variable="Pos" name_i="1"/>
+ <type variable="Threshold" name_i="1"/>
+ <type variable="SetValue" name_i="1"/>
<fsummary>Update a counter object in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = term()</v>
- <v>UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}</v>
- <v>Pos = Incr = Threshold = SetValue = Result = integer()</v>
- </type>
<desc>
<p>This function provides an efficient way to update one or more
counters, without the hassle of having to look up an object, update
@@ -1771,22 +1609,22 @@ true</pre>
into the table again. (The update is done atomically; i.e. no process
can access the ets table in the middle of the operation.)
</p>
- <p>It will destructively update the object with key <c>Key</c>
- in the table <c>Tab</c> by adding <c>Incr</c> to the element
- at the <c>Pos</c>:th position. The new counter value is
+ <p>It will destructively update the object with key <c><anno>Key</anno></c>
+ in the table <c><anno>Tab</anno></c> by adding <c><anno>Incr</anno></c> to the element
+ at the <c><anno>Pos</anno></c>:th position. The new counter value is
returned. If no position is specified, the element directly
following the key (<c><![CDATA[<keypos>+1]]></c>) is updated.</p>
- <p>If a <c>Threshold</c> is specified, the counter will be
- reset to the value <c>SetValue</c> if the following
+ <p>If a <c><anno>Threshold</anno></c> is specified, the counter will be
+ reset to the value <c><anno>SetValue</anno></c> if the following
conditions occur:</p>
<list type="bulleted">
- <item>The <c>Incr</c> is not negative (<c>>= 0</c>) and the
- result would be greater than (<c>></c>) <c>Threshold</c></item>
- <item>The <c>Incr</c> is negative (<c><![CDATA[< 0]]></c>) and the
+ <item>The <c><anno>Incr</anno></c> is not negative (<c>>= 0</c>) and the
+ result would be greater than (<c>></c>) <c><anno>Threshold</anno></c></item>
+ <item>The <c><anno>Incr</anno></c> is negative (<c><![CDATA[< 0]]></c>) and the
result would be less than (<c><![CDATA[<]]></c>)
- <c>Threshold</c></item>
+ <c><anno>Threshold</anno></c></item>
</list>
- <p>A list of <c>UpdateOp</c> can be supplied to do several update
+ <p>A list of <c><anno>UpdateOp</anno></c> can be supplied to do several update
operations within the object. The operations are carried out in the
order specified in the list. If the same counter position occurs
more than one time in the list, the corresponding counter will thus
@@ -1797,7 +1635,7 @@ true</pre>
returned. If the function should fail, no updates will be done at
all.
</p>
- <p>The given Key is used to identify the object by either
+ <p>The given <c><anno>Key</anno></c> is used to identify the object by either
<em>matching</em> the key of an object in a <c>set</c> table,
or <em>compare equal</em> to the key of an object in an
<c>ordered_set</c> table (see
@@ -1812,29 +1650,28 @@ true</pre>
<item>the object has the wrong arity,</item>
<item>the element to update is not an integer,</item>
<item>the element to update is also the key, or,</item>
- <item>any of <c>Pos</c>, <c>Incr</c>, <c>Threshold</c> or
- <c>SetValue</c> is not an integer</item>
+ <item>any of <c><anno>Pos</anno></c>, <c><anno>Incr</anno></c>, <c><anno>Threshold</anno></c> or
+ <c><anno>SetValue</anno></c> is not an integer</item>
</list>
</desc>
</func>
<func>
- <name>update_element(Tab, Key, {Pos,Value}) -> true | false</name>
- <name>update_element(Tab, Key, [{Pos,Value}]) -> true | false</name>
+ <name name="update_element" arity="3" clause_i="1"/>
+ <name name="update_element" arity="3" clause_i="2"/>
+ <type variable="Tab"/>
+ <type variable="Key"/>
+ <type variable="Value"/>
+ <type variable="Pos"/>
<fsummary>Updates the <c>Pos</c>:th element of the object with a given key in an ETS table.</fsummary>
- <type>
- <v>Tab = tid() | atom()</v>
- <v>Key = Value = term()</v>
- <v>Pos = integer()</v>
- </type>
<desc>
<p>This function provides an efficient way to update one or more
elements within an object, without the hassle of having to look up,
update and write back the entire object.
</p>
- <p>It will destructively update the object with key <c>Key</c>
- in the table <c>Tab</c>. The element at the <c>Pos</c>:th position
- will be given the value <c>Value</c>. </p>
- <p>A list of <c>{Pos,Value}</c> can be supplied to update several
+ <p>It will destructively update the object with key <c><anno>Key</anno></c>
+ in the table <c><anno>Tab</anno></c>. The element at the <c><anno>Pos</anno></c>:th position
+ will be given the value <c><anno>Value</anno></c>. </p>
+ <p>A list of <c>{<anno>Pos</anno>,<anno>Value</anno>}</c> can be supplied to update several
elements within the same object. If the same position occurs more
than one in the list, the last value in the list will be written. If
the list is empty or the function fails, no updates will be done at
@@ -1842,9 +1679,9 @@ true</pre>
can never see any intermediate results.
</p>
<p>The function returns <c>true</c> if an object with the key
- <c>Key</c> was found, <c>false</c> otherwise.
+ <c><anno>Key</anno></c> was found, <c>false</c> otherwise.
</p>
- <p>The given Key is used to identify the object by either
+ <p>The given <c><anno>Key</anno></c> is used to identify the object by either
<em>matching</em> the key of an object in a <c>set</c> table,
or <em>compare equal</em> to the key of an object in an
<c>ordered_set</c> table (see
@@ -1855,7 +1692,7 @@ true</pre>
<list type="bulleted">
<item>the table is not of type <c>set</c> or
<c>ordered_set</c>,</item>
- <item><c>Pos</c> is less than 1 or greater than the object
+ <item><c><anno>Pos</anno></c> is less than 1 or greater than the object
arity, or,</item>
<item>the element to update is also the key</item>
</list>
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index f3079c7337..bd780b2b2f 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2011</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,8 +36,9 @@
<description>
<p>This module contains utilities on a higher level than the <c>file</c>
module.</p>
- <p>The module supports Unicode file names, so that it will match against regular expressions given in Unicode and that it will find and process raw file names (i.e. files named in a way that does not confirm to the expected encoding).</p>
- <p>If the VM operates in Unicode file naming mode on a machine with transparent file naming, the <c>fun()</c> provided to <c>fold_files/5</c> needs to be prepared to handle binary file names.</p>
+ <p>This module does not support "raw" file names (i.e. files whose names
+ do not comply with the expected encoding). Such files will be ignored
+ by the functions in this module.</p>
<p>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p>
</description>
@@ -150,6 +151,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 +198,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..90f24c4cbc 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>2013</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;}
@@ -213,34 +211,46 @@
</desc>
</func>
<func>
+ <name name="printable_range" arity="0"/>
+ <fsummary>Get user requested printable character range</fsummary>
+ <desc>
+ <p>Return the user requested range of printable Unicode characters.</p>
+ <p>The user can request a range of characters that are to be considered printable in heuristic detection of strings by the shell and by the formatting functions. This is done by supplying <c>+pc &lt;range&gt;</c> when starting Erlang.</p>
+ <p>Currently the only valid values for <c>&lt;range&gt;</c> are <c>latin1</c> and <c>unicode</c>. <c>latin1</c> means that only code points below 256 (with the exception of control characters etc) will be considered printable. <c>unicode</c> means that all printable characters in all unicode character ranges are considered printable by the io functions.</p>
+ <p>By default, Erlang is started so that only the <c>latin1</c> range of characters will indicate that a list of integers is a string.</p>
+ <p>The simplest way to utilize the setting is to call <seealso marker="io_lib#printable_list/1">io_lib:printable_list/1</seealso>, which will use the return value of this function to decide if a list is a string of printable characters or not.</p>
+ <note><p>In the future, this function may return more values and ranges. It is recommended to use the io_lib:printable_list/1 function to avoid compatibility problems.</p></note>
+ </desc>
+ </func>
+ <func>
<name name="setopts" arity="1"/>
<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 +263,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 +299,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
@@ -307,29 +318,43 @@
<item>
<p>The parsing failed.</p>
</item>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
+ <item>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
+ if reading from an NFS file system.</p>
+ </item>
</taglist>
</desc>
</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>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
+ <item>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
+ if reading from an NFS file system.</p>
+ </item>
</taglist>
</desc>
</func>
@@ -377,10 +402,11 @@ 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,
- is supported) that changes the interpretation of Data.</p>
-
- <p>The following control sequences are available:</p>
+ single character (currently only <c>t</c>, for Unicode
+ translation, and <c>l</c>, for stopping <c>p</c> and
+ <c>P</c> from detecting printable characters, are supported)
+ that changes the interpretation of Data.</p>
+ <p>The following control sequences are available:</p>
<taglist>
<tag><c>~</c></tag>
<item>
@@ -394,18 +420,18 @@ ok</pre>
which in turn defaults to 1. The following example
illustrates:</p>
<pre>
-2> <input>io:fwrite("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]).</input>
+1> <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>
+2> <input>io:fwrite("~tc~n",[1024]).</input>
\x{400}
ok
-2> <input>io:fwrite("~c~n",[1024]).</input>
+3> <input>io:fwrite("~c~n",[1024]).</input>
^@
ok</pre>
@@ -437,29 +463,32 @@ ok</pre>
</item>
<tag><c>s</c></tag>
<item>
- <p>Prints the argument with the <c>string</c> syntax. The
+ <p>Prints the argument with the string 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
+ <c>iolist()</c>, a <c>binary()</c>, or an <c>atom()</c>.
+ If the Unicode translation modifier (<c>t</c>) is in effect,
+ the argument is <c>unicode:chardata()</c>, meaning that
+ binaries are in UTF-8. The characters
are printed without quotes. The string is first truncated
by the given precision and then padded and justified
to the given field width. The default precision is the field width.</p>
<p>This format can be used for printing any object and
truncating the output so it fits a specified field:</p>
<pre>
-3> <input>io:fwrite("|~10w|~n", [{hey, hey, hey}]).</input>
+1> <input>io:fwrite("|~10w|~n", [{hey, hey, hey}]).</input>
|**********|
ok
-4> <input>io:fwrite("|~10s|~n", [io_lib:write({hey, hey, hey})]).</input>
+2> <input>io:fwrite("|~10s|~n", [io_lib:write({hey, hey, hey})]).</input>
|{hey,hey,h|
-5> <input>io:fwrite("|~-10.8s|~n", [io_lib:write({hey, hey, hey})]).</input>
+3> <input>io:fwrite("|~-10.8s|~n", [io_lib:write({hey, hey, hey})]).</input>
|{hey,hey |
ok</pre>
<p>A list with integers larger than 255 is considered an error if the Unicode translation modifier is not given:</p>
<pre>
-1> <input>io:fwrite("~ts~n",[[1024]]).</input>
+4> <input>io:fwrite("~ts~n",[[1024]]).</input>
\x{400}
ok
-2> io:fwrite("~s~n",[[1024]]).
+5> <input>io:fwrite("~s~n",[[1024]]).</input>
** exception exit: {badarg,[{io,format,[&lt;0.26.0&gt;,"~s~n",[[1024]]]},
...</pre>
</item>
@@ -476,20 +505,22 @@ 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>
+1> <input>T = [{attributes,[[{id,age,1.50000},{mode,explicit},</input>
<input>{typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]},</input>
<input>{typename,'Person'},{tag,{'PRIVATE',3}},{mode,implicit}].</input>
...
-6> <input>io:fwrite("~w~n", [T]).</input>
+2> <input>io:fwrite("~w~n", [T]).</input>
[{attributes,[[{id,age,1.5},{mode,explicit},{typename,
[73,78,84,69,71,69,82]}],[{id,cho},{mode,explicit},{typena
me,'Cho'}]]},{typename,'Person'},{tag,{'PRIVATE',3}},{mode
,implicit}]
ok
-7> <input>io:fwrite("~62p~n", [T]).</input>
+3> <input>io:fwrite("~62p~n", [T]).</input>
[{attributes,[[{id,age,1.5},
{mode,explicit},
{typename,"INTEGER"}],
@@ -505,7 +536,7 @@ ok</pre>
<c>io:fwrite</c> or <c>io:format</c>. For example, using
<c>T</c> above:</p>
<pre>
-8> <input>io:fwrite("Here T = ~62p~n", [T]).</input>
+4> <input>io:fwrite("Here T = ~62p~n", [T]).</input>
Here T = [{attributes,[[{id,age,1.5},
{mode,explicit},
{typename,"INTEGER"}],
@@ -516,6 +547,31 @@ Here T = [{attributes,[[{id,age,1.5},
{tag,{'PRIVATE',3}},
{mode,implicit}]
ok</pre>
+ <p>When the modifier <c>l</c> is given no detection of
+ printable character lists will take place. For example:</p>
+ <pre>
+5> <input>S = [{a,"a"}, {b, "b"}].</input>
+6> <input>io:fwrite("~15p~n", [S]).</input>
+[{a,"a"},
+ {b,"b"}]
+ok
+7> <input>io:fwrite("~15lp~n", [S]).</input>
+[{a,[97]},
+ {b,[98]}]
+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>
@@ -524,7 +580,7 @@ ok</pre>
are printed. Anything below this depth is replaced with
<c>...</c>. For example, using <c>T</c> above:</p>
<pre>
-9> <input>io:fwrite("~W~n", [T,9]).</input>
+8> <input>io:fwrite("~W~n", [T,9]).</input>
[{attributes,[[{id,age,1.5},{mode,explicit},{typename,...}],
[{id,cho},{mode,...},{...}]]},{typename,'Person'},
{tag,{'PRIVATE',3}},{mode,implicit}]
@@ -541,7 +597,7 @@ ok</pre>
are printed. Anything below this depth is replaced with
<c>...</c>. For example:</p>
<pre>
-10> <input>io:fwrite("~62P~n", [T,9]).</input>
+9> <input>io:fwrite("~62P~n", [T,9]).</input>
[{attributes,[[{id,age,1.5},{mode,explicit},{typename,...}],
[{id,cho},{mode,...},{...}]]},
{typename,'Person'},
@@ -555,13 +611,13 @@ ok</pre>
10. A leading dash is printed for negative integers.</p>
<p>The precision field selects base. For example:</p>
<pre>
-11> <input>io:fwrite("~.16B~n", [31]).</input>
+1> <input>io:fwrite("~.16B~n", [31]).</input>
1F
ok
-12> <input>io:fwrite("~.2B~n", [-19]).</input>
+2> <input>io:fwrite("~.2B~n", [-19]).</input>
-10011
ok
-13> <input>io:fwrite("~.36B~n", [5*36+35]).</input>
+3> <input>io:fwrite("~.36B~n", [5*36+35]).</input>
5Z
ok</pre>
</item>
@@ -573,22 +629,22 @@ ok</pre>
<p>The prefix can be a possibly deep list of characters or
an atom.</p>
<pre>
-14> <input>io:fwrite("~X~n", [31,"10#"]).</input>
+1> <input>io:fwrite("~X~n", [31,"10#"]).</input>
10#31
ok
-15> <input>io:fwrite("~.16X~n", [-31,"0x"]).</input>
+2> <input>io:fwrite("~.16X~n", [-31,"0x"]).</input>
-0x1F
ok</pre>
</item>
<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>
+1> <input>io:fwrite("~.10#~n", [31]).</input>
10#31
ok
-17> <input>io:fwrite("~.16#~n", [-31]).</input>
+2> <input>io:fwrite("~.16#~n", [-31]).</input>
-16#1F
ok</pre>
</item>
@@ -622,10 +678,10 @@ ok</pre>
</taglist>
<p>If an error occurs, there is no output. For example:</p>
<pre>
-18> <input>io:fwrite("~s ~w ~i ~w ~c ~n",['abc def', 'abc def', {foo, 1},{foo, 1}, 65]).</input>
+1> <input>io:fwrite("~s ~w ~i ~w ~c ~n",['abc def', 'abc def', {foo, 1},{foo, 1}, 65]).</input>
abc def 'abc def' {foo,1} A
ok
-19> <input>io:fwrite("~s", [65]).</input>
+2> <input>io:fwrite("~s", [65]).</input>
** exception exit: {badarg,[{io,format,[&lt;0.22.0>,"~s","A"]},
{erl_eval,do_apply,5},
{shell,exprs,6},
@@ -633,13 +689,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 +721,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 +747,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 +770,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 +826,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 +855,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,26 +867,40 @@ 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>
+ <p>End of file was encountered by the tokenizer.</p>
</item>
- <tag><c>{error, ErrorInfo, ErrorLine}</c></tag>
+ <tag><c>eof</c></tag>
+ <item>
+ <p>End of file was encountered by the I/O-server.</p>
+ </item>
+ <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag>
<item>
- <p>An error occurred.</p>
+ <p>An error occurred while tokenizing.</p>
</item>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
+ <item>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
+ if reading from an NFS file system.</p>
+ </item>
</taglist>
<p>Example:</p>
<pre>
@@ -840,13 +916,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,27 +936,42 @@ 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>
+ <p>End of file was encountered by the tokenizer.</p>
+ </item>
+ <tag><c>eof</c></tag>
+ <item>
+ <p>End of file was encountered by the I/O-server.</p>
</item>
- <tag><c>{error, ErrorInfo, ErrorLine}</c></tag>
+ <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag>
<item>
- <p>An error occurred.</p>
+ <p>An error occurred while tokenizing or parsing.</p>
</item>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
+ <item>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
+ if reading from an NFS file system.</p>
+ </item>
</taglist>
<p>Example:</p>
<pre>
@@ -891,28 +987,42 @@ 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>
+ <p>End of file was encountered by the tokenizer.</p>
+ </item>
+ <tag><c>eof</c></tag>
+ <item>
+ <p>End of file was encountered by the I/O-server.</p>
</item>
- <tag><c>{error, ErrorInfo, ErrorLine}</c></tag>
+ <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag>
<item>
- <p>An error occurred.</p>
+ <p>An error occurred while tokenizing or parsing.</p>
</item>
+ <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag>
+ <item>
+ <p>Other (rare) error condition, for instance <c>{error, estale}</c>
+ if reading from an NFS file system.</p>
+ </item>
</taglist>
</desc>
</func>
@@ -940,7 +1050,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 +1066,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..3dac259477 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>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -50,6 +50,12 @@
<datatype>
<name name="depth"/>
</datatype>
+ <datatype>
+ <name name="fread_error"/>
+ </datatype>
+ <datatype>
+ <name name="latin1_string"/>
+ </datatype>
</datatypes>
<funcs>
<func>
@@ -204,8 +210,25 @@
<name name="write_string" arity="1"/>
<fsummary>Write a string</fsummary>
<desc>
- <p>Returns the list of characters needed to print <c><anno>String</anno></c>
- as a string.</p>
+ <p>Returns the list of characters needed to print
+ <c><anno>String</anno></c> as a string.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="write_string_as_latin1" arity="1"/>
+ <fsummary>Write a string</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print
+ <c><anno>String</anno></c> as a string. Non-Latin-1
+ characters are escaped.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="write_latin1_string" arity="1"/>
+ <fsummary>Write an ISO-latin-1 string</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print
+ <c><anno>Latin1String</anno></c> as a string.</p>
</desc>
</func>
<func>
@@ -213,6 +236,23 @@
<fsummary>Write a 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_char_as_latin1" arity="1"/>
+ <fsummary>Write a 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="write_latin1_char" arity="1"/>
+ <fsummary>Write an ISO-latin-1 character</fsummary>
+ <desc>
+ <p>Returns the list of characters needed to print a character
constant in the ISO-latin-1 character set.</p>
</desc>
</func>
@@ -229,6 +269,14 @@
<fsummary>Test for a list of 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="latin1_char_list" arity="1"/>
+ <fsummary>Test for a list of ISO-latin-1 characters</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of
characters in the ISO-latin-1 range, otherwise it returns <c>false</c>.</p>
</desc>
</func>
@@ -237,17 +285,45 @@
<fsummary>Test for a deep list of 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="deep_latin1_char_list" arity="1"/>
+ <fsummary>Test for a deep list of characters</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, list
of characters in the ISO-latin-1 range, otherwise it returns <c>false</c>.</p>
</desc>
</func>
<func>
<name name="printable_list" arity="1"/>
+ <fsummary>Test for a list of printable characters</fsummary>
+ <desc>
+ <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of
+ printable characters, otherwise it returns <c>false</c>.</p>
+ <p>What is a printable character in this case is determined by the
+ <c>+pc</c> start up flag to the Erlang VM. See
+ <seealso marker="io#printable_range/0">io:printable_range/0</seealso>
+ and <seealso marker="erts:erl#erl">erl(1)</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="printable_latin1_list" arity="1"/>
<fsummary>Test for a list of printable ISO-latin-1 characters</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of
printable ISO-latin-1 characters, otherwise it returns <c>false</c>.</p>
</desc>
</func>
+ <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/lists.xml b/lib/stdlib/doc/src/lists.xml
index 8b31f3ac3d..b6c0fa4e05 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -248,18 +248,13 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keyfind(Key, N, TupleList) -> Tuple | false</name>
+ <name name="keyfind" arity="3"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Search for an element in a list of tuples</fsummary>
- <type>
- <v>Key = term()</v>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>TupleList = [Tuple]</v>
- <v>Tuple = tuple()</v>
- </type>
- <desc>
- <p>Searches the list of tuples <c>TupleList</c> for a
- tuple whose <c>N</c>th element compares equal to <c>Key</c>.
- Returns <c>Tuple</c> if such a tuple is found,
+ <desc>
+ <p>Searches the list of tuples <c><anno>TupleList</anno></c> for a
+ tuple whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>.
+ Returns <c><anno>Tuple</anno></c> if such a tuple is found,
otherwise <c>false</c>.</p>
</desc>
</func>
@@ -281,17 +276,12 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keymember(Key, N, TupleList) -> boolean()</name>
+ <name name="keymember" arity="3"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Test for membership of a list of tuples</fsummary>
- <type>
- <v>Key = term()</v>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>TupleList = [Tuple]</v>
- <v>&nbsp;Tuple = tuple()</v>
- </type>
- <desc>
- <p>Returns <c>true</c> if there is a tuple in <c>TupleList</c>
- whose <c>N</c>th element compares equal to <c>Key</c>, otherwise
+ <desc>
+ <p>Returns <c>true</c> if there is a tuple in <c><anno>TupleList</anno></c>
+ whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>, otherwise
<c>false</c>.</p>
</desc>
</func>
@@ -321,18 +311,13 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keysearch(Key, N, TupleList) -> {value, Tuple} | false</name>
+ <name name="keysearch" arity="3"/>
+ <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc>
<fsummary>Search for an element in a list of tuples</fsummary>
- <type>
- <v>Key = term()</v>
- <v>N = 1..tuple_size(Tuple)</v>
- <v>TupleList = [Tuple]</v>
- <v>Tuple = tuple()</v>
- </type>
- <desc>
- <p>Searches the list of tuples <c>TupleList</c> for a
- tuple whose <c>N</c>th element compares equal to <c>Key</c>.
- Returns <c>{value, Tuple}</c> if such a tuple is found,
+ <desc>
+ <p>Searches the list of tuples <c><anno>TupleList</anno></c> for a
+ tuple whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>.
+ Returns <c>{value, <anno>Tuple</anno>}</c> if such a tuple is found,
otherwise <c>false</c>.</p>
<note><p>This function is retained for backward compatibility.
The function <c>lists:keyfind/3</c> (introduced in R13A)
@@ -425,15 +410,11 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>member(Elem, List) -> boolean()</name>
+ <name name="member" arity="2"/>
<fsummary>Test for membership of a list</fsummary>
- <type>
- <v>Elem = term()</v>
- <v>List = [term()]</v>
- </type>
<desc>
- <p>Returns <c>true</c> if <c>Elem</c> matches some element of
- <c>List</c>, otherwise <c>false</c>.</p>
+ <p>Returns <c>true</c> if <c><anno>Elem</anno></c> matches some element of
+ <c><anno>List</anno></c>, otherwise <c>false</c>.</p>
</desc>
</func>
<func>
@@ -562,14 +543,11 @@ c</pre>
</desc>
</func>
<func>
- <name>reverse(List1, Tail) -> List2</name>
+ <name name="reverse" arity="2"/>
<fsummary>Reverse a list appending a tail</fsummary>
- <type>
- <v>List1 = Tail = List2 = [term()]</v>
- </type>
<desc>
- <p>Returns a list with the elements in <c>List1</c>
- in reverse order, with the tail <c>Tail</c> appended. For
+ <p>Returns a list with the elements in <c><anno>List1</anno></c>
+ in reverse order, with the tail <c><anno>Tail</anno></c> appended. For
example:</p>
<pre>
> <input>lists:reverse([1, 2, 3, 4], [a, b, c]).</input>
diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml
index 518457d5d8..0219dcce10 100644
--- a/lib/stdlib/doc/src/math.xml
+++ b/lib/stdlib/doc/src/math.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -52,54 +52,47 @@
</desc>
</func>
<func>
- <name>sin(X)</name>
- <name>cos(X)</name>
- <name>tan(X)</name>
- <name>asin(X)</name>
- <name>acos(X)</name>
- <name>atan(X)</name>
- <name>atan2(Y, X)</name>
- <name>sinh(X)</name>
- <name>cosh(X)</name>
- <name>tanh(X)</name>
- <name>asinh(X)</name>
- <name>acosh(X)</name>
- <name>atanh(X)</name>
- <name>exp(X)</name>
- <name>log(X)</name>
- <name>log10(X)</name>
- <name>pow(X, Y)</name>
- <name>sqrt(X)</name>
+ <name name="sin" arity="1"/>
+ <name name="cos" arity="1"/>
+ <name name="tan" arity="1"/>
+ <name name="asin" arity="1"/>
+ <name name="acos" arity="1"/>
+ <name name="atan" arity="1"/>
+ <name name="atan2" arity="2"/>
+ <name name="sinh" arity="1"/>
+ <name name="cosh" arity="1"/>
+ <name name="tanh" arity="1"/>
+ <name name="asinh" arity="1"/>
+ <name name="acosh" arity="1"/>
+ <name name="atanh" arity="1"/>
+ <name name="exp" arity="1"/>
+ <name name="log" arity="1"/>
+ <name name="log10" arity="1"/>
+ <name name="pow" arity="2"/>
+ <name name="sqrt" arity="1"/>
+ <type variable="X" name_i="7"/>
+ <type variable="Y" name_i="7"/>
<fsummary>Diverse math functions</fsummary>
- <type>
- <v>X = Y = number()</v>
- </type>
<desc>
<p>A collection of math functions which return floats. Arguments
are numbers. </p>
</desc>
</func>
<func>
- <name>erf(X) -> float()</name>
+ <name name="erf" arity="1"/>
<fsummary>Error function.</fsummary>
- <type>
- <v>X = number()</v>
- </type>
<desc>
- <p>Returns the error function of <c>X</c>, where</p>
+ <p>Returns the error function of <c><anno>X</anno></c>, where</p>
<pre>
erf(X) = 2/sqrt(pi)*integral from 0 to X of exp(-t*t) dt. </pre>
</desc>
</func>
<func>
- <name>erfc(X) -> float()</name>
+ <name name="erfc" arity="1"/>
<fsummary>Another error function</fsummary>
- <type>
- <v>X = number()</v>
- </type>
<desc>
<p><c>erfc(X)</c> returns <c>1.0 - erf(X)</c>, computed by
- methods that avoid cancellation for large <c>X</c>. </p>
+ methods that avoid cancellation for large <c><anno>X</anno></c>. </p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 2a308cbe09..2ec0d6a60f 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,281 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 1.19.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Bugs related to Unicode have been fixed in the
+ <c>erl_eval</c> module. </p>
+ <p>
+ Own Id: OTP-10622 Aux Id: kunagi-351 [262] </p>
+ </item>
+ <item>
+ <p><c>filelib:wildcard("some/relative/path/*.beam",
+ Path)</c> would fail to match any file. That is,
+ filelib:wildcard/2 would not work if the first component
+ of the pattern did not contain any wildcard characters.
+ (A previous attempt to fix the problem in R15B02 seems to
+ have made matters worse.)</p>
+ <p>(Thanks to Samuel Rivas and Tuncer Ayaz.)</p>
+ <p>There is also an incompatible change to the
+ <c>Path</c> argument. It is no longer allowed to be a
+ binary.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10812</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The new STDLIB application variable
+ <c>shell_strings</c> can be used for determining how the
+ Erlang shell outputs lists of integers. The new function
+ <c>shell:strings/1</c> toggles the value of the variable.
+ </p> <p> The control sequence modifier <c>l</c> can be
+ used for turning off the string recognition of <c>~p</c>
+ and <c>~P</c>. </p>
+ <p>
+ Own Id: OTP-10755</p>
+ </item>
+ <item>
+ <p> Miscellaneous updates due to Unicode support. </p>
+ <p>
+ Own Id: OTP-10820</p>
+ </item>
+ <item>
+ <p> Extend <c>~ts</c> to handle binaries with characters
+ coded in ISO-latin-1 </p>
+ <p>
+ Own Id: OTP-10836</p>
+ </item>
+ <item>
+ <p>
+ The +pc flag to erl can be used to set the range of
+ characters considered printable. This affects how the
+ shell and io:format("~tp",...) functionality does
+ heuristic string detection. More can be read in STDLIB
+ users guide.</p>
+ <p>
+ Own Id: OTP-10884</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.19</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Wildcards such as "some/path/*" passed to
+ <c>filelib:wildcard/2</c> would fail to match any file.
+ (Thanks to Samuel Rivas for reporting this bug.)</p>
+ <p>
+ Own Id: OTP-6874 Aux Id: kunagi-190 [101] </p>
+ </item>
+ <item>
+ <p>
+ Fixed error handling in proc_lib:start which could hang
+ if the spawned process died in init.</p>
+ <p>
+ Own Id: OTP-9803 Aux Id: kunagi-209 [120] </p>
+ </item>
+ <item>
+ <p>
+ Allow ** in filelib:wildcard</p>
+ <p>
+ Two adjacent * used as a single pattern will match all
+ files and zero or more directories and subdirectories.
+ (Thanks to Jos� Valim)</p>
+ <p>
+ Own Id: OTP-10431</p>
+ </item>
+ <item>
+ <p>
+ Add the \gN and \g{N} syntax for back references in
+ re:replace/3,4 to allow use with numeric replacement
+ strings. (Thanks to Vance Shipley)</p>
+ <p>
+ Own Id: OTP-10455</p>
+ </item>
+ <item>
+ <p>
+ Export ets:match_pattern/0 type (Thanks to Joseph Wayne
+ Norton)</p>
+ <p>
+ Own Id: OTP-10472</p>
+ </item>
+ <item>
+ <p>
+ Fix printing the empty binary at depth 1 with ~W (Thanks
+ to Andrew Thompson)</p>
+ <p>
+ Own Id: OTP-10504</p>
+ </item>
+ <item>
+ <p> The type <c>ascii_string()</c> in the <c>base64</c>
+ module has been corrected. The type
+ <c>file:file_info()</c> has been cleaned up. The type
+ <c>file:fd()</c> has been made opaque in the
+ documentation. </p>
+ <p>
+ Own Id: OTP-10624 Aux Id: kunagi-352 [263] </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Dets tables are no longer fixed while traversing with
+ a bound key (when only the objects with the right key are
+ matched). This optimization affects the functions
+ <c>match/2</c>, <c>match_object/2</c>, <c>select/2</c>,
+ <c>match_delete/2</c>, and <c>select_delete/2</c>. </p>
+ <p>
+ Own Id: OTP-10097</p>
+ </item>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p> The linter now warns for opaque types that are not
+ exported, as well as for under-specified opaque types.
+ </p>
+ <p>
+ Own Id: OTP-10436</p>
+ </item>
+ <item>
+ <p> The type <c>file:name()</c> has been substituted for
+ the type <c>file:filename()</c> in the following
+ functions in the <c>filename</c> module:
+ <c>absname/2</c>, <c>absname_join/2</c>, <c>join/1,2</c>,
+ and <c>split/1</c>. </p>
+ <p>
+ Own Id: OTP-10474</p>
+ </item>
+ <item>
+ <p>
+ If a child process fails in its start function, then the
+ error reason was earlier only reported as an error report
+ from the error_handler, and supervisor:start_link would
+ only return <c>{error,shutdown}</c>. This has been
+ changed so the supervisor will now return
+ <c>{error,{shutdown,Reason}}</c>, where <c>Reason</c>
+ identifies the failing child and its error reason.
+ (Thanks to Tomas Pihl)</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10490</p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p> The contracts and types of the modules
+ <c>erl_scan</c> and <c>sys</c> have been corrected and
+ improved. (Thanks to Kostis Sagonas.) </p>
+ <p>
+ Own Id: OTP-10658</p>
+ </item>
+ <item>
+ <p> The Erlang shell now skips the rest of the line when
+ it encounters an Erlang scanner error. </p>
+ <p>
+ Own Id: OTP-10659</p>
+ </item>
+ <item>
+ <p>
+ Clean up some specs in the proplists module. (Thanks to
+ Kostis Sagonas.)</p>
+ <p>
+ Own Id: OTP-10663</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ <item>
+ <p>
+ Enable escript to accept emulator arguments when script
+ file has no shebang. Thanks to Magnus Henoch</p>
+ <p>
+ Own Id: OTP-10691</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in queue:out/1, queue:out_r/1 that makes it
+ O(N^2) in worst case. Thanks to Aleksandr Erofeev.</p>
+ <p>
+ Own Id: OTP-10722</p>
+ </item>
+ <item>
+ <p> There are new functions in the <c>epp</c> module
+ which read the character encoding from files. See
+ <c>epp(3)</c> for more information. </p>
+ <p>
+ Own Id: OTP-10742 Aux Id: OTP-10302 </p>
+ </item>
+ <item>
+ <p> The functions in <c>io_lib</c> have been adjusted for
+ Unicode. The existing functions <c>write_string()</c> and
+ so on now take Unicode strings, while the old behavior
+ has been taken over by new functions
+ <c>write_latin1_string()</c> and so on. There are also
+ new functions to write Unicode strings as Latin-1
+ strings, mainly targetted towards the Erlang pretty
+ printer (<c>erl_pp</c>). </p>
+ <p>
+ Own Id: OTP-10745 Aux Id: OTP-10302 </p>
+ </item>
+ <item>
+ <p> The new functions <c>proc_lib:format/2</c> and
+ <c>erl_parse:abstract/2</c> accept an encoding as second
+ argument. </p>
+ <p>
+ Own Id: OTP-10749 Aux Id: OTP-10302 </p>
+ </item>
+ <item>
+ <p>
+ Increased potential concurrency in ETS for
+ <c>write_concurrency</c> option. The number of internal
+ table locks has increased from 16 to 64. This makes it
+ four times less likely that two concurrent processes
+ writing to the same table would collide and thereby
+ serialized. The cost is an increased constant memory
+ footprint for tables using write_concurrency. The memory
+ consumption per inserted record is not affected. The
+ increased footprint can be particularly large if
+ <c>write_concurrency</c> is combined with
+ <c>read_concurrency</c>.</p>
+ <p>
+ Own Id: OTP-10787</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 1.18.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index abc17c4a91..b597074044 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -209,6 +209,13 @@ init(Parent) ->
<name name="format" arity="1"/>
<fsummary>Format a crash report.</fsummary>
<desc>
+ <p>Equivalent to <c>format(<anno>CrashReport</anno>, latin1)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="format" arity="2"/>
+ <fsummary>Format a crash report.</fsummary>
+ <desc>
<p>This function can be used by a user defined event handler to
format a crash report. The crash report is sent using
<c>error_logger:error_report(crash_report, <anno>CrashReport</anno>)</c>.
diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml
index 225c5e97eb..a0063a58d3 100644
--- a/lib/stdlib/doc/src/proplists.xml
+++ b/lib/stdlib/doc/src/proplists.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2002</year><year>2011</year>
+ <year>2002</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 6d5336796c..71a6e34513 100644
--- a/lib/stdlib/doc/src/re.xml
+++ b/lib/stdlib/doc/src/re.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2011</year>
+ <year>2012</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -78,28 +78,15 @@
</datatypes>
<funcs>
<func>
- <name>compile(Regexp) -> {ok, MP} | {error, ErrSpec}</name>
+ <name name="compile" arity="1"/>
<fsummary>Compile a regular expression into a match program</fsummary>
- <type>
- <v>Regexp = iodata()</v>
- </type>
<desc>
- <p>The same as <c>compile(Regexp,[])</c></p>
+ <p>The same as <c>compile(<anno>Regexp</anno>,[])</c></p>
</desc>
</func>
<func>
- <name>compile(Regexp,Options) -> {ok, MP} | {error, ErrSpec}</name>
+ <name name="compile" arity="2"/>
<fsummary>Compile a regular expression into a match program</fsummary>
- <type>
- <v>Regexp = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>Options = [ Option ]</v>
- <v>Option = <seealso marker="#type-compile_option">compile_option()</seealso></v>
- <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v>
- <v>MP = <seealso marker="#type-mp">mp()</seealso></v>
- <v>ErrSpec = {ErrString, Position}</v>
- <v>ErrString = string()</v>
- <v>Position = non_neg_integer()</v>
- </type>
<desc>
<p>This function compiles a regular expression with the syntax
described below into an internal format to be used later as a
@@ -109,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>
@@ -165,44 +152,23 @@ This option makes it possible to include comments inside complicated patterns. N
</func>
<func>
- <name>run(Subject,RE) -> {match, Captured} | nomatch</name>
+ <name name="run" arity="2"/>
<fsummary>Match a subject against regular expression and capture subpatterns</fsummary>
- <type>
- <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata()</v>
- <v>Captured = [ CaptureData ]</v>
- <v>CaptureData = {integer(),integer()}</v>
- </type>
<desc>
- <p>The same as <c>run(Subject,RE,[])</c>.</p>
+ <p>The same as <c>run(<anno>Subject</anno>,<anno>RE</anno>,[])</c>.</p>
</desc>
</func>
<func>
- <name>run(Subject,RE,Options) -> {match, Captured} | match | nomatch</name>
+ <name name="run" arity="3"/>
+ <type_desc variable="CompileOpt">See <seealso marker="#compile_options">compile/2</seealso> above.</type_desc>
<fsummary>Match a subject against regular expression and capture subpatterns</fsummary>
- <type>
- <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v>
- <v>Options = [ Option ]</v>
- <v>Option = anchored | global | notbol | noteol | notempty | {offset, integer() >= 0} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt</v>
- <v>Type = index | list | binary</v>
- <v>ValueSpec = all | all_but_first | first | none | ValueList</v>
- <v>ValueList = [ ValueID ]</v>
- <v>ValueID = integer() | string() | atom()</v>
- <v>CompileOpt = <seealso marker="#type-compile_option">compile_option()</seealso></v>
- <d>See <seealso marker="#compile_options">compile/2</seealso> above.</d>
- <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v>
- <v>Captured = [ CaptureData ] | [ [ CaptureData ] ... ]</v>
- <v>CaptureData = {integer(),integer()} | ListConversionData | binary()</v>
- <v>ListConversionData = string() | {error, string(), binary()} | {incomplete, string(), binary()}</v>
- </type>
<desc>
<p>Executes a regexp matching, returning <c>match/{match,
- Captured}</c> or <c>nomatch</c>. The regular expression can be
+ <anno>Captured</anno>}</c> or <c>nomatch</c>. The regular expression can be
given either as <c>iodata()</c> in which case it is
automatically compiled (as by <c>re:compile/2</c>) and executed,
- or as a pre compiled <c>mp()</c> in which case it is executed
+ or as a pre-compiled <c>mp()</c> in which case it is executed
against the subject directly.</p>
<p>When compilation is involved, the exception <c>badarg</c> is
@@ -214,23 +180,23 @@ This option makes it possible to include comments inside complicated patterns. N
list can only contain the options <c>anchored</c>,
<c>global</c>, <c>notbol</c>, <c>noteol</c>,
<c>notempty</c>, <c>{offset, integer() >= 0}</c>, <c>{newline,
- NLSpec}</c> and <c>{capture, ValueSpec}/{capture, ValueSpec,
- Type}</c>. Otherwise all options valid for the
+ <anno>NLSpec</anno>}</c> and <c>{capture, <anno>ValueSpec</anno>}/{capture, <anno>ValueSpec</anno>,
+ <anno>Type</anno>}</c>. Otherwise all options valid for the
<c>re:compile/2</c> function are allowed as well. Options
allowed both for compilation and execution of a match, namely
- <c>anchored</c> and <c>{newline, NLSpec}</c>, will affect both
+ <c>anchored</c> and <c>{newline, <anno>NLSpec</anno>}</c>, will affect both
the compilation and execution if present together with a non
pre-compiled regular expression.</p>
<p>If the regular expression was previously compiled with the
- option <c>unicode</c>, the <c>Subject</c> should be provided as
+ option <c>unicode</c>, the <c><anno>Subject</anno></c> should be provided as
a valid Unicode <c>charlist()</c>, otherwise any <c>iodata()</c>
will do. If compilation is involved and the option
- <c>unicode</c> is given, both the <c>Subject</c> and the regular
+ <c>unicode</c> is given, both the <c><anno>Subject</anno></c> and the regular
expression should be given as valid Unicode
<c>charlists()</c>.</p>
- <p>The <c>{capture, ValueSpec}/{capture, ValueSpec, Type}</c>
+ <p>The <c>{capture, <anno>ValueSpec</anno>}/{capture, <anno>ValueSpec</anno>, <anno>Type</anno>}</c>
defines what to return from the function upon successful
matching. The <c>capture</c> tuple may contain both a
value specification telling which of the captured
@@ -244,9 +210,9 @@ This option makes it possible to include comments inside complicated patterns. N
at all is to be done (<c>{capture, none}</c>), the function will
return the single atom <c>match</c> upon successful matching,
otherwise the tuple
- <c>{match, ValueList}</c> is returned. Disabling capturing can
+ <c>{match, <anno>ValueList</anno>}</c> is returned. Disabling capturing can
be done either by specifying <c>none</c> or an empty list as
- <c>ValueSpec</c>.</p>
+ <c><anno>ValueSpec</anno></c>.</p>
<p>The options relevant for execution are:</p>
@@ -266,7 +232,7 @@ This option makes it possible to include comments inside complicated patterns. N
Perl). Each match is returned as a separate
<c>list()</c> containing the specific match as well as any
matching subexpressions (or as specified by the <c>capture
- option</c>). The <c>Captured</c> part of the return value will
+ option</c>). The <c><anno>Captured</anno></c> part of the return value will
hence be a <c>list()</c> of <c>list()</c>s when this
option is given.</p>
@@ -362,7 +328,7 @@ This option makes it possible to include comments inside complicated patterns. N
subject string. The offset is zero-based, so that the default is
<c>{offset,0}</c> (all of the subject string).</item>
- <tag><c>{newline, NLSpec}</c></tag>
+ <tag><c>{newline, <anno>NLSpec</anno>}</c></tag>
<item>
<p>Override the default definition of a newline in the subject string, which is LF (ASCII 10) in Erlang.</p>
<taglist>
@@ -383,7 +349,7 @@ This option makes it possible to include comments inside complicated patterns. N
<tag><c>bsr_unicode</c></tag>
<item>Specifies specifically that \R is to match all the Unicode newline characters (including crlf etc, the default).(overrides compilation option)</item>
- <tag><c>{capture, ValueSpec}</c>/<c>{capture, ValueSpec, Type}</c></tag>
+ <tag><c>{capture, <anno>ValueSpec</anno>}</c>/<c>{capture, <anno>ValueSpec</anno>, <anno>Type</anno>}</c></tag>
<item>
<p>Specifies which captured substrings are returned and in what
@@ -392,7 +358,7 @@ This option makes it possible to include comments inside complicated patterns. N
substring as well as all capturing subpatterns (all of the
pattern is automatically captured). The default return type is
(zero-based) indexes of the captured parts of the string, given as
- <c>{Offset,Length}</c> pairs (the <c>index</c> <c>Type</c> of
+ <c>{Offset,Length}</c> pairs (the <c>index</c> <c><anno>Type</anno></c> of
capturing).</p>
<p>As an example of the default behavior, the following call:</p>
@@ -422,8 +388,8 @@ This option makes it possible to include comments inside complicated patterns. N
<p>The capture tuple is built up as follows:</p>
<taglist>
- <tag><c>ValueSpec</c></tag>
- <item><p>Specifies which captured (sub)patterns are to be returned. The ValueSpec can either be an atom describing a predefined set of return values, or a list containing either the indexes or the names of specific subpatterns to return.</p>
+ <tag><c><anno>ValueSpec</anno></c></tag>
+ <item><p>Specifies which captured (sub)patterns are to be returned. The <c><anno>ValueSpec</anno></c> can either be an atom describing a predefined set of return values, or a list containing either the indexes or the names of specific subpatterns to return.</p>
<p>The predefined sets of subpatterns are:</p>
<taglist>
<tag><c>all</c></tag>
@@ -437,7 +403,7 @@ This option makes it possible to include comments inside complicated patterns. N
</taglist>
<p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>s or <c>string()</c>s to specify the subpatterns to be returned. For example, consider the regular expression:</p>
<code> ".*(abcd).*"</code>
- <p>matched against the string ""ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p>
+ <p>matched against the string "ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p>
<code> re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).</code>
<p>The call will yield the following result:</p>
<code> {match,[{3,4}]}</code>
@@ -460,8 +426,8 @@ This option makes it possible to include comments inside complicated patterns. N
or list respectively.</p>
</item>
- <tag><c>Type</c></tag>
- <item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c>Type</c> can be one of the following:</p>
+ <tag><c><anno>Type</anno></c></tag>
+ <item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c><anno>Type</anno></c> can be one of the following:</p>
<taglist>
<tag><c>index</c></tag>
<item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly virtual) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem counter-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item>
@@ -478,7 +444,7 @@ This option makes it possible to include comments inside complicated patterns. N
<code> "ABCabcdABC"</code>
<p>the subpattern at index 2 won't match, as "abdd" is not present in the string, but the complete pattern matches (due to the alternative <c>a(..d)</c>. The subpattern at index 2 is therefore unassigned and the default return value will be:</p>
<code> {match,[{0,10},{3,4},{-1,0},{4,3}]}</code>
- <p>Setting the capture <c>Type</c> to <c>binary</c> would give the following:</p>
+ <p>Setting the capture <c><anno>Type</anno></c> to <c>binary</c> would give the following:</p>
<code> {match,[&lt;&lt;"ABCabcdABC"&gt;&gt;,&lt;&lt;"abcd"&gt;&gt;,&lt;&lt;&gt;&gt;,&lt;&lt;"bcd"&gt;&gt;]}</code>
<p>where the empty binary (<c>&lt;&lt;&gt;&gt;</c>) represents the unassigned subpattern. In the <c>binary</c> case, some information about the matching is therefore lost, the <c>&lt;&lt;&gt;&gt;</c> might just as well be an empty string captured.</p>
<p>If differentiation between empty matches and non existing subpatterns is necessary, use the <c>type</c> <c>index</c>
@@ -512,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
@@ -524,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/shell.xml b/lib/stdlib/doc/src/shell.xml
index bc2120c37d..7f251c863e 100644
--- a/lib/stdlib/doc/src/shell.xml
+++ b/lib/stdlib/doc/src/shell.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -781,7 +781,7 @@ loop(N) ->
</desc>
</func>
<func>
- <name>catch_exception(Bool) -> Bool</name>
+ <name>catch_exception(Bool) -> boolean()</name>
<fsummary>Sets the exception handling of the shell</fsummary>
<type>
<v>Bool = boolean()</v>
@@ -801,8 +801,8 @@ loop(N) ->
<name name="prompt_func" arity="1"/>
<fsummary>Sets the shell prompt</fsummary>
<desc>
- <p>Sets the shell prompt function to <c>PromptFunc</c>. The
- previous prompt function is returned.</p>
+ <p>Sets the shell prompt function to <c><anno>PromptFunc</anno></c>.
+ The previous prompt function is returned.</p>
</desc>
</func>
<func>
@@ -827,6 +827,20 @@ loop(N) ->
is meant to be called from the shell.</p>
</desc>
</func>
+ <func>
+ <name name="strings" arity="1"/>
+ <fsummary>Sets the shell's string recognition flag.</fsummary>
+ <desc>
+ <p>Sets pretty printing of lists to <c><anno>Strings</anno></c>.
+ The previous value of the flag is returned.</p>
+ <p>The flag can also be set by the STDLIB application variable
+ <c>shell_strings</c>. The default is
+ <c>true</c> which means that lists of integers will be
+ printed using the string syntax, when possible. The value
+ <c>false</c> means that no lists will be printed using the
+ string syntax.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
diff --git a/lib/stdlib/doc/src/stdlib_app.xml b/lib/stdlib/doc/src/stdlib_app.xml
index a615c1bf88..2391bb6f03 100644
--- a/lib/stdlib/doc/src/stdlib_app.xml
+++ b/lib/stdlib/doc/src/stdlib_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>2005</year><year>2010</year>
+ <year>2005</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -51,7 +51,7 @@
<p>This parameter can be used to run the Erlang shell
in restricted mode.</p>
</item>
- <tag><c>shell_catch_exception = bool()</c></tag>
+ <tag><c>shell_catch_exception = boolean()</c></tag>
<item>
<p>This parameter can be used to set the exception handling
of the Erlang shell's evaluator process.</p>
@@ -76,6 +76,11 @@
<p>This parameter can be used to determine how many
results are saved by the Erlang shell.</p>
</item>
+ <tag><c>shell_strings = boolean()</c></tag>
+ <item>
+ <p>This parameter can be used to determine how the Erlang
+ shell outputs lists of integers.</p>
+ </item>
</taglist>
</section>
diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml
index 48867ffe72..549c871aed 100644
--- a/lib/stdlib/doc/src/string.xml
+++ b/lib/stdlib/doc/src/string.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2011</year>
+ <year>1996</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -255,18 +255,12 @@ sub_string("Hello World", 4, 8).
</desc>
</func>
<func>
- <name>to_float(String) -> {Float,Rest} | {error,Reason} </name>
+ <name name="to_float" arity="1"/>
<fsummary>Returns a float whose text representation is the integers (ASCII values) in String.</fsummary>
- <type>
- <v>String = string()</v>
- <v>Float = float()</v>
- <v>Rest = string()</v>
- <v>Reason = no_float | not_a_list</v>
- </type>
<desc>
- <p>Argument <c>String</c> is expected to start with a valid text
+ <p>Argument <c><anno>String</anno></c> is expected to start with a valid text
represented float (the digits being ASCII values). Remaining characters
- in the string after the float are returned in <c>Rest</c>.</p>
+ in the string after the float are returned in <c><anno>Rest</anno></c>.</p>
<p>Example:</p>
<code type="none">
> {F1,Fs} = string:to_float("1.0-1.0e-1"),
@@ -280,18 +274,12 @@ sub_string("Hello World", 4, 8).
</desc>
</func>
<func>
- <name>to_integer(String) -> {Int,Rest} | {error,Reason} </name>
+ <name name="to_integer" arity="1"/>
<fsummary>Returns an integer whose text representation is the integers (ASCII values) in String.</fsummary>
- <type>
- <v>String = string()</v>
- <v>Int = integer()</v>
- <v>Rest = string()</v>
- <v>Reason = no_integer | not_a_list</v>
- </type>
<desc>
- <p>Argument <c>String</c> is expected to start with a valid text
+ <p>Argument <c><anno>String</anno></c> is expected to start with a valid text
represented integer (the digits being ASCII values). Remaining characters
- in the string after the integer are returned in <c>Rest</c>.</p>
+ in the string after the integer are returned in <c><anno>Rest</anno></c>.</p>
<p>Example:</p>
<code type="none">
> {I1,Is} = string:to_integer("33+22"),
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 1001ebbae4..deba6adb11 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -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,7 @@
<datatype>
<name name="unicode_binary"/>
<desc>
- <p>A binary() with characters encoded in the UTF-8 coding standard.</p>
- </desc>
- </datatype>
- <datatype>
- <name name="unicode_char"/>
- <desc>
- <p>An integer() representing a valid unicode codepoint.</p>
+ <p>A <c>binary()</c> with characters encoded in the UTF-8 coding standard.</p>
</desc>
</datatype>
<datatype>
@@ -62,9 +56,6 @@
</datatype>
<datatype>
<name name="charlist"/>
- <desc>
- <p>A unicode_binary is allowed as the tail of the list.</p>
- </desc>
</datatype>
<datatype>
<name name="external_unicode_binary"/>
@@ -78,14 +69,10 @@
</datatype>
<datatype>
<name name="external_charlist"/>
- <desc>
- <p>An <c>external_unicode_binary()</c> is allowed as the tail
- of the list.</p>
- </desc>
</datatype>
<datatype>
<name name="latin1_binary"/>
- <desc><p>A <c>binary()</c> with characters coded in iso-latin-1.</p>
+ <desc><p>A <c>binary()</c> with characters coded in ISO-latin-1.</p>
</desc>
</datatype>
<datatype>
@@ -96,11 +83,12 @@
</datatype>
<datatype>
<name name="latin1_chardata"/>
+ <desc><p>The same as <c>iodata()</c>.</p>
+ </desc>
</datatype>
<datatype>
<name name="latin1_charlist"/>
- <desc><p>A <c>latin1_binary()</c> is allowed as the tail of
- the list.</p>
+ <desc><p>The same as <c>iolist()</c>.</p>
</desc>
</datatype>
</datatypes>
@@ -110,7 +98,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,50 +116,40 @@
<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>
- <name>characters_to_list(Data, InEncoding) -> Result</name>
+ <name name="characters_to_list" arity="2"/>
<fsummary>Convert a collection of characters to list of Unicode characters</fsummary>
- <type>
- <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>Result = list() | {error, list(), RestData} | {incomplete, list(), binary()}</v>
- <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v>
- </type>
<desc>
- <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>InEncoding</c> parameter should be given as
+ case the <c><anno>InEncoding</anno></c> parameter should be given as
<c>latin1</c>, or have characters encoded as one of the
- UTF-encodings, which is given as the <c>InEncoding</c>
- parameter. Only when the <c>InEncoding</c> is one of the UTF
+ UTF-encodings, which is given as the <c><anno>InEncoding</anno></c>
+ parameter. Only when the <c><anno>InEncoding</anno></c> is one of the UTF
encodings, integers in the list are allowed to be grater than
255.</p>
- <p>If <c>InEncoding</c> is <c>latin1</c>, the <c>Data</c> parameter
+ <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>Data</c> parameter can contain integers greater than 255
- (unicode characters beyond the iso-latin-1 range), which would
+ the <c><anno>Data</anno></c> parameter can contain integers greater than 255
+ (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>
@@ -177,7 +157,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
@@ -186,43 +166,43 @@
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>Data</c> is a pure binary, the third
+ <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
well.</p>
<p>Errors occur for the following reasons:</p>
<list type="bulleted">
- <item>Integers out of range - If <c>InEncoding</c> is
+ <item>Integers out of range - If <c><anno>InEncoding</anno></c> is
<c>latin1</c>, an error occurs whenever an integer greater
- than 255 is found in the lists. If <c>InEncoding</c> is
+ than 255 is found in the lists. If <c><anno>InEncoding</anno></c> is
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>
is found.
</item>
- <item>UTF encoding incorrect - If <c>InEncoding</c> is
+ <item>UTF encoding incorrect - If <c><anno>InEncoding</anno></c> is
one of the UTF types, the bytes in any binaries have to be valid
in that encoding. Errors can occur for various
reasons, including &quot;pure&quot; decoding errors
(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>InEncoding</c> is <c>latin1</c>, binaries are always valid
+ <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>
@@ -238,7 +218,7 @@
the first part of a (so far) valid UTF character.</p>
<p>If one UTF characters is split over two consecutive
- binaries in the <c>Data</c>, the conversion succeeds. This means
+ binaries in the <c><anno>Data</anno></c>, the conversion succeeds. This means
that a character can be decoded from a range of binaries as long
as the whole range is given as input without errors
occurring. Example:</p>
@@ -260,7 +240,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>
@@ -268,38 +248,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>characters_to_binary(Data,InEncoding) -> Result</name>
- <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary>
+ <name name="characters_to_binary" arity="2"/>
+ <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary>
- <type>
- <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>Result = binary() | {error, binary(), RestData} | {incomplete, binary(), binary()}</v>
- <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso>
- | <seealso marker="#type-chardata">chardata()</seealso>
- | <seealso marker="#type-external_chardata">external_chardata()</seealso></v>
- <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v>
- </type>
<desc>
- <p>Same as characters_to_binary(Data, InEncoding, unicode).</p>
+ <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
@@ -314,7 +283,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>
@@ -324,16 +293,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 bbcd49a934..c5d476e54b 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>2012</year>
+ <year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,226 +32,1084 @@
<rev>PA1</rev>
<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>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>
-<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>The most widely spread encodings are:</p>
-<taglist>
-<tag>UTF-8</tag>
-<item>Each character is stored in one to four bytes depending on codepoint. The encoding is backwards compatible with 7-bit ASCII as all 7-bit characters are stored in one single byte as is. The characters beyond codepoint 127 are stored in more bytes, letting the most significant bit in the first character indicate a multi-byte character. For details on the encoding, the RFC is publicly available.</item>
-<tag>UTF-16</tag>
-<item>This encoding has many similarities to UTF-8, but the basic unit is a 16-bit number. This means that all characters occupy at least two bytes, some high numbers even four bytes. Some programs and operating systems claiming to use UTF-16 only allows for characters that can be stored in one 16-bit entity, which is usually sufficient to handle living languages. As the basic unit is more than one byte, byte-order issues occur, why UTF-16 exists in both a big-endian and little-endian variant.</item>
-<tag>UTF-32</tag>
-<item>The most straight forward representation, each character is stored in one single 32-bit number. There is no need for escapes or any variable amount of entities for one character, all Unicode codepoints can be stored in one single 32-bit entity. As with UTF-16, there are byte-order issues, UTF-32 can be both big- and little-endian.</item>
-<tag>UCS-4</tag>
-<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, 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>
+<title>Unicode Implementation</title>
+ <p>Implementing support for Unicode character sets is an ongoing
+ process. The Erlang Enhancement Proposal (EEP) 10 outlined the
+ basics of Unicode support and also specified a default encoding in
+ binaries that all Unicode-aware modules should handle in the
+ future.</p>
+
+ <p>The functionality described in EEP10 was implemented in Erlang/OTP
+ as of R13A, but that was by no means the end of it. In R14B01 support
+ for Unicode file names was added, although it was in no way complete
+ and was by default disabled on platforms where no guarantee was given
+ for the file name encoding. With R16A came support for UTF-8 encoded
+ source code, among with enhancements to many of the applications to
+ support both Unicode encoded file names as well as support for UTF-8
+ encoded files in several circumstances. Most notable is the support
+ for UTF-8 in files read by <c>file:consult/1</c>, release handler support
+ for UTF-8 and more support for Unicode character sets in the
+ I/O-system.</p>
+
+ <p>In R17, the encoding default for Erlang source files will be
+ switched to UTF-8 and in R18 Erlang will support atoms in the full
+ Unicode range, meaning full Unicode function and module
+ names</p>
+
+ <p>This guide outlines the current Unicode support and gives a couple
+ of recipes for working with Unicode data.</p>
+</section>
+<section>
+<title>Understanding Unicode</title>
+ <p>Experience with the Unicode support in Erlang has made it
+ painfully clear that understanding Unicode characters and encodings
+ is not as easy as one would expect. The complexity of the field as
+ well as the implications of the standard requires thorough
+ understanding of concepts rarely before thought of.</p>
+
+ <p>Furthermore the Erlang implementation requires understanding of
+ concepts that never were an issue for many (Erlang) programmers. To
+ understand and use Unicode characters requires that you study the
+ subject thoroughly, even if you're an experienced programmer.</p>
+
+ <p>As an example, one could contemplate the issue of converting
+ between upper and lower case letters. Reading the standard will make
+ you realize that, to begin with, there's not a simple one to one
+ mapping in all scripts. Take German as an example, where there's a
+ letter "ß" (Sharp s) in lower case, but the uppercase equivalent is
+ "SS". Or Greek, where "Σ" has two different lowercase forms: "ς" in
+ word-final position and "σ" elsewhere. Or Turkish where dotted and
+ dot-less "i" both exist in lower case and upper case forms, or
+ Cyrillic "I" which usually has no lowercase form. Or of course
+ languages that have no concept of upper case (or lower case). So, a
+ conversion function will need to know not only one character at a
+ time, but possibly the whole sentence, maybe the natural language
+ the translation should be in and also take into account differences
+ in input and output string length and so on. There is at the time of
+ writing no Unicode to_upper/to_lower functionality in Erlang/OTP, but
+ there are publicly available libraries that address these issues.</p>
+
+ <p>Another example is the accented characters where the same glyph
+ has two different representations. Let's look at the Swedish
+ "ö". There's a code point for that in the Unicode standard, but you
+ can also write it as "o" followed by U+0308 (Combining Diaeresis,
+ with the simplified meaning that the last letter should have a "¨"
+ above). They have exactly the same glyph. They are for most
+ purposes the same, but they have completely different
+ representations. For example MacOS X converts all file names to use
+ Combining Diaeresis, while most other programs (including Erlang)
+ try to hide that by doing the opposite when for example listing
+ directories. However it's done, it's usually important to normalize
+ such characters to avoid utter confusion.</p>
+
+ <p>The list of examples can be made as long as the Unicode standard, I
+ suspect. The point is that one need a kind of knowledge that was
+ never needed when programs only took one or two languages into
+ account. The complexity of human languages and scripts, certainly
+ has made this a challenge when constructing a universal
+ standard. Supporting Unicode properly in your program <em>will</em> require
+ effort.</p>
+
</section>
<section>
-<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>
+<title>What Unicode Is</title>
+ <p>Unicode is a standard defining code points (numbers) for all
+ known, living or dead, scripts. In principle, every known symbol
+ used in any language has a Unicode code point.</p>
+ <p>Unicode code points 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 code points for all the
+ scripts, there are a couple of <em>encoding standards</em> available.</p>
+ <p>It is vital to understand the difference between encodings and
+ Unicode characters. Unicode characters are code points according to
+ the Unicode standard, while the encodings are ways to represent such
+ code points. An encoding is just a standard for representation,
+ UTF-8 can for example be used to represent a very limited part of
+ the Unicode character set (e.g. ISO-Latin-1), or the full Unicode
+ range. It's just an encoding format.</p>
+ <p>As long as all character sets were limited to 256 characters,
+ each character could be stored in one single byte, so there was more
+ or less only one practical encoding for the characters. Encoding
+ each character in one byte was so common that the encoding wasn't
+ even named. When we now, with the Unicode system, have a lot more
+ than 256 characters, we need a common way to represent these. The
+ common ways of representing the code points are the encodings. This
+ means a whole new concept to the programmer, the concept of
+ character representation, which was before a non-issue.</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>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>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 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>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>
-<code type="none">
+ <p>The most widely spread encodings are:</p>
+ <taglist>
+ <tag>Bytewise representation</tag>
+ <item>This is not a proper Unicode representation, but the
+ representation used for characters before the Unicode standard. It
+ can still be used to represent character code points in the Unicode
+ standard that have numbers below 256, which corresponds exactly to
+ the ISO-Latin-1 character set. In Erlang, this is commonly denoted
+ <c>latin1</c> encoding, which is slightly misleading as ISO-Latin-1 is
+ a character code range, not an encoding.</item>
+ <tag>UTF-8</tag>
+ <item>Each character is stored in one to four bytes depending on
+ code point. The encoding is backwards compatible with bytewise
+ representation of 7-bit ASCII as all 7-bit characters are stored
+ in one single byte in UTF-8. The characters beyond code point 127
+ are stored in more bytes, letting the most significant bit in the
+ first character indicate a multi-byte character. For details on
+ the encoding, the RFC is publicly available. Note that UTF-8 is
+ <em>not</em> compatible with bytewise representation for
+ code points between 128 and 255, so a ISO-Latin-1 bytewise
+ representation is not generally compatible with UTF-8.</item>
+ <tag>UTF-16</tag>
+ <item>This encoding has many similarities to UTF-8, but the basic
+ unit is a 16-bit number. This means that all characters occupy at
+ least two bytes, some high numbers even four bytes. Some programs,
+ libraries and operating systems claiming to use UTF-16 only allows
+ for characters that can be stored in one 16-bit entity, which is
+ usually sufficient to handle living languages. As the basic unit
+ is more than one byte, byte-order issues occur, why UTF-16 exists
+ in both a big-endian and little-endian variant. In Erlang, the
+ full UTF-16 range is supported when applicable, like in the
+ <c>unicode</c> module and in the bit syntax.</item>
+ <tag>UTF-32</tag>
+ <item>The most straight forward representation. Each character is
+ stored in one single 32-bit number. There is no need for escapes
+ or any variable amount of entities for one character, all Unicode
+ code points can be stored in one single 32-bit entity. As with
+ UTF-16, there are byte-order issues, UTF-32 can be both big- and
+ little-endian.</item>
+ <tag>UCS-4</tag>
+ <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 numbers are left unused in the Unicode standard
+ 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 code point 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 might become 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>Areas of Unicode Support</title>
+ <p>To support Unicode in Erlang, problems in several areas have been
+ addressed. Each area is described briefly in this section and more
+ thoroughly further down in this document:</p>
+ <taglist>
+ <tag>Representation</tag>
+ <item>To handle Unicode characters in Erlang, we have to have a
+ common representation both in lists and binaries. The EEP (10) and
+ the subsequent initial implementation in R13A settled a standard
+ representation of Unicode characters in Erlang.</item>
+ <tag>Manipulation</tag>
+ <item>The Unicode characters need to be processed by the Erlang
+ program, why library functions need to be able to handle them. In
+ some cases functionality was added to already existing interfaces
+ (as the string module now can handle lists with arbitrary code points),
+ in some cases new functionality or options need to be added (as in
+ the <c>io</c>-module, the file handling, the <c>unicode</c> module
+ and the bit syntax). Today most modules in kernel and STDLIB, as
+ well as the VM are Unicode aware.</item>
+ <tag>File I/O</tag>
+ <item>I/O is by far the most problematic area for Unicode. A file
+ is an entity where bytes are stored and the lore of programming
+ has been to treat characters and bytes as interchangeable. With
+ Unicode characters, you need to decide on an encoding as soon as
+ you want to store the data in a file. In Erlang you can open a
+ text file with an encoding option, so that you can read characters
+ from it rather than bytes, but you can also open a file for
+ bytewise I/O. The I/O-system of Erlang has been designed (or at
+ least used) in a way where you expect any I/O-server to be
+ able to cope with any string data, but that is no longer the case
+ when you work with Unicode characters. Handling the fact that you
+ need to know the capabilities of the device where your data ends
+ up is something new to the Erlang programmer. Furthermore, ports
+ in Erlang are byte oriented, so an arbitrary string of (Unicode)
+ characters can not be sent to a port without first converting it
+ to an encoding of choice.</item>
+ <tag>Terminal I/O</tag>
+ <item>Terminal I/O is slightly easier than file I/O. The output is
+ meant for human reading and is usually Erlang syntax (e.g. in the
+ shell). There exists syntactic representation of any Unicode
+ character without actually displaying the glyph (instead written
+ as <c>\x{</c>HHH<c>}</c>), so Unicode data can usually be displayed
+ even if the terminal as such do not support the whole Unicode
+ range.</item>
+ <tag>File names</tag>
+ <item>File names can be stored as Unicode strings, in different
+ ways depending on the underlying OS and file system. This can be
+ handled fairly easy by a program. The problems arise when the file
+ system is not consistent in it's encodings, like for example
+ Linux. Linux allows files to be named with any sequence of bytes,
+ leaving to each program to interpret those bytes. On systems where
+ these "transparent" file names are used, Erlang has to be informed
+ about the file name encoding by a startup flag. The default is
+ bytewise interpretation, which is actually usually wrong, but
+ allows for interpretation of <em>all</em> file names. The concept
+ of "raw file names" can be used to handle wrongly encoded
+ file names if one enables Unicode file name translation
+ (<c>+fnu</c>) on platforms where this is not the default.</item>
+ <tag>Source code encoding</tag>
+ <item>When it comes to the Erlang source code, there is support
+ for the UTF-8 encoding and bytewise encoding. The default in R16B
+ is bytewise (or latin1) encoding. You can control the encoding by
+ a comment like:
+<code>
+%% -*- coding: utf-8 -*-
+</code>
+ in the beginning of the file. This of course requires your editor to
+ support UTF-8 as well. The same comment is also interpreted by
+ functions like <c>file:consult/1</c>, the release handler etc, so that
+ you can have all text files in your source directories in UTF-8
+ encoding.
+ </item>
+ <tag>The language</tag>
+ <item>Having the source code in UTF-8 also allows you to write
+ string literals containing Unicode characters with code points &gt;
+ 255, although atoms, module names and function names will be
+ restricted to the ISO-Latin-1 range until the R18 release. Binary
+ literals where you use the <c>/utf8</c> type, can also be
+ expressed using Unicode characters &gt; 255. Having module names
+ using characters other than 7-bit ASCII can cause trouble on
+ operating systems with inconsistent file naming schemes, and might
+ also hurt portability, so it's not really recommended. It is
+ suggested in EEP 40 that the language should also allow for
+ Unicode characters &gt; 255 in variable names. Whether to
+ implement that EEP or not is yet to be decided.</item>
+ </taglist>
+</section>
+<section>
+ <title>Standard Unicode Representation</title>
+ <p>In Erlang, strings are actually lists of integers. A string was
+ up until R13 defined to be encoded in the ISO-latin-1 (ISO8859-1)
+ character set, which is, code point by code point, a sub-range of
+ the Unicode character set.</p>
+ <p>The standard list encoding for strings was therefore easily
+ extended to cope with the whole Unicode range: A Unicode string in
+ Erlang is simply a list containing integers, each integer being a
+ valid Unicode code point and representing one character in the
+ Unicode character set.</p>
+ <p>Erlang strings in ISO-latin-1 are a subset of Unicode
+ strings.</p>
+ <p>Only if a string contains code points &lt; 256, can it be
+ directly converted to a binary by using
+ i.e. <c>erlang:iolist_to_binary/1</c> or can be sent directly to a
+ port. If the string contains Unicode characters &gt; 255, an
+ encoding has to be decided upon and the string should be converted
+ to a binary in the preferred encoding using
+ <c>unicode:characters_to_binary/{1,2,3}</c>. Strings are not
+ generally lists of bytes, as they were before R13. They are lists of
+ characters. Characters are not generally bytes, they are Unicode
+ code points.</p>
+
+ <p>Binaries 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>, an ISO-Latin-1 Erlang string could
+ be converted into a binary, effectively using bytewise encoding -
+ one byte per character. This was very convenient for those limited
+ Erlang strings, but cannot be done for arbitrary Unicode lists.</p>
+ <p>As the UTF-8 encoding is widely spread and provides some backward
+ compatibility in the 7-bit ASCII range, it is selected as the
+ standard encoding for 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 <c>iodata</c> or <c>iolist</c>s, 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 code points:</p>
+ <code type="none">
unicode_binary() = binary() with characters encoded in UTF-8 coding standard
-unicode_char() = integer() representing valid unicode codepoint
chardata() = charlist() | unicode_binary()
-charlist() = [unicode_char() | unicode_binary() | charlist()]
- a unicode_binary is allowed as the tail of the list</code>
-<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)
+charlist() = maybe_improper_list(char() | unicode_binary() | charlist(),
+ unicode_binary() | nil())</code>
+ <p>The module <seealso
+ marker="stdlib:unicode"><c>unicode</c></seealso> 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_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() = maybe_improper_list(char() |
+ external_unicode_binary() |
+ external_charlist(),
+ external_unicode_binary() | nil())</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>
-
-<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>
-<code>
+ <title>Basic Language Support</title>
+ <p><marker id="unicode_in_erlang"/>As of Erlang/OTP R16 Erlang
+ source files can be written in either UTF-8 or bytewise encoding
+ (a.k.a. <c>latin1</c> encoding). The details on how to state the encoding
+ of an Erlang source file can be found in
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>. Strings and comments
+ can be written using Unicode, but functions still have to be named
+ using characters from the ISO-latin-1 character set and atoms are
+ restricted to the same ISO-latin-1 range. These restrictions in the
+ language are of course independent of the encoding of the source
+ file. 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>
+ <code>
&lt;&lt;Ch/utf8,_/binary&gt;&gt; = Bin1,
&lt;&lt;Ch/utf16-little,_/binary&gt;&gt; = Bin2,
Bin3 = &lt;&lt;$H/utf32-little, $e/utf32-little, $l/utf32-little, $l/utf32-little,
- $o/utf32-little&gt;&gt;,</code>
-<p>For convenience, literal strings can be encoded with a Unicode encoding in binaries using the following (or similar) syntax:</p>
-<code>
+$o/utf32-little&gt;&gt;,</code>
+ <p>For convenience, literal strings can be encoded with a Unicode
+ encoding in binaries using the following (or similar) syntax:</p>
+ <code>
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>
+ </section>
+ <section>
+ <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 code point
+ literally in a string even when the encoding of the source file is
+ bytewise (<c>latin1</c>).</p>
+ <p>In the shell, if using a Unicode input device, or in source
+ code stored in UTF-8, <c>$</c> can be followed directly by a
+ Unicode character producing an integer. In the following example
+ the code point of a Cyrillic <c>с</c> is output:</p>
+ <pre>
+7> <input>$с.</input>
+1089</pre>
+ </section>
+ <section>
+ <title>Heuristic String Detection</title>
+ <p>In certain output functions and in the output of return values
+ in the shell, Erlang tries to heuristically detect string data in
+ lists and binaries. Typically you will see heuristic detection in
+ a situation like this:</p>
+ <pre>
+1> <input>[97,98,99].</input>
+"abc"
+2> <input>&lt;&lt;97,98,99&gt;&gt;.</input>
+&lt;&lt;"abc"&gt;&gt;
+3> <input>&lt;&lt;195,165,195,164,195,182&gt;&gt;.</input>
+&lt;&lt;"åäö"/utf8&gt;&gt;</pre>
+ <p>Here the shell will detect lists containing printable
+ characters or binaries containing printable characters either in
+ bytewise or UTF-8 encoding. The question here is: what is a
+ printable character? One view would be that anything the Unicode
+ standard thinks is printable, will also be printable according to
+ the heuristic detection. The result would be that almost any list
+ of integers will be deemed a string, resulting in all sorts of
+ characters being printed, maybe even characters your terminal does
+ not have in its font set (resulting in some generic output you
+ probably will not appreciate). Another way is to keep it backwards
+ compatible so that only the ISO-Latin-1 character set is used to
+ detect a string. A third way would be to let the user decide
+ exactly what Unicode ranges are to be viewed as characters. In
+ R16B you can select either the whole Unicode range or the
+ ISO-Latin-1 range by supplying the startup flag <c>+pc
+ </c><i>Range</i>, where <i>Range</i> is either <c>latin1</c> or
+ <c>unicode</c>. For backwards compatibility, the default is
+ <c>latin1</c>. This only controls how heuristic string detection
+ is done. In the future, more ranges are expected to be added, so
+ that one can tailor the heuristics to the language and region
+ relevant to the user.</p>
+ <p>Lets look at an example with the two different startup options:</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>
+$ <input>erl +pc latin1</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (abort with ^G)
+1> <input>[1024].</input>
+[1024]
+2> <input>[1070,1085,1080,1082,1086,1076].</input>
+[1070,1085,1080,1082,1086,1076]
+3> <input>[229,228,246].</input>
+"åäö"
+4> <input>&lt;&lt;208,174,208,189,208,184,208,186,208,190,208,180&gt;&gt;.</input>
+&lt;&lt;208,174,208,189,208,184,208,186,208,190,208,180&gt;&gt;
+5> <input>&lt;&lt;229/utf8,228/utf8,246/utf8&gt;&gt;.</input>
+&lt;&lt;"åäö"/utf8&gt;&gt;
+</pre>
<pre>
-4> <input>$\x{400}.</input>
-1024</pre>
-<p>This also is a translation by the scanner:</p>
+$ <input>erl +pc unicode</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (abort with ^G)
+1> <input>[1024].</input>
+"Ѐ"
+2> <input>[1070,1085,1080,1082,1086,1076].</input>
+"Юникод"
+3> <input>[229,228,246].</input>
+"åäö"
+4> <input>&lt;&lt;208,174,208,189,208,184,208,186,208,190,208,180&gt;&gt;.</input>
+&lt;&lt;"Юникод"/utf8&gt;&gt;
+5> <input>&lt;&lt;229/utf8,228/utf8,246/utf8&gt;&gt;.</input>
+&lt;&lt;"åäö"/utf8&gt;&gt;
+</pre>
+ <p>In the examples, we can see that the default Erlang shell will
+ only interpret characters from the ISO-Latin1 range as printable
+ and will only detect lists or binaries with those "printable"
+ characters as containing string data. The valid UTF-8 binary
+ containing "Юникод", will not be printed as a string. When, on the
+ other hand, started with all Unicode characters printable (<c>+pc
+ unicode</c>), the shell will output anything containing printable
+ Unicode data (in binaries either UTF-8 or bytewise encoded) as
+ string data.</p>
+
+ <p>These heuristics are also used by
+ <c>io</c>(<c>_lib</c>)<c>:format/2</c> and friends when the
+ <c>t</c> modifier is used in conjunction with <c>~p</c> or
+ <c>~P</c>:</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>
+$ <input>erl +pc latin1</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (abort with ^G)
+1> <input>io:format("~tp~n",[{&lt;&lt;"åäö"&gt;&gt;, &lt;&lt;"åäö"/utf8&gt;&gt;, &lt;&lt;208,174,208,189,208,184,208,186,208,190,208,180&gt;&gt;}]).</input>
+{&lt;&lt;"åäö"&gt;&gt;,&lt;&lt;"åäö"/utf8&gt;&gt;,&lt;&lt;208,174,208,189,208,184,208,186,208,190,208,180&gt;&gt;}
+ok
+</pre>
<pre>
-7> <input>$c.</input>
-1089</pre>
+$ <input>erl +pc unicode</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (abort with ^G)
+1> <input>io:format("~tp~n",[{&lt;&lt;"åäö"&gt;&gt;, &lt;&lt;"åäö"/utf8&gt;&gt;, &lt;&lt;208,174,208,189,208,184,208,186,208,190,208,180&gt;&gt;}]).</input>
+{&lt;&lt;"åäö"&gt;&gt;,&lt;&lt;"åäö"/utf8&gt;&gt;,&lt;&lt;"Юникод"/utf8&gt;&gt;}
+ok
+</pre>
+ <p>Please observe that this only affects <i>heuristic</i> interpretation
+ of lists and binaries on output. For example the <c>~ts</c> format
+ sequence does always output a valid lists of characters,
+ regardless of the <c>+pc</c> setting, as the programmer has
+ explicitly requested string output.</p>
+ </section>
</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>
-<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>
-<pre>
+ <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, 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-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>
-<pre>
+ <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>To investigate what Erlang thinks about the terminal, the <c>io:getopts()</c> call can be used when the shell is started:</p>
-<pre>
+ <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.1) [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.1 (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.1) [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.1 (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>
-<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>
+
+ <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, 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>
+
+ <pre>
+$ <input>erl</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (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>
+ <pre>
+$ <input>erl</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (abort with ^G)
+1> <input>$ξ.</input>
+958
+2> <input>Юникод.</input>
+* 1: illegal character
+2> </pre>
</section>
<section>
-<title>Unicode file names</title>
-<p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p>
-<taglist>
-<tag>Mandatory Unicode file naming</tag>
-<item>
-<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem have names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows has selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in &quot;Unicode file name translation mode&quot;, meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p>
-<p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p>
-<p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p>
-</item>
-<tag>Transparent file naming</tag>
-<item>
-<p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as &quot;raw file names&quot; if Unicode file naming support is turned on.</p>
-<p>A raw file name is not a list, but a binary. Many non core applications still do not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, do not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p>
-</item>
-</taglist>
-<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p>
+ <title>Unicode File Names</title>
+ <p>Most modern operating systems support Unicode file names in some
+ way or another. There are several different ways to do this and
+ Erlang by default treats the different approaches differently:</p>
+ <taglist>
+ <tag>Mandatory Unicode file naming</tag>
+ <item>
+ <p>Windows and, for most common uses, MacOS X enforces Unicode
+ support for file names. All files created in the file system have
+ names that can consistently be interpreted. In MacOS X, all file
+ names are retrieved in UTF-8 encoding, while Windows has
+ selected an approach where each system call handling file names
+ has a special Unicode aware variant, giving much the same
+ effect. There are no file names on these systems that are not
+ Unicode file names, why the default behavior of the Erlang VM is
+ to work in &quot;Unicode file name translation mode&quot;,
+ meaning that a file name can be given as a Unicode list and that
+ will be automatically translated to the proper name encoding for
+ the underlying operating and file system.</p>
+ <p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems
+ may return Unicode lists with code points beyond 255, depending
+ on the content of the actual file system.</p>
+ <p>As the feature is fairly new, you may still stumble upon non
+ core applications that cannot handle being provided with file
+ names containing characters with code points larger than 255, but
+ the core Erlang system should have no problems with Unicode file
+ names.</p>
+ </item>
+ <tag>Transparent file naming</tag>
+ <item>
+ <p>Most Unix operating systems have adopted a simpler approach,
+ namely that Unicode file naming is not enforced, but by
+ convention. Those systems usually use UTF-8 encoding for Unicode
+ file names, but do not enforce it. On such a system, a file name
+ containing characters having code points between 128 and 255 may
+ be named either as plain ISO-latin-1 or using UTF-8 encoding. As
+ no consistency is enforced, the Erlang VM can do no consistent
+ translation of all file names. If the VM would automatically
+ select encoding based on heuristics, one could get unexpected
+ behavior on these systems. By default, Erlang starts in "latin1"
+ file name mode on such systems, meaning bytewise encoding in file
+ names. This allows for list representation of all file names in
+ the system, but, for example, a file named "Östersund.txt", will
+ appear in <c>file:list_dir/1</c> as either "Östersund.txt" (if
+ the file name was encoded in bytewise ISO-Latin-1 by the program
+ creating the file, or more probably as
+ <c>[195,150,115,116,101,114,115,117,110,100]</c>, which is a
+ list containing UTF-8 bytes - not what you would want... If you
+ on the other hand use Unicode file name translation on such a
+ system, non-UTF-8 file names will simply be ignored by functions
+ like <c>file:list_dir/1</c>. They can be retrieved with
+ <c>file:list_dir_all/1</c>, but wrongly encoded file names will
+ appear as &quot;raw file names&quot;.</p>
+
+ </item>
+ </taglist>
+
+ <p>The Unicode file naming support was introduced with OTP release
+ R14B01. A VM operating in Unicode file name translation 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 get
+ Unicode lists as return value. The support lies in the Kernel and
+ STDLIB modules, why most applications (that does not explicitly
+ require the file names to be in the ISO-latin-1 range) will benefit
+ from the Unicode support without change.</p>
-<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, were completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p>
+ <p>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 MacOS X as the vfs layer of
+ the OS will accept all your file names as UTF-8 and will not rewrite
+ them.</p>
-<p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p>
+ <p>For most systems, turning on Unicode file name translation is no
+ problem even if it uses transparent file naming. Very few systems
+ have mixed file name encodings. A consistent UTF-8 named system will
+ work perfectly in Unicode file name mode. It was still however
+ considered experimental in R14B01 and is still not the default on
+ such systems. Unicode file name translation is turned on with the
+ <c>+fnu</c> switch to the 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. On Windows and MacOS X, the
+ default behavior is that of Unicode 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, as stated before, be
+ changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see
+ the <seealso marker="erts:erl"><c>erl</c></seealso> program. If the
+ VM is started in Unicode file name translation mode,
+ <c>file:native_name_encoding/0</c> will return the atom
+ <c>utf8</c>. The <c>+fnu</c> switch can be followed by <c>w</c>,
+ <c>i</c> or <c>e</c>, to control how wrongly encoded file names are
+ to be reported. <c>w</c> means that a warning is sent to the
+ <c>error_logger</c> whenever a wrongly encoded file name is
+ "skipped" in directory listings, <c>i</c> means that those wrongly
+ encoded file names are silently ignored and <c>e</c> means that the
+ API function will return an error whenever a wrongly encoded file
+ (or directory) name is encountered. <c>w</c> is the default. Note
+ that <c>file:read_link/1</c> will always return an error if the link
+ points to an invalid file name.</p>
-<p>In Unicode file name mode, file names given to the BIF <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are also interpreted as Unicode. So is the parameter list given in the <c>args</c> option available when using <c>spawn_executable</c>. The UTF-8 translation of arguments can be avoided using binaries, see the discussion about raw file names below.</p>
+ <p>In Unicode file name mode, file names given to the BIF
+ <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are
+ also interpreted as Unicode. So is the parameter list given in the
+ <c>args</c> option available when using <c>spawn_executable</c>. The
+ UTF-8 translation of arguments can be avoided using binaries, see
+ the discussion about raw file names below.</p>
-<p>It is worth noting that the file <c>encoding</c> options given when opening a file has nothing to do with the file <em>name</em> encoding convention. You can very well open files containing UTF-8 but having file names in ISO-latin-1 or vice versa.</p>
+ <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 data
+ encoded in UTF-8 but having file names in bytewise (<c>latin1</c>) encoding
+ 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 code points 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>
-<p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p>
-<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The &quot;UTF-8 list&quot; is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p>
-<p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p>
+ <title>Notes About Raw File Names</title>
+
+ <p>Raw file names were introduced together with Unicode file name
+ support in erts-5.8.2 (OTP R14B01). The reason &quot;raw file
+ names&quot; was introduced in the system was 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. By the common <c>file:list_dir/1</c> function, the wrongly
+ encoded file names are simply ignored in Unicode file name
+ translation mode, but by the <c>file:list_dir_all/1</c> function,
+ the file names with invalid encoding are returned as &quot;raw&quot;
+ file names, i.e. as binaries.</p>
+
+ <p>The Erlang <c>file</c> module accepts raw file names as
+ input. <c>open_port({spawn_executable, ...} ...)</c> also accepts
+ 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 was considered experimental in OTP R14B01 due to
+ the fact that the initial implementation did not ignore wrongly
+ encoded file names, so that raw file names could spread unexpectedly
+ throughout the system. Beginning with R16B, the wrongly encoded file
+ names are only retrieved by special functions
+ (e.g. <c>file:list_dir_all/1</c>), so the impact on existing code is
+ much lower, why it is now supported. Unicode file name translation
+ is expected to be default in future releases.</p>
+
+ <p>Even if you are operating without Unicode file naming translation
+ automatically done by the VM, you can access and create files with
+ names in UTF-8 encoding by using raw file names encoded as
+ UTF-8. Enforcing the UTF-8 encoding regardless of the mode the
+ Erlang VM is started in might, in some circumstances be a good idea,
+ as the convention of using UTF-8 file names is spreading.</p>
</section>
<section>
-<title>Notes about MacOSX</title>
-<p>MacOSXs vfs layer enforces UTF-8 file names in a quite aggressive way. Older versions did this by simply refusing to create non UTF-8 conforming file names, while newer versions replace offending bytes with the sequence &quot;%HH&quot;, where HH is the original character in hexadecimal notation. As Unicode translation is enabled by default on MacOSX, the only way to come up against this is to either start the VM with the <c>+fnl</c> flag or to use a raw file name in <c>latin1</c> encoding. In that case, the file can not be opened with the same name as the one used to create this. The problem is by design in newer versions of MacOSX.</p>
-<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>�</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name &quot;bj�rn&quot; is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p>
+ <title>Notes About MacOS X</title>
+ <p>MacOS X's 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 MacOS X, 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 bytewise (<c>latin1</c>) encoding. If using a raw
+ filename, with a bytewise encoding containing characters between 127
+ and 255, to create a file, the file can not be opened using the same
+ name as the one used to create it. There is no remedy for this
+ behaviour, other than keeping the file names in the right
+ encoding.</p>
+
+ <p>MacOS X also reorganizes the names of files so that the
+ representation of accents etc is using the "combining characters",
+ i.e. the character <c>ö</c> is represented as the code points
+ [111,776], where 111 is the character <c>o</c> and 776 is the
+ special accent character "combining diaeresis". This way of
+ normalizing Unicode is otherwise very seldom used and Erlang
+ normalizes those file names in the opposite way upon retrieval, so
+ that file names using combining accents are 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. The
+ normalization into combining accents are redone when actually
+ accessing files, so this can usually be ignored by the Erlang
+ programmer.</p>
</section>
</section>
<section>
-<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>
+ <title>Unicode in Environment 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 code points > 255. On Windows the
+ Unicode versions of the environment system API will be used, also
+ allowing for code points > 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 shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p>
-<p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p>
-<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>
-<taglist>
-<tag><c>unicode</c></tag>
-<item>
-<p>The module <seealso marker="stdlib:unicode">unicode</seealso> is obviously Unicode-aware. It contains functions for conversion between different Unicode formats as well as some utilities for identifying byte order marks. Few programs handling Unicode data will survive without this module.</p>
-</item>
-<tag><c>io</c></tag>
-<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>
-<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>
-</item>
-<tag><c>re</c></tag>
-<item>
-<p>The <seealso marker="stdlib:re">re</seealso> module allows for matching Unicode strings as a special option. As the library is actually centered on matching in binaries, the Unicode support is UTF-8-centered.</p>
-</item>
-<tag><c>wx</c></tag>
-<item>
-<p>The <seealso marker="wx:wx">wx</seealso> graphical library has extensive support for Unicode text</p>
-</item>
-</taglist>
-<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>
+ <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>
+ <taglist>
+ <tag><c>unicode</c></tag>
+ <item>
+ <p>The module <seealso marker="stdlib:unicode"><c>unicode</c></seealso>
+ is obviously Unicode-aware. It contains functions for conversion
+ between different Unicode formats as well as some utilities for
+ identifying byte order marks. Few programs handling Unicode data
+ will survive without this module.</p>
+ </item>
+ <tag><c>io</c></tag>
+ <item>
+ <p>The <seealso marker="stdlib:io"><c>io</c></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>, <c>user</c></tag>
+ <item>
+ <p>I/O-servers throughout the system are able 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"><c>shell</c></seealso> has support for
+ Unicode terminals and the <seealso
+ marker="kernel:file"><c>file</c></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"><c>io</c></seealso> module.</p>
+ </item>
+ <tag><c>re</c></tag>
+ <item>
+ <p>The <seealso marker="stdlib:re"><c>re</c></seealso> module allows
+ for matching Unicode strings as a special option. As the library
+ is actually centered on matching in binaries, the Unicode
+ support is UTF-8-centered.</p>
+ </item>
+ <tag><c>wx</c></tag>
+ <item>
+ <p>The <seealso marker="wx:wx"><c>wx</c></seealso> graphical library
+ has extensive support for Unicode text</p>
+ </item>
+ </taglist>
+ <p>The module <seealso
+ marker="stdlib:string"><c>string</c></seealso> works perfectly 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"><c>to_upper</c></seealso> and
+ <seealso marker="stdlib:string#to_lower/1"><c>to_lower</c></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, as there are language and locale
+ issues as well as multi-character mappings to consider when
+ converting 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>
-<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>
+ <title>Unicode Data in Files</title>
+ <p>The fact that Erlang as such can handle Unicode data in many forms
+ does not automatically mean that the content of any file can be
+ Unicode text. The external entities such as ports or I/O-servers are
+ not generally Unicode capable.</p>
+ <p>Ports are always byte oriented, so before sending data that you
+ are not sure is bytewise encoded to a port, make sure to encode it
+ in a proper Unicode encoding. Sometimes this will mean that only
+ part of the data shall be encoded as e.g. UTF-8, some parts may be
+ binary data (like a length indicator) or something else that shall
+ not undergo character encoding, so no automatic translation is
+ present.</p>
+ <p>I/O-servers behave a little differently. The I/O-servers connected
+ to terminals (or stdout) can usually cope with Unicode data
+ regardless of the <c>encoding</c> option. This is convenient when
+ one expects a modern environment but do not want to crash when
+ writing to a archaic terminal or pipe. Files on the other hand are
+ more picky. A file can have an encoding option which makes it
+ generally usable by the io-module (e.g. <c>{encoding,utf8}</c>), but
+ is by default opened as a byte oriented file. The <seealso
+ marker="kernel:file"><c>file</c></seealso> module is byte oriented, why only
+ ISO-Latin-1 characters can be written using that module. The
+ <seealso marker="stdlib:io"><c>io</c></seealso> module is the one to use if
+ Unicode data is to be output to a file with other <c>encoding</c>
+ than <c>latin1</c> (a.k.a. bytewise encoding). It is slightly
+ confusing that a file opened with
+ e.g. <c>file:open(Name,[read,{encoding,utf8}])</c>, cannot be
+ properly read using <c>file:read(File,N)</c> but you have to use the
+ <c>io</c> module to retrieve the Unicode data from it. The reason is
+ that <c>file:read</c> and <c>file:write</c> (and friends) are purely
+ byte oriented, and should so be, as that is the way to access
+ files other than text files - byte by byte. Just as with ports, you
+ can of course write encoded data into a file by "manually" converting
+ the data to the encoding of choice (using the <seealso
+ marker="stdlib:unicode"><c>unicode</c></seealso> module or the bit syntax)
+ and then output it on a bytewise encoded (<c>latin1</c>) file.</p>
+ <p>The rule of thumb is that the <seealso
+ marker="kernel:file"><c>file</c></seealso> module should be used for files
+ opened for bytewise access (<c>{encoding,latin1}</c>) and the
+ <seealso marker="stdlib:io"><c>io</c></seealso> module should be used when
+ accessing files with any other encoding
+ (e.g. <c>{encoding,uf8}</c>).</p>
+
+ <p>Functions reading Erlang syntax from files generally recognize
+ the <c>coding:</c> comment and can therefore handle Unicode data on
+ input. When writing Erlang Terms to a file, you should insert
+ such comments when applicable:</p>
+ <pre>
+$ <input>erl +fna +pc unicode</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (abort with ^G)
+1> <input>file:write_file("test.term",&lt;&lt;"%% coding: utf-8\n[{\"Юникод\",4711}].\n"/utf8&gt;&gt;).</input>
+ok
+2> <input>file:consult("test.term").</input>
+{ok,[[{"Юникод",4711}]]}
+ </pre>
+</section>
+<section>
+ <title><marker id="unicode_options_summary"/>Summary of Options</title>
+ <p>The Unicode support is controlled by both command line switches,
+ some standard environment variables and the version of OTP you are
+ using. Most options affect mainly the way Unicode data is displayed,
+ not the actual functionality of the API's in the standard
+ libraries. This means that Erlang programs usually do not
+ need to concern themselves with these options, they are more for the
+ development environment. An Erlang program can be written so that it
+ works well regardless of the type of system or the Unicode options
+ that are in effect.</p>
+
+ <p>Here follows a summary of the settings affecting Unicode:</p>
+ <taglist>
+ <tag>The <c>LANG</c> and <c>LC_CTYPE</c> environment variables</tag>
+ <item>
+ <p>The language setting in the OS mainly affects the shell. The
+ terminal (i.e. the group leader) will operate with <c>{encoding,
+ unicode}</c> only if the environment tells it that UTF-8 is
+ allowed. This setting should correspond to the actual terminal
+ you are using.</p>
+ <p>The environment can also affect file name interpretation, if
+ Erlang is started with the <c>+fna</c> flag.</p>
+ <p>You can check the setting of this by calling
+ <c>io:getopts()</c>, which will give you an option list
+ containing <c>{encoding,unicode}</c> or
+ <c>{encoding,latin1}</c>.</p>
+ </item>
+ <tag>The <c>+pc </c>{<c>unicode</c>|<c>latin1</c>} flag to
+ <seealso marker="erts:erl"><c>erl(1)</c></seealso></tag>
+ <item>
+ <p>This flag affects what is interpreted as string data when
+ doing heuristic string detection in the shell and in
+ <c>io</c>/<c>io_lib:format</c> with the <c>"~tp"</c> and
+ <c>~tP</c> formatting instructions, as described above.</p>
+ <p>You can check this option by calling io:printable_range/0,
+ which in R16B will return <c>unicode</c> or <c>latin1</c>. To be
+ compatible with future (expected) extensions to the settings,
+ one should rather use <c>io_lib:printable_list/1</c> to check if
+ a list is printable according to the setting. That function will
+ take into account new possible settings returned from
+ <c>io:printable_range/0</c>.</p>
+ </item>
+ <tag>The <c>+fn</c>{<c>l</c>|<c>a</c>|<c>u</c>}
+ [{<c>w</c>|<c>i</c>|<c>e</c>}]
+ flag to <seealso marker="erts:erl"><c>erl(1)</c></seealso></tag>
+ <item>
+ <p>This flag affects how the file names are to be interpreted. On
+ operating systems with transparent file naming, this has to be
+ specified to allow for file naming in Unicode characters (and
+ for correct interpretation of file names containing characters
+ &gt; 255.</p>
+ <p><c>+fnl</c> means bytewise interpretation of file names, which
+ was the usual way to represent ISO-Latin-1 file names before
+ UTF-8 file naming got widespread. This is the default on all
+ Unix-like operating systems except MacOS X.</p>
+ <p><c>+fnu</c> means that file names are encoded in UTF-8, which
+ is nowadays the common scheme (although not enforced).</p>
+ <p><c>+fna</c> means that you automatically select between
+ <c>+fnl</c> and <c>+fnu</c>, based on the <c>LANG</c> and
+ <c>LC_CTYPE</c> environment variables. This is optimistic
+ heuristics indeed, nothing enforces a user to have a terminal
+ with the same encoding as the file system, but usually, this is
+ the case. This might be the default behavior in a future
+ release.</p>
+
+ <p>The file name translation mode can be read with the
+ <c>file:native_name_encoding/0</c> function, which returns
+ <c>latin1</c> (meaning bytewise encoding) or <c>utf8</c>.</p>
+ </item>
+ <tag><seealso marker="stdlib:epp#default_encoding/0">
+ <c>epp:default_encoding/0</c></seealso></tag>
+ <item>
+ <p>This function returns the default encoding for Erlang source
+ files (if no encoding comment is present) in the currently
+ running release. For R16 this returns <c>latin1</c> (meaning
+ bytewise encoding). In R17 and forward it is expected to return
+ <c>utf8</c>.</p>
+ <p>The encoding of each file can be specified using comments as
+ described in
+ <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p>
+ </item>
+ <tag><seealso marker="stdlib:io#setopts/1"><c>io:setopts/</c>{<c>1</c>,<c>2</c>}</seealso> and the <c>-oldshell</c>/<c>-noshell</c> flags.</tag>
+ <item>
+ <p>When Erlang is started with <c>-oldshell</c> or
+ <c>-noshell</c>, the I/O-server for <c>standard_io</c> is default
+ set to bytewise encoding, while an interactive shell defaults to
+ what the environment variables says.</p>
+ <p>With the <c>io:setopts/2</c> function you can set the
+ encoding of a file or other I/O-server. This can also be set when
+ opening a file. Setting the terminal (or other
+ <c>standard_io</c> server) unconditionally to the option
+ <c>{encoding,utf8}</c> will for example make UTF-8 encoded characters
+ being written to the device regardless of how Erlang was started or
+ the users environment.</p>
+ <p>Opening files with <c>encoding</c> option is convenient when
+ writing or reading text files in a known encoding.</p>
+ <p>You can retrieve the <c>encoding</c> setting for an I/O-server
+ using <seealso
+ marker="stdlib:io#getopts/1"><c>io:getopts()</c></seealso>.</p>
+ </item>
+ </taglist>
+</section>
<section>
-<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>
+ <title>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>
+ <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
+ code point 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"><c>io</c></seealso>
+ module). Note that error handling is omitted from the code:</p>
<code>
open_bom_file_for_reading(File) -&gt;
{ok,F} = file:open(File,[read,binary]),
@@ -261,8 +1119,15 @@ 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>To open a file for writing and putting the BOM first is even simpler:</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;
{ok,F} = file:open(File,[write,binary]),
@@ -270,29 +1135,95 @@ open_bom_file_for_writing(File,Encoding) -&gt;
io:setopts(F,[{encoding,Encoding}]),
{ok,F}.
</code>
-<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>
-<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>
+ <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 code
+ points beyond the ISO-latin-1 range.</p>
+ </section>
+ <section>
+ <title>Formatted I/O</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"><c>io</c></seealso> or <seealso
+ marker="stdlib:io_lib"><c>io_lib</c></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 code points 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>
+åäö
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>
-</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>
-<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>
+ <p>Obviously the second <c>io:format/2</c> gives undesired output
+ because the UTF-8 binary is not in latin1. For backward
+ compatibility, the non prefixed <c>s</c> control character expects
+ bytewise encoded ISO-latin-1 characters in binaries and lists
+ containing only code points &lt; 256.</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. A
+ bytewise encoded binary will also be interpreted as a string and
+ printed even when using <c>~ts</c>, but it might be mistaken for a
+ valid UTF-8 string and one should therefore avoid using the
+ <c>~ts</c> control if the binary contains bytewise encoded
+ characters and not UTF-8.</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 +pc unicode</input>
+Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false]
+
+Eshell V5.10.1 (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>The Unicode string is returned as a Unicode list, which is
+ recognized as such since the Erlang shell uses the Unicode
+ encoding (and is started with all Unicode characters considered
+ printable). The Unicode list is valid input to the <seealso
+ marker="stdlib:io#put_chars/2"><c>io:put_chars/2</c></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 code points 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 is
+ 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 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;
case unicode:characters_to_binary(Bin,utf8,utf8) of
Bin ->
@@ -300,9 +1231,16 @@ heuristic_encoding_bin(Bin) when is_binary(Bin) -&gt;
_ ->
latin1
end.
-</code>
-<p>If one does not have a complete binary of the file content, one could instead chunk through the file and check part by part. The return-tuple <c>{incomplete,Decoded,Rest}</c> from <c>unicode:characters_to_binary/{1,2,3}</c> comes in handy. The incomplete rest from one chunk of data read from the file is prepended to the next chunk and we therefore circumvent the problem of character boundaries when reading chunks of bytes in UTF-8 encoding:</p>
-<code>
+ </code>
+ <p>If one does not have a complete binary of the file content, one
+ could instead chunk through the file and check part by part. The
+ return-tuple <c>{incomplete,Decoded,Rest}</c> from
+ <c>unicode:characters_to_binary/{1,2,3}</c> comes in handy. The
+ incomplete rest from one chunk of data read from the file is
+ prepended to the next chunk and we therefore circumvent the
+ problem of character boundaries when reading chunks of bytes in
+ UTF-8 encoding:</p>
+ <code>
heuristic_encoding_file(FileName) -&gt;
{ok,F} = file:open(FileName,[read,binary]),
loop_through_file(F,&lt;&lt;&gt;&gt;,file:read(F,1024)).
@@ -320,9 +1258,12 @@ loop_through_file(F,Acc,{ok,Bin}) when is_binary(Bin) -&gt;
Res when is_binary(Res) ->
loop_through_file(F,&lt;&lt;&gt;&gt;,file:read(F,1024))
end.
-</code>
-<p>Another option is to try to read the whole file in utf8 encoding and see if it fails. Here we need to read the file using <c>io:get_chars/3</c>, as we have to succeed in reading characters with a codepoint over 255:</p>
-<code>
+ </code>
+ <p>Another option is to try to read the whole file in UTF-8
+ encoding and see if it fails. Here we need to read the file using
+ <c>io:get_chars/3</c>, as we have to succeed in reading characters
+ with a code point over 255:</p>
+ <code>
heuristic_encoding_file2(FileName) -&gt;
{ok,F} = file:open(FileName,[read,binary,{encoding,utf8}]),
loop_through_file2(F,io:get_chars(F,'',1024)).
@@ -333,7 +1274,68 @@ loop_through_file2(_,{error,_Err}) -&gt;
latin1;
loop_through_file2(F,Bin) when is_binary(Bin) -&gt;
loop_through_file2(F,io:get_chars(F,'',1024)).
-</code>
-</section>
+ </code>
+ </section>
+ <section>
+ <title>Lists of UTF-8 Bytes</title>
+ <p>For various reasons, you may find yourself having a list of
+ UTF-8 bytes. This is not a regular string of Unicode characters as
+ each element in the list does not contain one character. Instead
+ you get the "raw" UTF-8 encoding that you have in binaries. This
+ is easily converted to a proper Unicode string by first converting
+ byte per byte into a binary and then converting the binary of
+ UTF-8 encoded characters back to a Unicode string:</p>
+ <code>
+ utf8_list_to_string(StrangeList) ->
+ unicode:characters_to_list(list_to_binary(StrangeList)).
+ </code>
+ </section>
+ <section>
+ <title>Double UTF-8 Encoding</title>
+ <p>When working with binaries, you may get the horrible "double
+ UTF-8 encoding", where strange characters are encoded in your
+ binaries or files that you did not expect. What you may have got,
+ is a UTF-8 encoded binary that is for the second time encoded as
+ UTF-8. A common situation is where you read a file, byte by byte,
+ but the actual content is already UTF-8. If you then convert the
+ bytes to UTF-8, using i.e. the <c>unicode</c> module or by
+ writing to a file opened with the <c>{encoding,utf8}</c>
+ option. You will have each <i>byte</i> in the in the input file
+ encoded as UTF-8, not each character of the original text (one
+ character may have been encoded in several bytes). There is no
+ real remedy for this other than being very sure of which data is
+ actually encoded in which format, and never convert UTF-8 data
+ (possibly read byte by byte from a file) into UTF-8 again.</p>
+ <p>The by far most common situation where this happens, is when
+ you get lists of UTF-8 instead of proper Unicode strings, and then
+ convert them to UTF-8 in a binary or on a file:</p>
+ <code>
+ wrong_thing_to_do() ->
+ {ok,Bin} = file:read_file("an_utf8_encoded_file.txt"),
+ MyList = binary_to_list(Bin), %% Wrong! It is an utf8 binary!
+ {ok,C} = file:open("catastrophe.txt",[write,{encoding,utf8}]),
+ io:put_chars(C,MyList), %% Expects a Unicode string, but get UTF-8
+ %% bytes in a list!
+ file:close(C). %% The file catastrophe.txt contains more or less unreadable
+ %% garbage!
+ </code>
+ <p>Make very sure you know what a binary contains before
+ converting it to a string. If no other option exists, try
+ heuristics:</p>
+ <code>
+ if_you_can_not_know() ->
+ {ok,Bin} = file:read_file("maybe_utf8_encoded_file.txt"),
+ MyList = case unicode:characters_to_list(Bin) of
+ L when is_list(L) ->
+ L;
+ _ ->
+ binary_to_list(Bin) %% The file was bytewise encoded
+ end,
+ %% Now we know that the list is a Unicode string, not a list of UTF-8 bytes
+ {ok,G} = file:open("greatness.txt",[write,{encoding,utf8}]),
+ io:put_chars(G,MyList), %% Expects a Unicode string, which is what it gets!
+ file:close(G). %% The file contains valid UTF-8 encoded Unicode characters!
+ </code>
+ </section>
</section>
</chapter>
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..484bf0e080 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2011</year>
+ <year>2006</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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 14304824d3..f3387d669b 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -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
@@ -149,7 +149,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
-ERL_COMPILE_FLAGS += -I../include -I../../kernel/include
+ERL_COMPILE_FLAGS += -I../include -I../../kernel/include -Werror
# ----------------------------------------------------
# Targets
@@ -171,27 +171,29 @@ primary_bootstrap_compiler: \
$(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..7bf281bd8a 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
@@ -29,6 +29,7 @@
%%-------------------------------------------------------------------------
-type ascii_string() :: [1..255].
+-type ascii_binary() :: binary().
%%-------------------------------------------------------------------------
%% encode_to_string(ASCII) -> Base64String
@@ -39,7 +40,7 @@
%%-------------------------------------------------------------------------
-spec encode_to_string(Data) -> Base64String when
- Data :: string() | binary(),
+ Data :: ascii_string() | ascii_binary(),
Base64String :: ascii_string().
encode_to_string(Bin) when is_binary(Bin) ->
@@ -56,15 +57,15 @@ encode_to_string(List) when is_list(List) ->
%%-------------------------------------------------------------------------
-spec encode(Data) -> Base64 when
- Data :: string() | binary(),
- Base64 :: binary().
+ Data :: ascii_string() | ascii_binary(),
+ Base64 :: ascii_binary().
encode(Bin) when is_binary(Bin) ->
encode_binary(Bin);
encode(List) when is_list(List) ->
list_to_binary(encode_l(List)).
--spec encode_l(string()) -> ascii_string().
+-spec encode_l(ascii_string()) -> ascii_string().
encode_l([]) ->
[];
@@ -107,8 +108,8 @@ encode_binary(Bin) ->
%%-------------------------------------------------------------------------
-spec decode(Base64) -> Data when
- Base64 :: string() | binary(),
- Data :: binary().
+ Base64 :: ascii_string() | ascii_binary(),
+ Data :: ascii_binary().
decode(Bin) when is_binary(Bin) ->
decode_binary(<<>>, Bin);
@@ -116,21 +117,21 @@ decode(List) when is_list(List) ->
list_to_binary(decode_l(List)).
-spec mime_decode(Base64) -> Data when
- Base64 :: string() | binary(),
- Data :: binary().
+ Base64 :: ascii_string() | ascii_binary(),
+ Data :: ascii_binary().
mime_decode(Bin) when is_binary(Bin) ->
mime_decode_binary(<<>>, Bin);
mime_decode(List) when is_list(List) ->
mime_decode(list_to_binary(List)).
--spec decode_l(string()) -> string().
+-spec decode_l(ascii_string()) -> ascii_string().
decode_l(List) ->
L = strip_spaces(List, []),
decode(L, []).
--spec mime_decode_l(string()) -> string().
+-spec mime_decode_l(ascii_string()) -> ascii_string().
mime_decode_l(List) ->
L = strip_illegal(List, [], 0),
@@ -148,8 +149,8 @@ mime_decode_l(List) ->
%%-------------------------------------------------------------------------
-spec decode_to_string(Base64) -> DataString when
- Base64 :: string() | binary(),
- DataString :: string().
+ Base64 :: ascii_string() | ascii_binary(),
+ DataString :: ascii_string().
decode_to_string(Bin) when is_binary(Bin) ->
decode_to_string(binary_to_list(Bin));
@@ -157,8 +158,8 @@ decode_to_string(List) when is_list(List) ->
decode_l(List).
-spec mime_decode_to_string(Base64) -> DataString when
- Base64 :: string() | binary(),
- DataString :: string().
+ Base64 :: ascii_string() | ascii_binary(),
+ DataString :: ascii_string().
mime_decode_to_string(Bin) when is_binary(Bin) ->
mime_decode_to_string(binary_to_list(Bin));
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index e9a5e6831e..fe7e0f8e60 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -240,21 +240,21 @@ format_error({error, Error}) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({unknown_chunk, File, ChunkName}) ->
- io_lib:format("~p: Cannot find chunk ~p~n", [File, ChunkName]);
+ io_lib:format("~tp: Cannot find chunk ~p~n", [File, ChunkName]);
format_error({invalid_chunk, File, ChunkId}) ->
- io_lib:format("~p: Invalid contents of chunk ~p~n", [File, ChunkId]);
+ io_lib:format("~tp: Invalid contents of chunk ~p~n", [File, ChunkId]);
format_error({not_a_beam_file, File}) ->
- io_lib:format("~p: Not a BEAM file~n", [File]);
+ io_lib:format("~tp: Not a BEAM file~n", [File]);
format_error({file_error, File, Reason}) ->
- io_lib:format("~p: ~p~n", [File, file:format_error(Reason)]);
+ io_lib:format("~tp: ~tp~n", [File, file:format_error(Reason)]);
format_error({missing_chunk, File, ChunkId}) ->
- io_lib:format("~p: Not a BEAM file: no IFF \"~s\" chunk~n",
+ io_lib:format("~tp: Not a BEAM file: no IFF \"~s\" chunk~n",
[File, ChunkId]);
format_error({invalid_beam_file, File, Pos}) ->
- io_lib:format("~p: Invalid format of BEAM file near byte number ~p~n",
+ io_lib:format("~tp: Invalid format of BEAM file near byte number ~p~n",
[File, Pos]);
format_error({chunk_too_big, File, ChunkId, Size, Len}) ->
- io_lib:format("~p: Size of chunk \"~s\" is ~p bytes, "
+ io_lib:format("~tp: Size of chunk \"~s\" is ~p bytes, "
"but only ~p bytes could be read~n",
[File, ChunkId, Size, Len]);
format_error({chunks_different, Id}) ->
@@ -265,16 +265,16 @@ format_error({modules_different, Module1, Module2}) ->
io_lib:format("Module names ~p and ~p differ in the two files~n",
[Module1, Module2]);
format_error({not_a_directory, Name}) ->
- io_lib:format("~p: Not a directory~n", [Name]);
+ io_lib:format("~tp: Not a directory~n", [Name]);
format_error({key_missing_or_invalid, File, abstract_code}) ->
- io_lib:format("~p: Cannot decrypt abstract code because key is missing or invalid",
+ io_lib:format("~tp: Cannot decrypt abstract code because key is missing or invalid",
[File]);
format_error(badfun) ->
"not a fun or the fun has the wrong arity";
format_error(exists) ->
"a fun has already been installed";
format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
%%
%% Exported functions for encrypted debug info.
@@ -324,13 +324,13 @@ diff_directories(Dir1, Dir2) ->
{OnlyDir1, OnlyDir2, Diff} = compare_dirs(Dir1, Dir2),
diff_only(Dir1, OnlyDir1),
diff_only(Dir2, OnlyDir2),
- foreach(fun(D) -> io:format("** different: ~p~n", [D]) end, Diff),
+ foreach(fun(D) -> io:format("** different: ~tp~n", [D]) end, Diff),
ok.
diff_only(_Dir, []) ->
ok;
diff_only(Dir, Only) ->
- io:format("Only in ~p: ~p~n", [Dir, Only]).
+ io:format("Only in ~tp: ~tp~n", [Dir, Only]).
%% -> {OnlyInDir1, OnlyInDir2, Different} | throw(Error)
compare_dirs(Dir1, Dir2) ->
@@ -1030,11 +1030,11 @@ f_p_s(P, F) ->
{error, enoent} ->
{error, enoent};
{error, {Line, _Mod, _Term}=E} ->
- error("file:path_script(~p,~p): error on line ~p: ~s~n",
+ error("file:path_script(~tp,~tp): error on line ~p: ~ts~n",
[P, F, Line, file:format_error(E)]),
ok;
{error, E} when is_atom(E) ->
- error("file:path_script(~p,~p): ~s~n",
+ error("file:path_script(~tp,~tp): ~ts~n",
[P, F, file:format_error(E)]),
ok;
Other ->
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
index cb1e12ae46..4850a59eb6 100644
--- a/lib/stdlib/src/binary.erl
+++ b/lib/stdlib/src/binary.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
@@ -18,29 +18,187 @@
%%
-module(binary).
%%
-%% The following functions implemented as BIF's
-%% binary:compile_pattern/1
-%% binary:match/{2,3}
-%% binary:matches/{2,3}
-%% binary:longest_common_prefix/1
-%% binary:longest_common_suffix/1
-%% binary:first/1
-%% binary:last/1
-%% binary:at/2
-%% binary:part/{2,3}
-%% binary:bin_to_list/{1,2,3}
-%% binary:list_to_bin/1
-%% binary:copy/{1,2}
-%% binary:referenced_byte_size/1
-%% binary:decode_unsigned/{1,2}
-%% - Not yet:
-%%
%% Implemented in this module:
-export([split/2,split/3,replace/3,replace/4]).
--opaque cp() :: tuple().
+-export_type([cp/0]).
+
+-opaque cp() :: {'am' | 'bm', binary()}.
-type part() :: {Start :: non_neg_integer(), Length :: integer()}.
+%%% BIFs.
+
+-export([at/2, bin_to_list/1, bin_to_list/2, bin_to_list/3,
+ compile_pattern/1, copy/1, copy/2, decode_unsigned/1,
+ decode_unsigned/2, encode_unsigned/1, encode_unsigned/2,
+ first/1, last/1, list_to_bin/1, longest_common_prefix/1,
+ longest_common_suffix/1, match/2, match/3, matches/2,
+ matches/3, part/2, part/3, referenced_byte_size/1]).
+
+-spec at(Subject, Pos) -> byte() when
+ Subject :: binary(),
+ Pos :: non_neg_integer().
+
+at(_, _) ->
+ erlang:nif_error(undef).
+
+-spec bin_to_list(Subject) -> [byte()] when
+ Subject :: binary().
+
+bin_to_list(_) ->
+ erlang:nif_error(undef).
+
+-spec bin_to_list(Subject, PosLen) -> [byte()] when
+ Subject :: binary(),
+ PosLen :: part().
+
+bin_to_list(_, _) ->
+ erlang:nif_error(undef).
+
+-spec bin_to_list(Subject, Pos, Len) -> [byte()] when
+ Subject :: binary(),
+ Pos :: non_neg_integer(),
+ Len :: integer().
+
+bin_to_list(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec compile_pattern(Pattern) -> cp() when
+ Pattern :: binary() | [binary()].
+
+compile_pattern(_) ->
+ erlang:nif_error(undef).
+
+-spec copy(Subject) -> binary() when
+ Subject :: binary().
+
+copy(_) ->
+ erlang:nif_error(undef).
+
+-spec copy(Subject, N) -> binary() when
+ Subject :: binary(),
+ N :: non_neg_integer().
+
+copy(_, _) ->
+ erlang:nif_error(undef).
+
+-spec decode_unsigned(Subject) -> Unsigned when
+ Subject :: binary(),
+ Unsigned :: non_neg_integer().
+
+decode_unsigned(_) ->
+ erlang:nif_error(undef).
+
+-spec decode_unsigned(Subject, Endianess) -> Unsigned when
+ Subject :: binary(),
+ Endianess :: big | little,
+ Unsigned :: non_neg_integer().
+
+decode_unsigned(_, _) ->
+ erlang:nif_error(undef).
+
+-spec encode_unsigned(Unsigned) -> binary() when
+ Unsigned :: non_neg_integer().
+
+encode_unsigned(_) ->
+ erlang:nif_error(undef).
+
+-spec encode_unsigned(Unsigned, Endianess) -> binary() when
+ Unsigned :: non_neg_integer(),
+ Endianess :: big | little.
+
+encode_unsigned(_, _) ->
+ erlang:nif_error(undef).
+
+-spec first(Subject) -> byte() when
+ Subject :: binary().
+
+first(_) ->
+ erlang:nif_error(undef).
+
+-spec last(Subject) -> byte() when
+ Subject :: binary().
+
+last(_) ->
+ erlang:nif_error(undef).
+
+-spec list_to_bin(ByteList) -> binary() when
+ ByteList :: iodata().
+
+list_to_bin(_) ->
+ erlang:nif_error(undef).
+
+-spec longest_common_prefix(Binaries) -> non_neg_integer() when
+ Binaries :: [binary()].
+
+longest_common_prefix(_) ->
+ erlang:nif_error(undef).
+
+-spec longest_common_suffix(Binaries) -> non_neg_integer() when
+ Binaries :: [binary()].
+
+longest_common_suffix(_) ->
+ erlang:nif_error(undef).
+
+-spec match(Subject, Pattern) -> Found | nomatch when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: part().
+
+match(_, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Subject, Pattern, Options) -> Found | nomatch when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: part(),
+ Options :: [Option],
+ Option :: {scope, part()}.
+
+match(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec matches(Subject, Pattern) -> Found when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: [part()].
+
+matches(_, _) ->
+ erlang:nif_error(undef).
+
+-spec matches(Subject, Pattern, Options) -> Found when
+ Subject :: binary(),
+ Pattern :: binary() | [binary()] | cp(),
+ Found :: [part()],
+ Options :: [Option],
+ Option :: {scope, part()}.
+
+matches(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec part(Subject, PosLen) -> binary() when
+ Subject :: binary(),
+ PosLen :: part().
+
+part(_, _) ->
+ erlang:nif_error(undef).
+
+-spec part(Subject, Pos, Len) -> binary() when
+ Subject :: binary(),
+ Pos :: non_neg_integer(),
+ Len :: integer().
+
+part(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec referenced_byte_size(Binary) -> non_neg_integer() when
+ Binary :: binary().
+
+referenced_byte_size(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% split
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index a920921a5e..91d317489c 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.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
@@ -116,13 +116,13 @@ 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),
check_load(code:load_abs(File2,Mod), Mod);
_OtherMod ->
- format("** Module name '~p' does not match file name '~p' **~n",
+ format("** Module name '~p' does not match file name '~tp' **~n",
[Mod,File]),
{error, badfile}
end;
@@ -203,11 +203,11 @@ make_term(Str) ->
case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
{ok, Term} -> Term;
{error, {_,_,Reason}} ->
- io:format("~s: ~s~n", [Reason, Str]),
+ io:format("~ts: ~ts~n", [Reason, Str]),
throw(error)
end;
{error, {_,_,Reason}, _} ->
- io:format("~s: ~s~n", [Reason, Str]),
+ io:format("~ts: ~ts~n", [Reason, Str]),
throw(error)
end.
@@ -475,11 +475,11 @@ f_p_e(P, F) ->
{error, enoent} = Enoent ->
Enoent;
{error, E={Line, _Mod, _Term}} ->
- error("file:path_eval(~p,~p): error on line ~p: ~s~n",
+ error("file:path_eval(~tp,~tp): error on line ~p: ~ts~n",
[P, F, Line, file:format_error(E)]),
ok;
{error, E} ->
- error("file:path_eval(~p,~p): ~s~n",
+ error("file:path_eval(~tp,~tp): ~ts~n",
[P, F, file:format_error(E)]),
ok;
Other ->
@@ -512,7 +512,7 @@ m(M) ->
print_object_file(Mod) ->
case code:is_loaded(Mod) of
{file,File} ->
- format("Object file: ~s\n", [File]);
+ format("Object file: ~ts\n", [File]);
_ ->
ignore
end.
@@ -588,7 +588,12 @@ month(12) -> "December".
flush() ->
receive
X ->
- format("Shell got ~p~n",[X]),
+ case lists:keyfind(encoding, 1, io:getopts()) of
+ {encoding,unicode} ->
+ format("Shell got ~tp~n",[X]);
+ _ ->
+ format("Shell got ~p~n",[X])
+ end,
flush()
after 0 ->
ok
@@ -680,7 +685,7 @@ portformat(Name, Id, Cmd) ->
pwd() ->
case file:get_cwd() of
{ok, Str} ->
- ok = io:format("~ts\n", [fixup_one_bin(Str)]);
+ ok = io:format("~ts\n", [Str]);
{error, _} ->
ok = io:format("Cannot determine current directory\n")
end.
@@ -707,27 +712,11 @@ ls() ->
ls(Dir) ->
case file:list_dir(Dir) of
{ok, Entries} ->
- ls_print(sort(fixup_bin(Entries)));
+ ls_print(sort(Entries));
{error,_E} ->
format("Invalid directory\n")
end.
-fixup_one_bin(X) when is_binary(X) ->
- L = binary_to_list(X),
- [ if
- El > 127 ->
- $?;
- true ->
- El
- end || El <- L];
-fixup_one_bin(X) ->
- X.
-fixup_bin([H|T]) ->
- [fixup_one_bin(H) | fixup_bin(T)];
-fixup_bin([]) ->
- [].
-
-
ls_print([]) -> ok;
ls_print(L) ->
Width = min([max(lengths(L, [])), 40]) + 5,
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index c0f9ce34b0..ecb509f4b5 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-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
@@ -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}}.
@@ -2484,7 +2504,7 @@ fopen2(Fname, Tab) ->
end,
case Do of
{repair, Mess} ->
- io:format(user, "dets: file ~p~s~n", [Fname, Mess]),
+ io:format(user, "dets: file ~tp~s~n", [Fname, Mess]),
Version = default,
case fsck(Fd, Tab, Fname, FH, default, default, Version) of
ok ->
@@ -2579,7 +2599,7 @@ fopen_existing_file(Tab, OpenArgs) ->
_ when FH#fileheader.keypos =/= Kp ->
throw({error, {keypos_mismatch, Fname}});
{compact, SourceHead} ->
- io:format(user, "dets: file ~p is now compacted ...~n", [Fname]),
+ io:format(user, "dets: file ~tp is now compacted ...~n", [Fname]),
{ok, NewSourceHead} = open_final(SourceHead, Fname, read, false,
?DEFAULT_CACHE, Tab, Debug),
case catch compact(NewSourceHead) of
@@ -2589,14 +2609,14 @@ fopen_existing_file(Tab, OpenArgs) ->
_Err ->
_ = file:close(Fd),
dets_utils:stop_disk_map(),
- io:format(user, "dets: compaction of file ~p failed, "
+ io:format(user, "dets: compaction of file ~tp failed, "
"now repairing ...~n", [Fname]),
{ok, Fd2, _FH} = read_file_header(Fname, Acc, Ram),
do_repair(Fd2, Tab, Fname, FH, MinSlots, MaxSlots,
Version, OpenArgs)
end;
{repair, Mess} ->
- io:format(user, "dets: file ~p~s~n", [Fname, Mess]),
+ io:format(user, "dets: file ~tp~s~n", [Fname, Mess]),
do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots,
Version, OpenArgs);
_ when FH#fileheader.version =/= Version, Version =/= default ->
diff --git a/lib/stdlib/src/dets_utils.erl b/lib/stdlib/src/dets_utils.erl
index 5db2ad3049..7bbb34dd15 100644
--- a/lib/stdlib/src/dets_utils.erl
+++ b/lib/stdlib/src/dets_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -395,7 +395,7 @@ corrupt_reason(Head, Reason0) ->
corrupt(Head, Error) ->
case get(verbose) of
yes ->
- error_logger:format("** dets: Corrupt table ~p: ~p\n",
+ error_logger:format("** dets: Corrupt table ~p: ~tp\n",
[Head#head.name, Error]);
_ -> ok
end,
diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl
index 3e962a1c8b..24d6e06ec8 100644
--- a/lib/stdlib/src/dets_v8.erl
+++ b/lib/stdlib/src/dets_v8.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -1492,7 +1492,7 @@ scan_next_allocated(Bin, From0, _To, <<From:32, To:32, L/binary>>, Ts, R) ->
%% Read term from file at position Pos
prterm(Head, Pos, ReadAhead) ->
Res = dets_utils:pread(Head, Pos, ?OHDSZ, ReadAhead),
- ?DEBUGF("file:pread(~p, ~p, ?) -> ~p~n", [Head#head.filename, Pos, Res]),
+ ?DEBUGF("file:pread(~tp, ~p, ?) -> ~p~n", [Head#head.filename, Pos, Res]),
{ok, <<Next:32, Sz:32, _Status:32, Bin0/binary>>} = Res,
?DEBUGF("{Next, Sz} = ~p~n", [{Next, Sz}]),
Bin = case byte_size(Bin0) of
diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl
index f577b4410f..308f81c23b 100644
--- a/lib/stdlib/src/dets_v9.erl
+++ b/lib/stdlib/src/dets_v9.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -2662,7 +2662,7 @@ v_segment(H, SegNo, SegPos, SegSlot) ->
{'EXIT', Reason} ->
dets_utils:vformat("** dets: Corrupt or truncated dets file~n",
[]),
- io:format("~nERROR ~p~n", [Reason]);
+ io:format("~nERROR ~tp~n", [Reason]);
[] -> %% don't print empty buckets
true;
{Size, CollP, Objects} ->
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 2c8d84a9e1..0a1caa7178 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.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
@@ -23,13 +23,19 @@
-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_from_binary/1, read_encoding_from_binary/2,
+ set_encoding/1, read_encoding/1, read_encoding/2]).
-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 +219,201 @@ 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_binary(Binary) -> source_encoding() | none when
+ Binary :: binary().
+
+-define(ENC_CHUNK, 32).
+-define(N_ENC_CHUNK, 16). % a total of 512 bytes
+
+read_encoding_from_binary(Binary) ->
+ read_encoding_from_binary(Binary, []).
+
+-spec read_encoding_from_binary(Binary, Options) ->
+ source_encoding() | none when
+ Binary :: binary(),
+ Options :: [Option],
+ Option :: {in_comment_only, boolean()}.
+
+read_encoding_from_binary(Binary, Options) ->
+ InComment = proplists:get_value(in_comment_only, Options, true),
+ try
+ com_nl(Binary, fake_reader(0), 0, InComment)
+ catch
+ throw:no ->
+ none
+ end.
+
+fake_reader(N) ->
+ fun() when N =:= ?N_ENC_CHUNK ->
+ throw(no);
+ () ->
+ {<<>>, fake_reader(N+1)}
+ end.
+
+-spec read_encoding_from_file(File, InComment) -> source_encoding() | none when
+ File :: io:device(),
+ InComment :: boolean().
+
+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 +467,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 +589,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),
@@ -409,7 +614,7 @@ enter_file_reply(From, Name, Location, AtLocation) ->
%% Flatten filename to a string. Must be a valid filename.
-file_name([C | T]) when is_integer(C), C > 0, C =< 255 ->
+file_name([C | T]) when is_integer(C), C > 0 ->
[C | file_name(T)];
file_name([H|T]) ->
file_name(H) ++ file_name(T);
@@ -1048,8 +1253,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 +1297,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) ->
@@ -1103,14 +1307,14 @@ token_src({var, _, X}) ->
token_src({char,_,C}) ->
io_lib:write_char(C);
token_src({string, _, X}) ->
- lists:flatten(io_lib:format("~p", [X]));
+ io_lib:write_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_compile.erl b/lib/stdlib/src/erl_compile.erl
index 81bec21a3f..8c3d59467b 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -68,7 +68,7 @@ compile(List) ->
{'EXIT', Pid, {compiler_result, Result}} ->
Result;
{'EXIT', Pid, Reason} ->
- io:format("Runtime error: ~p~n", [Reason]),
+ io:format("Runtime error: ~tp~n", [Reason]),
error
end.
@@ -170,12 +170,12 @@ compile3([], _Cwd, _Options) -> ok.
%% Invokes the appropriate compiler, depending on the file extension.
compile_file("", Input, _Output, _Options) ->
- io:format("File has no extension: ~s~n", [Input]),
+ io:format("File has no extension: ~ts~n", [Input]),
error;
compile_file(Ext, Input, Output, Options) ->
case compiler(Ext) of
no ->
- io:format("Unknown extension: '~s'\n", [Ext]),
+ io:format("Unknown extension: '~ts'\n", [Ext]),
error;
{M, F} ->
case catch M:F(Input, Output, Options) of
@@ -215,10 +215,10 @@ make_term(Str) ->
case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
{ok, Term} -> Term;
{error, {_,_,Reason}} ->
- io:format("~s: ~s~n", [Reason, Str]),
+ io:format("~ts: ~ts~n", [Reason, Str]),
throw(error)
end;
{error, {_,_,Reason}, _} ->
- io:format("~s: ~s~n", [Reason, Str]),
+ io:format("~ts: ~ts~n", [Reason, Str]),
throw(error)
end.
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 95ba6b1096..0b57af1b6d 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.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
@@ -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).
@@ -354,7 +346,12 @@ expr({call,_,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs) ->
expr({call,_,Func0,As0}, Bs0, Lf, Ef, RBs) -> % function or {Mod,Fun}
{value,Func,Bs1} = expr(Func0, Bs0, Lf, Ef, none),
{As,Bs2} = expr_list(As0, Bs1, Lf, Ef),
- do_apply(Func, As, Bs2, Ef, RBs);
+ case Func of
+ {M,F} when is_atom(M), is_atom(F) ->
+ erlang:raise(error, {badfun,Func}, stacktrace());
+ _ ->
+ do_apply(Func, As, Bs2, Ef, RBs)
+ end;
expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs) ->
Ref = make_ref(),
case catch {Ref,expr(Expr, Bs0, Lf, Ef, none)} of
@@ -1210,41 +1207,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..378e629ac9 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2012. 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
@@ -254,6 +254,9 @@ bif(binary_part, 2) -> true;
bif(binary_part, 3) -> true;
bif(binary_to_atom, 2) -> true;
bif(binary_to_existing_atom, 2) -> true;
+bif(binary_to_integer, 1) -> true;
+bif(binary_to_integer, 2) -> true;
+bif(binary_to_float, 1) -> true;
bif(binary_to_list, 1) -> true;
bif(binary_to_list, 3) -> true;
bif(binary_to_term, 1) -> true;
@@ -278,6 +281,9 @@ 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(float_to_binary, 1) -> true;
+bif(float_to_binary, 2) -> true;
bif(garbage_collect, 0) -> true;
bif(garbage_collect, 1) -> true;
bif(get, 0) -> true;
@@ -289,6 +295,8 @@ bif(halt, 0) -> true;
bif(halt, 1) -> true;
bif(halt, 2) -> true;
bif(hd, 1) -> true;
+bif(integer_to_binary, 1) -> true;
+bif(integer_to_binary, 2) -> true;
bif(integer_to_list, 1) -> true;
bif(integer_to_list, 2) -> true;
bif(iolist_size, 1) -> true;
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 648ff349a4..68a8534f15 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2,7 +2,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
@@ -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
@@ -140,14 +136,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
format_error(undefined_module) ->
"no module definition";
-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]);
@@ -160,18 +152,12 @@ format_error({attribute,A}) ->
io_lib:format("attribute '~w' after function definitions", [A]);
format_error({missing_qlc_hrl,A}) ->
io_lib:format("qlc:q/~w called, but \"qlc.hrl\" not included", [A]);
-format_error({redefine_import,{bif,{F,A},M}}) ->
- io_lib:format("function ~w/~w already auto-imported from ~w", [F,A,M]);
format_error({redefine_import,{{F,A},M}}) ->
io_lib:format("function ~w/~w already imported from ~w", [F,A,M]);
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}}) ->
@@ -234,8 +220,6 @@ format_error({removed, MFA, String}) when is_list(String) ->
io_lib:format("~s: ~s", [format_mfa(MFA), String]);
format_error({obsolete_guard, {F, A}}) ->
io_lib:format("~p/~p obsolete", [F, A]);
-format_error({reserved_for_future,K}) ->
- io_lib:format("atom ~w: future reserved keyword - rename or quote", [K]);
format_error({too_many_arguments,Arity}) ->
io_lib:format("too many arguments (~w) - "
"maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
@@ -248,13 +232,6 @@ 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 "
- "like this: erlang:~w/~w",
- [F,A,F,A]);
%% --- records ---
format_error({undefined_record,T}) ->
io_lib:format("record ~w undefined", [T]);
@@ -292,8 +269,6 @@ format_error({variable_in_record_def,V}) ->
%% --- binaries ---
format_error({undefined_bittype,Type}) ->
io_lib:format("bit type ~w undefined", [Type]);
-format_error({bittype_mismatch,T1,T2,What}) ->
- io_lib:format("bit type mismatch (~s) between ~p and ~p", [What,T1,T2]);
format_error(bittype_unit) ->
"a bit unit size must not be specified unless a size is specified too";
format_error(illegal_bitsize) ->
@@ -365,6 +340,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);
@@ -533,7 +514,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()),
@@ -619,8 +599,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
@@ -675,51 +653,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'
@@ -728,15 +670,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) ->
@@ -851,7 +784,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.
@@ -1000,9 +934,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.
@@ -1215,73 +1149,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) ->
@@ -1355,11 +1262,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().
@@ -1382,15 +1287,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)),
@@ -1456,13 +1362,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}} ->
@@ -1844,13 +1743,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,
@@ -1895,11 +1787,9 @@ gexpr({call,Line,{atom,_La,F},As}, Vt, St0) ->
%% BifClash - Function called in guard
case erl_internal:guard_bif(F, A) andalso no_guard_bif_clash(St1,{F,A}) of
true ->
- %% Also check that it is auto-imported.
- case erl_internal:bif(F, A) of
- true -> {Asvt,St1};
- false -> {Asvt,add_error(Line, {explicit_export,F,A}, St1)}
- end;
+ %% Assert that it is auto-imported.
+ true = erl_internal:bif(F, A),
+ {Asvt,St1};
false ->
case is_local_function(St1#lint.locals,{F,A}) orelse
is_imported_function(St1#lint.imports,{F,A}) of
@@ -1916,9 +1806,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, St0) ->
- St = add_warning(L, deprecated_tuple_fun, St0),
- 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
@@ -1992,8 +1879,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) ->
@@ -2082,13 +1967,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,
@@ -2159,20 +2037,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),
@@ -2228,13 +2100,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
@@ -2277,9 +2142,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}
@@ -2557,15 +2420,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
@@ -2575,20 +2447,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) ->
@@ -2898,7 +2779,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
@@ -2917,6 +2798,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}.
@@ -3520,7 +3419,7 @@ check_format_3(Fmt, As) ->
_Len -> {warn,1,"wrong number of arguments in format call",[]}
end;
{error,S} ->
- {warn,1,"format string invalid (~s)",[S]}
+ {warn,1,"format string invalid (~ts)",[S]}
end.
args_list({cons,_L,_H,T}) -> args_list(T);
@@ -3578,8 +3477,18 @@ 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, [$l, $p | Fmt], Need) ->
+ extract_sequence(5, [$p|Fmt], Need);
+extract_sequence(4, [$l, $P | Fmt], Need) ->
+ extract_sequence(5, [$P|Fmt], Need);
+extract_sequence(4, [$l, C | _Fmt], _Need) ->
+ {error,"invalid control ~l" ++ [C]};
extract_sequence(4, Fmt, Need) ->
extract_sequence(5, Fmt, Need);
extract_sequence(5, [C|Fmt], Need0) ->
@@ -3614,49 +3523,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..9ff25fcbc5 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-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,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]).
@@ -526,7 +516,7 @@ Erlang code.
-type abstract_form() :: term().
-type error_description() :: term().
-type error_info() :: {erl_scan:line(), module(), error_description()}.
--type token() :: {Tag :: atom(), Line :: erl_scan:line()}.
+-type token() :: erl_scan:token().
%% mkop(Op, Arg) -> {op,Line,Op,Arg}.
%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}.
@@ -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..7c7566e4ec 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-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,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,13 @@
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) ->
+ State = state(Options),
+ frmt(lform(Thing, options(Options), State), State).
-spec(attribute(Attribute) -> io_lib:chars() when
Attribute :: erl_parse:abstract_form()).
@@ -61,12 +70,13 @@ 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) ->
+ State = state(Options),
+ frmt(lattribute(Thing, options(Options), State), State).
-spec(function(Function) -> io_lib:chars() when
Function :: erl_parse:abstract_form()).
@@ -74,18 +84,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 +103,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 +116,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_string_as_latin1/1,
+ char_fun = fun io_lib:write_char_as_latin1/1}.
+
+unicode_state() ->
+ #pp{string_fun = fun io_lib:write_string/1,
+ char_fun = fun io_lib:write_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, State) ->
+ lattribute({attribute,Line,Name,Arg}, Opts, State);
+lform({function,Line,Name,Arity,Clauses}, Opts, _State) ->
+ lfunction({function,Line,Name,Arity,Clauses}, Opts);
+lform({rule,Line,Name,Arity,Clauses}, Opts, _State) ->
+ 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, _State) ->
leaf(format("~p\n", [{error,E}]));
-lform({warning,W}, _Hook) ->
+lform({warning,W}, _Opts, _State) ->
leaf(format("~p\n", [{warning,W}]));
-lform({eof,_Line}, _Hook) ->
+lform({eof,_Line}, _Opts, _State) ->
$\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, _State) ->
+ [typeattr(type, Type, Opts),leaf(".\n")];
+lattribute({attribute,_Line,opaque,Type}, Opts, _State) ->
+ [typeattr(opaque, Type, Opts),leaf(".\n")];
+lattribute({attribute,_Line,spec,Arg}, _Opts, _State) ->
[specattr(Arg),leaf(".\n")];
-lattribute({attribute,_Line,Name,Arg}, Hook) ->
- [lattribute(Name, Arg, Hook),leaf(".\n")].
+lattribute({attribute,_Line,Name,Arg}, Opts, State) ->
+ [lattribute(Name, Arg, Opts, State),leaf(".\n")].
-lattribute(module, {M,Vs}, _Hook) ->
+lattribute(module, {M,Vs}, _Opts, _State) ->
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, _State) ->
attr("module", [{var,0,pname(M)}]);
-lattribute(export, Falist, _Hook) ->
+lattribute(export, Falist, _Opts, _State) ->
call({var,0,"-export"}, [falist(Falist)], 0, none);
-lattribute(import, Name, _Hook) when is_list(Name) ->
+lattribute(import, Name, _Opts, _State) when is_list(Name) ->
attr("import", [{var,0,pname(Name)}]);
-lattribute(import, {From,Falist}, _Hook) ->
+lattribute(import, {From,Falist}, _Opts, _State) ->
attr("import",[{var,0,pname(From)},falist(Falist)]);
-lattribute(file, {Name,Line}, _Hook) ->
- attr("file", [{var,0,format("~p", [Name])},{integer,0,Line}]);
-lattribute(record, {Name,Is}, Hook) ->
+lattribute(file, {Name,Line}, _Opts, State) ->
+ attr("file", [{var,0,(State#pp.string_fun)(Name)},{integer,0,Line}]);
+lattribute(record, {Name,Is}, Opts, _State) ->
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}, _State) ->
+ 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 +333,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 +373,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({var,_,V}, _, _) -> leaf(format("~ts", [V]));
+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 +604,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 +643,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,$)];
@@ -776,18 +801,18 @@ maybe_paren(_P, _Prec, Expr) ->
Expr.
leaf(S) ->
- {leaf,iolist_size(S),S}.
+ {leaf,chars_size(S),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 +828,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 +838,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 +874,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 +890,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 +935,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]),
@@ -982,7 +1011,7 @@ incr(I, Incr) ->
I+Incr.
indentation(E, I) when I < 0 ->
- iolist_size(E);
+ chars_size(E);
indentation(E, I0) ->
I = io_lib_format:indentation(E, I0),
case has_nl(E) of
@@ -999,30 +1028,53 @@ 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
%%
+chars_size([C | Es]) when is_integer(C) ->
+ 1 + chars_size(Es);
+chars_size([E | Es]) ->
+ chars_size(E) + chars_size(Es);
+chars_size([]) ->
+ 0;
+chars_size(B) when is_binary(B) ->
+ byte_size(B).
+
-define(N_SPACES, 30).
spacetab() ->
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 8e59e01f48..3651f608bc 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-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
@@ -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,16 @@
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,
+ token/0,
+ tokens_result/0]).
%%%
%%% Defines and type definitions
@@ -104,7 +114,7 @@
format_error({string,Quote,Head}) ->
lists:flatten(["unterminated " ++ string_thing(Quote) ++
" starting with " ++
- io_lib:write_unicode_string(Head, Quote)]);
+ io_lib:write_string(Head, Quote)]);
format_error({illegal,Type}) ->
lists:flatten(io_lib:fwrite("illegal ~w", [Type]));
format_error(char) -> "unterminated character";
@@ -183,6 +193,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 +216,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 +253,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 +337,12 @@ 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).
+-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)).
-%% 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(NO_UNICODE, 0).
--define(UNI255(C), (C) =< 16#ff).
+-define(UNI255(C), C >= 0, C =< 16#ff).
options(Opts0) when is_list(Opts0) ->
Opts = lists:foldr(fun expand_opt/2, [], Opts0),
@@ -504,18 +516,18 @@ scan1("."=Cs, _St, Line, Col, Toks) ->
scan1([$.=C|Cs], St, Line, Col, Toks) ->
scan_dot(Cs, St, Line, Col, Toks, [C]);
scan1([$"|Cs], St, Line, Col, Toks) -> %" Emacs
- State0 = {[],[],Line,Col,?NO_UNICODE},
+ State0 = {[],[],Line,Col},
scan_string(Cs, St, Line, incr_column(Col, 1), Toks, State0);
scan1([$'|Cs], St, Line, Col, Toks) -> %' Emacs
- State0 = {[],[],Line,Col,?NO_UNICODE},
+ State0 = {[],[],Line,Col},
scan_qatom(Cs, St, Line, incr_column(Col, 1), Toks, State0);
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 +640,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 ?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,23 +694,25 @@ 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};
scan_name(Cs, Ncs) ->
{lists:reverse(Ncs),Cs}.
+-define(STR(St, S), if St#erl_scan.text -> S; true -> [] end).
+
scan_dot([$%|_]=Cs, St, Line, Col, Toks, Ncs) ->
Attrs = attributes(Line, Col, St, Ncs),
{ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)};
scan_dot([$\n=C|Cs], St, Line, Col, Toks, Ncs) ->
- Attrs = attributes(Line, Col, St, Ncs++[C]),
+ Attrs = attributes(Line, Col, St, ?STR(St, Ncs++[C])),
{ok,[{dot,Attrs}|Toks],Cs,Line+1,new_column(Col, 1)};
scan_dot([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) ->
- Attrs = attributes(Line, Col, St, Ncs++[C]),
+ Attrs = attributes(Line, Col, St, ?STR(St, Ncs++[C])),
{ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 2)};
scan_dot(eof=Cs, St, Line, Col, Toks, Ncs) ->
Attrs = attributes(Line, Col, St, Ncs),
@@ -834,102 +845,53 @@ 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(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
- scan1(Ncs, St, Line, Ncol, Ntoks);
{Val,Str,Ncs,Ncol} ->
- Attrs = attributes(Line, Col, St, "$\\"++Str),
+ Attrs = attributes(Line, Col, St, ?STR(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]),
+ Attrs = attributes(Line, Col, St, ?STR(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
- 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 ?UNICODE(C) ->
+ Attrs = attributes(Line, Col, St, ?STR(St, [$$,C])),
+ scan1(Cs, St, Line, incr_column(Col, 2), [{char,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).
-scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) ->
- case scan_string0(Cs, St, Line, Col, $\", Str, Wcs, Uni0) of
- {more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} ->
- State = {Nwcs,Nstr,Line0,Col0,Uni},
+scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) ->
+ case scan_string0(Cs, St, Line, Col, $\", Str, Wcs) of %"
+ {more,Ncs,Nline,Ncol,Nstr,Nwcs} ->
+ State = {Nwcs,Nstr,Line0,Col0},
{more,{Ncs,Ncol,Toks,Nline,State,fun scan_string/6}};
{char_error,Ncs,Error,Nline,Ncol,EndCol} ->
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} ->
Attrs = attributes(Line0, Col0, St, Nstr),
- scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks]);
- {Ncs,Nline,Ncol,Nstr,_Nwcs,_Uni} ->
- Ntoks = unicode_string_to_list(Line0, Col0, St, Nstr, Toks),
- scan1(Ncs, St, Nline, Ncol, Ntoks)
+ scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks])
end.
-%% UNI
-unicode_string_to_list(Line, Col, St, [$"=C|Nstr], Toks) -> %" Emacs
- Paren = {'[',attributes(Line, Col, St, [C])},
- u2l(Nstr, Line, incr_column(Col, 1), St, [Paren|Toks]).
-
-u2l([$"]=Cs, Line, Col, St, Toks) -> %" Emacs
- [{']',attributes(Line, Col, St, Cs)}|Toks];
-u2l([$\n=C|Cs], Line, Col, St, Toks) ->
- Ntoks = unicode_nl_tokens(Line, Col, [C], C, St, Toks, Cs),
- u2l(Cs, Line+1, new_column(Col, 1), St, Ntoks);
-u2l([$\\|Cs], Line, Col, St, Toks) ->
- case scan_escape(Cs, Col) of
- {nl,Val,ValStr,Ncs,Ncol} ->
- Nstr = [$\\|ValStr],
- Ntoks = unicode_nl_tokens(Line, Col, Nstr, Val, St, Toks, Ncs),
- u2l(Ncs, Line+1, Ncol, St, Ntoks);
- {unicode,Val,ValStr,Ncs,Ncol} ->
- Nstr = [$\\|ValStr],
- Ntoks = unicode_tokens(Line, Col, Nstr, Val, St, Toks, Ncs),
- u2l(Ncs, Line, incr_column(Ncol, 1), St, Ntoks);
- {Val,ValStr,Ncs,Ncol} ->
- Nstr = [$\\|ValStr],
- Ntoks = unicode_tokens(Line, Col, Nstr, Val, St, Toks, Ncs),
- u2l(Ncs, Line, incr_column(Ncol, 1), St, Ntoks)
- end;
-u2l([C|Cs], Line, Col, St, Toks) ->
- Ntoks = unicode_tokens(Line, Col, [C], C, St, Toks, Cs),
- u2l(Cs, Line, incr_column(Col, 1), St, Ntoks).
-
-unicode_nl_tokens(Line, Col, Str, Val, St, Toks, Cs) ->
- Ccol = new_column(Col, 1),
- unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Line+1, Ccol).
-
-unicode_tokens(Line, Col, Str, Val, St, Toks, Cs) ->
- Ccol = incr_column(Col, length(Str)),
- unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Line, Ccol).
-
-unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Cline, Ccol) ->
- Attrs = attributes(Line, Col, St, Str),
- Tag = if ?UNI255(Val) -> char; true -> integer end,
- Token = {Tag,Attrs,Val},
- [{',',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
- {more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} ->
- State = {Nwcs,Nstr,Line0,Col0,Uni},
+scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) ->
+ case scan_string0(Cs, St, Line, Col, $\', Str, Wcs) of %'
+ {more,Ncs,Nline,Ncol,Nstr,Nwcs} ->
+ State = {Nwcs,Nstr,Line0,Col0},
{more,{Ncs,Ncol,Toks,Nline,State,fun scan_qatom/6}};
{char_error,Ncs,Error,Nline,Ncol,EndCol} ->
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} ->
case catch list_to_atom(Nwcs) of
A when is_atom(A) ->
Attrs = attributes(Line0, Col0, St, Nstr),
@@ -939,91 +901,75 @@ 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, [], Wcs) ->
+ scan_string_no_col(Cs, Line, Col, Q, Wcs);
+scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, Str, Wcs) ->
+ scan_string1(Cs, Line, Col, Q, Str, Wcs);
+scan_string0(Cs, St, Line, Col, Q, [], Wcs) ->
+ scan_string_col(Cs, St, Line, Col, Q, Wcs);
+scan_string0(Cs, _St, Line, Col, Q, Str, Wcs) ->
+ scan_string1(Cs, Line, Col, Q, Str, Wcs).
%% Optimization. Col =:= no_col.
-scan_string_no_col([Q|Cs], Line, Col, Q, Wcs, Uni) ->
- {Cs,Line,Col,_DontCare=[],lists:reverse(Wcs),Uni};
-scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs, Uni) ->
- scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs], Uni);
-scan_string_no_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
- ?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([Q|Cs], Line, Col, Q, Wcs) ->
+ {Cs,Line,Col,_DontCare=[],lists:reverse(Wcs)};
+scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs) ->
+ scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs]);
+scan_string_no_col([C|Cs], Line, Col, Q, Wcs) when C =/= $\\, ?UNICODE(C) ->
+ scan_string_no_col(Cs, Line, Col, Q, [C|Wcs]);
+scan_string_no_col(Cs, Line, Col, Q, Wcs) ->
+ scan_string1(Cs, Line, Col, Q, Wcs, Wcs).
%% Optimization. Col =/= no_col.
-scan_string_col([Q|Cs], Line, Col, Q, Wcs0, Uni) ->
+scan_string_col([Q|Cs], St, Line, Col, Q, Wcs0) ->
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).
-
-%% UNI_STR is to be replaced by STR when the Unicode-string-to-list
-%% workaround is eventually removed.
--define(UNI_STR(Col, S), S).
+ Str = ?STR(St, [Q|Wcs++[Q]]),
+ {Cs,Line,Col+1,Str,Wcs};
+scan_string_col([$\n=C|Cs], St, Line, _xCol, Q, Wcs) ->
+ scan_string_col(Cs, St, Line+1, 1, Q, [C|Wcs]);
+scan_string_col([C|Cs], St, Line, Col, Q, Wcs) when C =/= $\\, ?UNICODE(C) ->
+ scan_string_col(Cs, St, Line, Col+1, Q, [C|Wcs]);
+scan_string_col(Cs, _St, Line, Col, Q, Wcs) ->
+ scan_string1(Cs, Line, Col, Q, Wcs, Wcs).
%% Note: in those cases when a 'char_error' tuple is returned below it
%% is tempting to skip over characters up to the first Q character,
%% 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, Str0, Wcs0) ->
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) ->
+ Str = [Q|lists:reverse(Str0, [Q])],
+ {Cs,Line,incr_column(Col, 1),Str,Wcs};
+scan_string1([$\n=C|Cs], Line, Col, Q, Str, Wcs) ->
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, [C|Str], [C|Wcs]);
+scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs) ->
case scan_escape(Cs, Col) of
more ->
- {more,Cs0,Line,Col,Str,Wcs,Uni};
+ {more,Cs0,Line,Col,Str,Wcs};
{error,Ncs,Error,Ncol} ->
{char_error,Ncs,Error,Line,Col,incr_column(Ncol, 1)};
{eof,Ncol} ->
{error,Line,incr_column(Ncol, 1),lists:reverse(Wcs),eof};
{nl,Val,ValStr,Ncs,Ncol} ->
- Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])),
+ Nstr = 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
- {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+1, Ncol, Q, Nstr, Nwcs);
{Val,ValStr,Ncs,Ncol} ->
- Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])),
+ Nstr = 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, Nstr, Nwcs)
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, Str, Wcs) when ?UNICODE(C) ->
+ scan_string1(Cs, Line, Col, Q, [C|Str], [C|Wcs]);
+scan_string1([C|Cs], Line, Col, Q, Str, Wcs) when ?UNICODE(C) ->
+ scan_string1(Cs, Line, Col+1, Q, [C|Str], [C|Wcs]);
+scan_string1([C|Cs], Line, Col, _Q, _Str, _Wcs) when ?CHAR(C) ->
{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) ->
- {more,Cs,Line,Col,Str,Wcs,Uni};
-scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) ->
+scan_string1([]=Cs, Line, Col, _Q, Str, Wcs) ->
+ {more,Cs,Line,Col,Str,Wcs};
+scan_string1(eof, Line, Col, _Q, _Str, Wcs) ->
{error,Line,Col,lists:reverse(Wcs),eof}.
-define(OCT(C), C >= $0, C =< $7).
@@ -1034,16 +980,16 @@ scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) ->
%% \<1-3> octal digits
scan_escape([O1,O2,O3|Cs], Col) when ?OCT(O1), ?OCT(O2), ?OCT(O3) ->
Val = (O1*8 + O2)*8 + O3 - 73*$0,
- {Val,?UNI_STR(Col, [O1,O2,O3]),Cs,incr_column(Col, 3)};
+ {Val,[O1,O2,O3],Cs,incr_column(Col, 3)};
scan_escape([O1,O2], _Col) when ?OCT(O1), ?OCT(O2) ->
more;
scan_escape([O1,O2|Cs], Col) when ?OCT(O1), ?OCT(O2) ->
Val = (O1*8 + O2) - 9*$0,
- {Val,?UNI_STR(Col, [O1,O2]),Cs,incr_column(Col, 2)};
+ {Val,[O1,O2],Cs,incr_column(Col, 2)};
scan_escape([O1], _Col) when ?OCT(O1) ->
more;
scan_escape([O1|Cs], Col) when ?OCT(O1) ->
- {O1 - $0,?UNI_STR(Col, [O1]),Cs,incr_column(Col, 1)};
+ {O1 - $0,[O1],Cs,incr_column(Col, 1)};
%% \x{<hex digits>}
scan_escape([$x,${|Cs], Col) ->
scan_hex(Cs, incr_column(Col, 2), []);
@@ -1054,28 +1000,28 @@ scan_escape([$x|eof], Col) ->
%% \x<2> hexadecimal digits
scan_escape([$x,H1,H2|Cs], Col) when ?HEX(H1), ?HEX(H2) ->
Val = erlang:list_to_integer([H1,H2], 16),
- {Val,?UNI_STR(Col, [$x,H1,H2]),Cs,incr_column(Col, 3)};
+ {Val,[$x,H1,H2],Cs,incr_column(Col, 3)};
scan_escape([$x,H1], _Col) when ?HEX(H1) ->
more;
scan_escape([$x|Cs], Col) ->
{error,Cs,{illegal,character},incr_column(Col, 1)};
%% \^X -> CTL-X
scan_escape([$^=C0,$\n=C|Cs], Col) ->
- {nl,C,?UNI_STR(Col, [C0,C]),Cs,new_column(Col, 1)};
+ {nl,C,[C0,C],Cs,new_column(Col, 1)};
scan_escape([$^=C0,C|Cs], Col) when ?CHAR(C) ->
Val = C band 31,
- {Val,?UNI_STR(Col, [C0,C]),Cs,incr_column(Col, 2)};
+ {Val,[C0,C],Cs,incr_column(Col, 2)};
scan_escape([$^], _Col) ->
more;
scan_escape([$^|eof], Col) ->
{eof,incr_column(Col, 1)};
scan_escape([$\n=C|Cs], Col) ->
- {nl,C,?UNI_STR(Col, [C]),Cs,new_column(Col, 1)};
-scan_escape([C0|Cs], Col) when ?CHAR(C0), ?UNI255(C0) ->
+ {nl,C,[C],Cs,new_column(Col, 1)};
+scan_escape([C0|Cs], Col) when ?UNICODE(C0) ->
C = escape_char(C0),
- {C,?UNI_STR(Col, [C0]),Cs,incr_column(Col, 1)};
-scan_escape([C|Cs], Col) when ?CHAR(C) -> % UNI
- {unicode,C,?UNI_STR(Col, [C]),Cs,incr_column(Col, 1)};
+ {C,[C0],Cs,incr_column(Col, 1)};
+scan_escape([C|Cs], Col) when ?CHAR(C) ->
+ {error,Cs,{illegal,character},incr_column(Col, 1)};
scan_escape([], _Col) ->
more;
scan_escape(eof, Col) ->
@@ -1091,10 +1037,8 @@ scan_hex(Cs, Col, Wcs) ->
scan_esc_end([$}|Cs], Col, Wcs0, B, Str0) ->
Wcs = lists:reverse(Wcs0),
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 ->
- {unicode,Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col,1)};
+ Val when ?UNICODE(Val) ->
+ {Val,Str0++Wcs++[$}],Cs,incr_column(Col, 1)};
_ ->
{error,Cs,{illegal,character},incr_column(Col, 1)}
end;
@@ -1126,7 +1070,7 @@ scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0) ->
Ncs = lists:reverse(Ncs0),
case catch list_to_integer(Ncs) of
B when B >= 2, B =< 1+$Z-$A+10 ->
- Bcs = Ncs++[$#],
+ Bcs = ?STR(St, Ncs++[$#]),
scan_based_int(Cs, St, Line, Col, Toks, {B,[],Bcs});
B ->
Len = length(Ncs),
@@ -1159,7 +1103,7 @@ scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->
Ncs = lists:reverse(Ncs0),
case catch erlang:list_to_integer(Ncs, B) of
N when is_integer(N) ->
- tok3(Cs, St, Line, Col, Toks, integer, Bcs++Ncs, N);
+ tok3(Cs, St, Line, Col, Toks, integer, ?STR(St, Bcs++Ncs), N);
_ ->
Len = length(Bcs)+length(Ncs),
Ncol = incr_column(Col, Len),
@@ -1200,14 +1144,26 @@ float_end(Cs, St, Line, Col, Toks, Ncs0) ->
end.
skip_comment([C|Cs], St, Line, Col, Toks, N) when C =/= $\n, ?CHAR(C) ->
- skip_comment(Cs, St, Line, Col, Toks, N+1);
+ case ?UNICODE(C) of
+ true ->
+ skip_comment(Cs, St, Line, Col, Toks, N+1);
+ 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) ->
{more,{Cs,Col,Toks,Line,N,fun skip_comment/6}};
skip_comment(Cs, St, Line, Col, Toks, N) ->
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]);
+ case ?UNICODE(C) of
+ true ->
+ scan_comment(Cs, St, Line, Col, Toks, [C|Ncs]);
+ 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) ->
{more,{Cs,Col,Toks,Line,Ncs,fun scan_comment/6}};
scan_comment(Cs, St, Line, Col, Toks, Ncs0) ->
@@ -1345,7 +1301,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/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 306834e845..4b654833ed 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -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
@@ -154,7 +154,7 @@ table(Name, Opts) ->
t(Name) ->
case table(Name) of
{ok, List} ->
- lists:foreach(fun(N) -> ok = io:format("~s\n", [N]) end, List);
+ lists:foreach(fun(N) -> ok = io:format("~ts\n", [N]) end, List);
Error ->
Error
end.
@@ -216,11 +216,11 @@ format_error(bad_header) -> "Bad directory header";
format_error(eof) -> "Unexpected end of file";
format_error(symbolic_link_too_long) -> "Symbolic link too long";
format_error({Name,Reason}) ->
- lists:flatten(io_lib:format("~s: ~s", [Name,format_error(Reason)]));
+ lists:flatten(io_lib:format("~ts: ~ts", [Name,format_error(Reason)]));
format_error(Atom) when is_atom(Atom) ->
file:format_error(Atom);
format_error(Term) ->
- lists:flatten(io_lib:format("~p", [Term])).
+ lists:flatten(io_lib:format("~tp", [Term])).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -325,13 +325,13 @@ add1(TarFile, Name, NameInArchive, Opts) ->
end.
add1(Tar, Name, Header, Bin, Options) ->
- add_verbose(Options, "a ~s~n", [Name]),
+ add_verbose(Options, "a ~ts~n", [Name]),
file:write(Tar, [Header, Bin, padding(byte_size(Bin), ?record_size)]).
add_directory(TarFile, DirName, NameInArchive, Info, Options) ->
case file:list_dir(DirName) of
{ok, []} ->
- add_verbose(Options, "a ~s~n", [DirName]),
+ add_verbose(Options, "a ~ts~n", [DirName]),
Header = create_header(NameInArchive, Info),
file:write(TarFile, Header);
{ok, Files} ->
@@ -731,7 +731,7 @@ write_extracted_element(Header, Bin, Opts) ->
symlink ->
create_symlink(Name, Header, Opts);
Other -> % Ignore.
- read_verbose(Opts, "x ~s - unsupported type ~p~n",
+ read_verbose(Opts, "x ~ts - unsupported type ~p~n",
[Name, Other]),
not_written
end,
@@ -757,7 +757,7 @@ create_symlink(Name, #tar_header{linkname=Linkname}=Header, Opts) ->
create_symlink(Name, Header, Opts);
{error,eexist} -> not_written;
{error,enotsup} ->
- read_verbose(Opts, "x ~s - symbolic links not supported~n", [Name]),
+ read_verbose(Opts, "x ~ts - symbolic links not supported~n", [Name]),
not_written;
{error,Reason} -> throw({error, Reason})
end.
@@ -774,10 +774,10 @@ write_extracted_file(Name, Bin, Opts) ->
end,
case Write of
true ->
- read_verbose(Opts, "x ~s~n", [Name]),
+ read_verbose(Opts, "x ~ts~n", [Name]),
write_file(Name, Bin);
false ->
- read_verbose(Opts, "x ~s - exists, not created~n", [Name]),
+ read_verbose(Opts, "x ~ts - exists, not created~n", [Name]),
not_written
end.
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 498d850df3..32742e419b 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.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
@@ -624,7 +624,7 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) ->
ok = file:close(Fd),
check_source(S3, CheckOnly);
{error, Reason} ->
- io:format("escript: ~p\n", [Reason]),
+ io:format("escript: ~tp\n", [Reason]),
fatal("Preprocessor error")
end.
@@ -694,7 +694,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
epp_parse_file(Epp, S2, [Form | Forms]);
true ->
Args = lists:flatten(io_lib:format("illegal mode attribute: ~p", [NewMode])),
- io:format("~s:~w ~s\n", [S#state.file,Ln,Args]),
+ io:format("~ts:~w ~s\n", [S#state.file,Ln,Args]),
Error = {error,{Ln,erl_parse,Args}},
Nerrs= S#state.n_errors + 1,
epp_parse_file(Epp, S2#state{n_errors = Nerrs}, [Error | Forms])
@@ -710,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("~ts:~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 ->
@@ -780,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("~ts:~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("~ts: ~ts\n", [F,Mod:format_error(E)]),
list_errors(F, Es);
list_errors(_F, []) -> ok.
@@ -795,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("~ts:~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("~ts: Warning: ~ts\n", [F,Mod:format_error(E)])},
[M|format_message(F, Es)];
format_message(_, []) -> [].
@@ -851,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 afa914a456..77c8029f59 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.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
@@ -42,11 +42,16 @@
-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]).
%%-----------------------------------------------------------------------------
+-type access() :: public | protected | private.
-type tab() :: atom() | tid().
+-type type() :: set | ordered_set | bag | duplicate_bag.
+-type continuation() :: '$end_of_table'
+ | {tab(),integer(),integer(),binary(),list(),integer()}
+ | {tab(),_,_,integer(),binary(),list(),integer(),integer()}.
%% a similar definition is also in erl_types
-opaque tid() :: integer().
@@ -57,59 +62,398 @@
%%-----------------------------------------------------------------------------
-%% The following functions used to be found in this module, but
-%% are now BIFs (i.e. implemented in C).
-%%
-%% all/0
-%% new/2
-%% delete/1
-%% delete/2
-%% first/1
-%% info/1
-%% info/2
-%% safe_fixtable/2
-%% lookup/2
-%% lookup_element/3
-%% insert/2
-%% is_compiled_ms/1
-%% last/1
-%% member/2
-%% next/2
-%% prev/2
-%% rename/2
-%% slot/2
-%% match/1
-%% match/2
-%% match/3
-%% match_object/1
-%% match_object/2
-%% match_object/3
-%% match_spec_compile/1
-%% match_spec_run_r/3
-%% select/1
-%% select/2
-%% select/3
-%% select_count/2
-%% select_reverse/1
-%% select_reverse/2
-%% select_reverse/3
-%% select_delete/2
-%% setopts/2
-%% update_counter/3
-%% update_element/3
-%%
+%%% BIFs
+
+-export([all/0, delete/1, delete/2, delete_all_objects/1,
+ delete_object/2, first/1, give_away/3, info/1, info/2,
+ insert/2, insert_new/2, is_compiled_ms/1, last/1, lookup/2,
+ lookup_element/3, match/1, match/2, match/3, match_object/1,
+ match_object/2, match_object/3, match_spec_compile/1,
+ match_spec_run_r/3, member/2, new/2, next/2, prev/2,
+ rename/2, safe_fixtable/2, select/1, select/2, select/3,
+ select_count/2, select_delete/2, select_reverse/1,
+ select_reverse/2, select_reverse/3, setopts/2, slot/2,
+ update_counter/3, update_element/3]).
+
+-spec all() -> [Tab] when
+ Tab :: tab().
+
+all() ->
+ erlang:nif_error(undef).
+
+-spec delete(Tab) -> true when
+ Tab :: tab().
+
+delete(_) ->
+ erlang:nif_error(undef).
+
+-spec delete(Tab, Key) -> true when
+ Tab :: tab(),
+ Key :: term().
+
+delete(_, _) ->
+ erlang:nif_error(undef).
+
+-spec delete_all_objects(Tab) -> true when
+ Tab :: tab().
+
+delete_all_objects(_) ->
+ erlang:nif_error(undef).
+
+-spec delete_object(Tab, Object) -> true when
+ Tab :: tab(),
+ Object :: tuple().
+
+delete_object(_, _) ->
+ erlang:nif_error(undef).
+
+-spec first(Tab) -> Key | '$end_of_table' when
+ Tab :: tab(),
+ Key :: term().
+
+first(_) ->
+ erlang:nif_error(undef).
+
+-spec give_away(Tab, Pid, GiftData) -> true when
+ Tab :: tab(),
+ Pid :: pid(),
+ GiftData :: term().
+
+give_away(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec info(Tab) -> InfoList | undefined when
+ Tab :: tab(),
+ InfoList :: [InfoTuple],
+ InfoTuple :: {compressed, boolean()}
+ | {heir, pid() | none}
+ | {keypos, pos_integer()}
+ | {memory, non_neg_integer()}
+ | {name, atom()}
+ | {named_table, boolean()}
+ | {node, node()}
+ | {owner, pid()}
+ | {protection, access()}
+ | {size, non_neg_integer()}
+ | {type, type()}.
+
+info(_) ->
+ erlang:nif_error(undef).
+
+-spec info(Tab, Item) -> Value | undefined when
+ Tab :: tab(),
+ Item :: compressed | fixed | heir | keypos | memory
+ | name | named_table | node | owner | protection
+ | safe_fixed | size | stats | type,
+ Value :: term().
+
+info(_, _) ->
+ erlang:nif_error(undef).
+
+-spec insert(Tab, ObjectOrObjects) -> true when
+ Tab :: tab(),
+ ObjectOrObjects :: tuple() | [tuple()].
+
+insert(_, _) ->
+ erlang:nif_error(undef).
+
+-spec insert_new(Tab, ObjectOrObjects) -> boolean() when
+ Tab :: tab(),
+ ObjectOrObjects :: tuple() | [tuple()].
+
+insert_new(_, _) ->
+ erlang:nif_error(undef).
+
+-spec is_compiled_ms(Term) -> boolean() when
+ Term :: term().
+
+is_compiled_ms(_) ->
+ erlang:nif_error(undef).
+
+-spec last(Tab) -> Key | '$end_of_table' when
+ Tab :: tab(),
+ Key :: term().
+
+last(_) ->
+ erlang:nif_error(undef).
+
+-spec lookup(Tab, Key) -> [Object] when
+ Tab :: tab(),
+ Key :: term(),
+ Object :: tuple().
+
+lookup(_, _) ->
+ erlang:nif_error(undef).
+
+-spec lookup_element(Tab, Key, Pos) -> Elem when
+ Tab :: tab(),
+ Key :: term(),
+ Pos :: pos_integer(),
+ Elem :: term() | [term()].
+
+lookup_element(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Tab, Pattern) -> [Match] when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Match :: [term()].
+
+match(_, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Tab, Pattern, Limit) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Limit :: pos_integer(),
+ Match :: [term()],
+ Continuation :: continuation().
+
+match(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec match(Continuation) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Match :: [term()],
+ Continuation :: continuation().
+
+match(_) ->
+ erlang:nif_error(undef).
+
+-spec match_object(Tab, Pattern) -> [Object] when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Object :: tuple().
+
+match_object(_, _) ->
+ erlang:nif_error(undef).
+
+-spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ Pattern :: match_pattern(),
+ Limit :: pos_integer(),
+ Match :: [term()],
+ Continuation :: continuation().
+
+match_object(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec match_object(Continuation) -> {[Match], Continuation} |
+ '$end_of_table' when
+ Match :: [term()],
+ Continuation :: continuation().
+
+match_object(_) ->
+ erlang:nif_error(undef).
+
+-spec match_spec_compile(MatchSpec) -> CompiledMatchSpec when
+ MatchSpec :: match_spec(),
+ CompiledMatchSpec :: comp_match_spec().
--opaque comp_match_spec() :: any(). %% this one is REALLY opaque
+match_spec_compile(_) ->
+ erlang:nif_error(undef).
--spec match_spec_run([tuple()], comp_match_spec()) -> [term()].
+-spec match_spec_run_r(List, CompiledMatchSpec, list()) -> list() when
+ List :: [tuple()],
+ CompiledMatchSpec :: comp_match_spec().
+
+match_spec_run_r(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec member(Tab, Key) -> boolean() when
+ Tab :: tab(),
+ Key :: term().
+
+member(_, _) ->
+ erlang:nif_error(undef).
+
+-spec new(Name, Options) -> tid() | atom() when
+ Name :: atom(),
+ Options :: [Option],
+ Option :: Type | Access | named_table | {keypos,Pos}
+ | {heir, Pid :: pid(), HeirData} | {heir, none} | Tweaks,
+ Type :: type(),
+ Access :: access(),
+ Tweaks :: {write_concurrency, boolean()}
+ | {read_concurrency, boolean()}
+ | compressed,
+ Pos :: pos_integer(),
+ HeirData :: term().
+
+new(_, _) ->
+ erlang:nif_error(undef).
+
+-spec next(Tab, Key1) -> Key2 | '$end_of_table' when
+ Tab :: tab(),
+ Key1 :: term(),
+ Key2 :: term().
+
+next(_, _) ->
+ erlang:nif_error(undef).
+
+-spec prev(Tab, Key1) -> Key2 | '$end_of_table' when
+ Tab :: tab(),
+ Key1 :: term(),
+ Key2 :: term().
+
+prev(_, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: ets:rename/2
+-spec rename(Tab, Name) -> Name when
+ Tab :: tab(),
+ Name :: atom().
+
+rename(_, _) ->
+ erlang:nif_error(undef).
+
+-spec safe_fixtable(Tab, Fix) -> true when
+ Tab :: tab(),
+ Fix :: boolean().
+
+safe_fixtable(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select(Tab, MatchSpec) -> [Match] when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Match :: term().
+
+select(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select(Tab, MatchSpec, Limit) -> {[Match],Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Limit :: pos_integer(),
+ Match :: term(),
+ Continuation :: continuation().
+
+select(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec select(Continuation) -> {[Match],Continuation} | '$end_of_table' when
+ Match :: term(),
+ Continuation :: continuation().
+
+select(_) ->
+ erlang:nif_error(undef).
+
+-spec select_count(Tab, MatchSpec) -> NumMatched when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ NumMatched :: non_neg_integer().
+
+select_count(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select_delete(Tab, MatchSpec) -> NumDeleted when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ NumDeleted :: non_neg_integer().
+
+select_delete(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select_reverse(Tab, MatchSpec) -> [Match] when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Match :: term().
+
+select_reverse(_, _) ->
+ erlang:nif_error(undef).
+
+-spec select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} |
+ '$end_of_table' when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ Limit :: pos_integer(),
+ Match :: term(),
+ Continuation :: continuation().
+
+select_reverse(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec select_reverse(Continuation) -> {[Match],Continuation} |
+ '$end_of_table' when
+ Continuation :: continuation(),
+ Match :: term().
+
+select_reverse(_) ->
+ erlang:nif_error(undef).
+
+-spec setopts(Tab, Opts) -> true when
+ Tab :: tab(),
+ Opts :: Opt | [Opt],
+ Opt :: {heir, pid(), HeirData} | {heir,none},
+ HeirData :: term().
+
+setopts(_, _) ->
+ erlang:nif_error(undef).
+
+-spec slot(Tab, I) -> [Object] | '$end_of_table' when
+ Tab :: tab(),
+ I :: non_neg_integer(),
+ Object :: tuple().
+
+slot(_, _) ->
+ erlang:nif_error(undef).
+
+-spec update_counter(Tab, Key, UpdateOp) -> Result when
+ Tab :: tab(),
+ Key :: term(),
+ UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue},
+ Pos :: integer(),
+ Incr :: integer(),
+ Threshold :: integer(),
+ SetValue :: integer(),
+ Result :: integer();
+ (Tab, Key, [UpdateOp]) -> [Result] when
+ Tab :: tab(),
+ Key :: term(),
+ UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue},
+ Pos :: integer(),
+ Incr :: integer(),
+ Threshold :: integer(),
+ SetValue :: integer(),
+ Result :: integer();
+ (Tab, Key, Incr) -> Result when
+ Tab :: tab(),
+ Key :: term(),
+ Incr :: integer(),
+ Result :: integer().
+
+update_counter(_, _, _) ->
+ erlang:nif_error(undef).
+
+-spec update_element(Tab, Key, ElementSpec :: {Pos, Value}) -> boolean() when
+ Tab :: tab(),
+ Key :: term(),
+ Pos :: pos_integer(),
+ Value :: term();
+ (Tab, Key, ElementSpec :: [{Pos, Value}]) -> boolean() when
+ Tab :: tab(),
+ Key :: term(),
+ Pos :: pos_integer(),
+ Value :: term().
+
+update_element(_, _, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
+-opaque comp_match_spec() :: binary(). %% this one is REALLY opaque
+
+-spec match_spec_run(List, CompiledMatchSpec) -> list() when
+ List :: [tuple()],
+ CompiledMatchSpec :: comp_match_spec().
match_spec_run(List, CompiledMS) ->
lists:reverse(ets:match_spec_run_r(List, CompiledMS, [])).
--type continuation() :: '$end_of_table'
- | {tab(),integer(),integer(),binary(),list(),integer()}
- | {tab(),_,_,integer(),binary(),list(),integer(),integer()}.
-
-spec repair_continuation(Continuation, MatchSpec) -> Continuation when
Continuation :: continuation(),
MatchSpec :: match_spec().
@@ -157,7 +501,7 @@ fun2ms(ShellFun) when is_function(ShellFun) ->
case ms_transform:transform_from_shell(
?MODULE,Clauses,ImportList) of
{error,[{_,[{_,_,Code}|_]}|_],_} ->
- io:format("Error: ~s~n",
+ io:format("Error: ~ts~n",
[ms_transform:format_error(Code)]),
{error,transform_error};
Else ->
@@ -1242,7 +1586,7 @@ choice(Height, Width, P, Mode, Tab, Key, Turn, Opos) ->
{ok,Re} ->
re_search(Height, Width, Tab, ets:first(Tab), Re, 1, 1);
{error,{ErrorString,_Pos}} ->
- io:format("~s\n", [ErrorString]),
+ io:format("~ts\n", [ErrorString]),
choice(Height, Width, P, Mode, Tab, Key, Turn, Opos)
end;
_ ->
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index f40904df1c..e49cbc1fd1 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -2,7 +2,7 @@
%%
%% %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
@@ -67,7 +67,8 @@ expr_grp([], Bs0, _Lf, Acc) ->
{value,Acc,Bs0}.
eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun) ->
- {list_to_binary(S),Bs0};
+ Latin1 = [C band 16#FF || C <- S],
+ {list_to_binary(Latin1),Bs0};
eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs, _Fun) ->
{_Size,[Type,_Unit,_Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
@@ -162,8 +163,10 @@ bin_gen([], Bin, _Bs0, _BBs0, _Mfun, _Efun, false) ->
bin_gen_field({bin_element,_,{string,_,S},default,default},
Bin, Bs, BBs, _Mfun, _Efun) ->
- Bits = list_to_binary(S),
- Size = byte_size(Bits),
+ Bits = try list_to_binary(S)
+ catch _:_ -> <<>>
+ end,
+ Size = length(S),
case Bin of
<<Bits:Size/binary,Rest/bitstring>> ->
{match,Bs,BBs,Rest};
@@ -172,16 +175,42 @@ bin_gen_field({bin_element,_,{string,_,S},default,default},
_ ->
done
end;
+bin_gen_field({bin_element,Line,{string,SLine,S},Size0,Options0},
+ Bin0, Bs0, BBs0, Mfun, Efun) ->
+ {Size1, [Type,{unit,Unit},Sign,Endian]} =
+ make_bit_type(Line, Size0, Options0),
+ match_check_size(Mfun, Size1, BBs0),
+ {value, Size, _BBs} = Efun(Size1, BBs0),
+ F = fun(C, Bin, Bs, BBs) ->
+ bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian,
+ {integer,SLine,C}, Bs, BBs, Mfun)
+ end,
+ bin_gen_field_string(S, Bin0, Bs0, BBs0, F);
bin_gen_field({bin_element,Line,VE,Size0,Options0},
Bin, Bs0, BBs0, Mfun, Efun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
V = erl_eval:partial_eval(VE),
+ NewV = coerce_to_float(V, Type),
match_check_size(Mfun, Size1, BBs0),
{value, Size, _BBs} = Efun(Size1, BBs0),
+ bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun).
+
+bin_gen_field_string([], Rest, Bs, BBs, _F) ->
+ {match,Bs,BBs,Rest};
+bin_gen_field_string([C|Cs], Bin0, Bs0, BBs0, Fun) ->
+ case Fun(C, Bin0, Bs0, BBs0) of
+ {match,Bs,BBs,Rest} ->
+ bin_gen_field_string(Cs, Rest, Bs, BBs, Fun);
+ {nomatch,Rest} ->
+ {nomatch,Rest};
+ done ->
+ done
+ end.
+
+bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun) ->
case catch get_value(Bin, Type, Size, Unit, Sign, Endian) of
{Val,<<_/bitstring>>=Rest} ->
- NewV = coerce_to_float(V, Type),
case catch Mfun(match, {NewV,Val,Bs0}) of
{match,Bs} ->
BBs = add_bin_binding(Mfun, NewV, Bs, BBs0),
@@ -223,20 +252,41 @@ match_bits_1([F|Fs], Bits0, Bs0, BBs0, Mfun, Efun) ->
match_field_1({bin_element,_,{string,_,S},default,default},
Bin, Bs, BBs, _Mfun, _Efun) ->
- Bits = list_to_binary(S),
+ Bits = list_to_binary(S), % fails if there are characters > 255
Size = byte_size(Bits),
<<Bits:Size/binary,Rest/binary-unit:1>> = Bin,
{Bs,BBs,Rest};
+match_field_1({bin_element,Line,{string,SLine,S},Size0,Options0},
+ Bin0, Bs0, BBs0, Mfun, Efun) ->
+ {Size1, [Type,{unit,Unit},Sign,Endian]} =
+ make_bit_type(Line, Size0, Options0),
+ Size2 = erl_eval:partial_eval(Size1),
+ match_check_size(Mfun, Size2, BBs0),
+ {value, Size, _BBs} = Efun(Size2, BBs0),
+ F = fun(C, Bin, Bs, BBs) ->
+ match_field(Bin, Type, Size, Unit, Sign, Endian,
+ {integer,SLine,C}, Bs, BBs, Mfun)
+ end,
+ match_field_string(S, Bin0, Bs0, BBs0, F);
match_field_1({bin_element,Line,VE,Size0,Options0},
Bin, Bs0, BBs0, Mfun, Efun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
V = erl_eval:partial_eval(VE),
+ NewV = coerce_to_float(V, Type),
Size2 = erl_eval:partial_eval(Size1),
match_check_size(Mfun, Size2, BBs0),
{value, Size, _BBs} = Efun(Size2, BBs0),
+ match_field(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun).
+
+match_field_string([], Rest, Bs, BBs, _Fun) ->
+ {Bs,BBs,Rest};
+match_field_string([C|Cs], Bin0, Bs0, BBs0, Fun) ->
+ {Bs,BBs,Bin} = Fun(C, Bin0, Bs0, BBs0),
+ match_field_string(Cs, Bin, Bs, BBs, Fun).
+
+match_field(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun) ->
{Val,Rest} = get_value(Bin, Type, Size, Unit, Sign, Endian),
- NewV = coerce_to_float(V, Type),
{match,Bs} = Mfun(match, {NewV,Val,Bs0}),
BBs = add_bin_binding(Mfun, NewV, Bs, BBs0),
{Bs,BBs,Rest}.
diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl
index 3f31852afc..2bf88959b7 100644
--- a/lib/stdlib/src/file_sorter.erl
+++ b/lib/stdlib/src/file_sorter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -633,7 +633,7 @@ last_merge(R, W) when length(R) =< W#w.no_files ->
case W#w.out of
Fun when is_function(Fun) ->
{Fs, W1} = init_merge(lists:reverse(R), 1, [], W),
- ?DEBUG("merging ~p~n", [lists:reverse(R)]),
+ ?DEBUG("merging ~tp~n", [lists:reverse(R)]),
W2 = merge_files(Fs, [], 0, nolast, W1),
NW = close_input(W2),
outfun(close, NW);
@@ -659,7 +659,7 @@ merge_runs([R, R1 | Rs], NRs0, W) ->
merge_files(R, W) ->
{W1, Temp} = next_temp(W),
- ?DEBUG("merging ~p~nto ~p~n", [lists:reverse(R), Temp]),
+ ?DEBUG("merging ~tp~nto ~tp~n", [lists:reverse(R), Temp]),
{Temp, merge_files(R, W1, Temp)}.
merge_files(R, W, FileName) ->
@@ -1501,7 +1501,7 @@ close_out(_) ->
close_file(Fd, W) ->
{Fd, FileName} = lists:keyfind(Fd, 1, W#w.temp),
- ?DEBUG("closing ~p~n", [FileName]),
+ ?DEBUG("closing ~tp~n", [FileName]),
file:close(Fd),
W#w{temp = [FileName | lists:keydelete(Fd, 1, W#w.temp)]}.
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index fa4f92617c..42ef3679a2 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -19,16 +19,14 @@
-module(filelib).
%% File utilities.
-
-%% Avoid warning for local function error/1 clashing with autoimported BIF.
--compile({no_auto_import,[error/1]}).
--export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1,
- compile_wildcard/1]).
+-export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1]).
-export([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]).
-
-export([wildcard/3, is_dir/2, is_file/2, is_regular/2]).
-export([fold_files/6, last_modified/2, file_size/2]).
+%% For debugging/testing.
+-export([compile_wildcard/1]).
+
-include_lib("kernel/include/file.hrl").
-define(HANDLE_ERROR(Expr),
@@ -37,7 +35,7 @@
catch
error:{badpattern,_}=UnUsUalVaRiAbLeNaMe ->
%% Get the stack backtrace correct.
- erlang:error(UnUsUalVaRiAbLeNaMe)
+ error(UnUsUalVaRiAbLeNaMe)
end).
-type filename() :: file:name().
@@ -48,19 +46,19 @@
-spec wildcard(Wildcard) -> [file:filename()] when
Wildcard :: filename() | dirname().
wildcard(Pattern) when is_list(Pattern) ->
- ?HANDLE_ERROR(do_wildcard(Pattern, file)).
+ ?HANDLE_ERROR(do_wildcard(Pattern, ".", file)).
-spec wildcard(Wildcard, Cwd) -> [file:filename()] when
Wildcard :: filename() | dirname(),
Cwd :: dirname().
-wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) ->
+wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) ->
?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file));
wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) ->
- ?HANDLE_ERROR(do_wildcard(Pattern, Mod)).
+ ?HANDLE_ERROR(do_wildcard(Pattern, ".", Mod)).
-spec wildcard(file:name(), file:name(), atom()) -> [file:filename()].
wildcard(Pattern, Cwd, Mod)
- when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) ->
+ when is_list(Pattern), is_list(Cwd), is_atom(Mod) ->
?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)).
-spec is_dir(Name) -> boolean() when
@@ -124,41 +122,6 @@ file_size(File, Mod) when is_atom(Mod) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-do_wildcard(Pattern, Mod) when is_list(Pattern) ->
- do_wildcard_comp(do_compile_wildcard(Pattern), Mod).
-
-do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) ->
- case eval_read_file_info(File, Mod) of
- {ok,_} -> [File];
- _ -> []
- end;
-do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) ->
- do_wildcard_1([Base], Rest, Mod).
-
-do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) ->
- do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod).
-
-do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
- case eval_read_file_info(filename:absname(File, Cwd), Mod) of
- {ok,_} -> [File];
- _ -> []
- end;
-do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) ->
- {Cwd,PrefixLen} = case filename:join([Cwd0]) of
- Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1};
- Other -> {Other,length(Other)+1}
- end, %Slash away redundant slashes.
- [
- if
- is_binary(N) ->
- <<_:PrefixLen/binary,Res/binary>> = N,
- Res;
- true ->
- lists:nthtail(PrefixLen, N)
- end || N <- do_wildcard_1([Cwd], Rest, Mod)];
-do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) ->
- do_wildcard_1([Base], Rest, Mod).
-
do_is_dir(Dir, Mod) ->
case eval_read_file_info(Dir, Mod) of
{ok, #file_info{type=directory}} ->
@@ -287,19 +250,37 @@ ensure_dir(F) ->
%%% Pattern matching using a compiled wildcard.
%%%
-do_wildcard_1(Files, Pattern, Mod) ->
- do_wildcard_2(Files, Pattern, [], Mod).
+do_wildcard(Pattern, Cwd, Mod) ->
+ {Compiled,PrefixLen} = compile_wildcard(Pattern, Cwd),
+ Files0 = do_wildcard_1(Compiled, Mod),
+ Files = if
+ PrefixLen =:= 0 ->
+ Files0;
+ true ->
+ [lists:nthtail(PrefixLen, File) || File <- Files0]
+ end,
+ lists:sort(Files).
+
+do_wildcard_1({exists,File}, Mod) ->
+ case eval_read_file_info(File, Mod) of
+ {ok,_} -> [File];
+ _ -> []
+ end;
+do_wildcard_1([Base|Rest], Mod) ->
+ do_wildcard_2([Base], Rest, [], Mod).
do_wildcard_2([File|Rest], Pattern, Result, Mod) ->
do_wildcard_2(Rest, Pattern, do_wildcard_3(File, Pattern, Result, Mod), Mod);
do_wildcard_2([], _, Result, _Mod) ->
Result.
-do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
- case do_list_dir(Base, Mod) of
- {ok, Files0} ->
- Files = lists:sort(Files0),
- Matches = wildcard_4(Pattern, Files, Base, []),
+do_wildcard_3(Base, [[double_star]|Rest], Result, Mod) ->
+ do_double_star(".", [Base], Rest, Result, Mod, true);
+do_wildcard_3(Base0, [Pattern|Rest], Result, Mod) ->
+ case do_list_dir(Base0, Mod) of
+ {ok, Files} ->
+ Base = prepare_base(Base0),
+ Matches = do_wildcard_4(Pattern, Base, Files),
do_wildcard_2(Matches, Rest, Result, Mod);
_ ->
Result
@@ -307,100 +288,121 @@ do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
do_wildcard_3(Base, [], Result, _Mod) ->
[Base|Result].
-wildcard_4(Pattern, [File|Rest], Base, Result) when is_binary(File) ->
- case wildcard_5(Pattern, binary_to_list(File)) of
- true ->
- wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]);
+do_wildcard_4(Pattern, Base, Files) ->
+ case will_always_match(Pattern) of
false ->
- wildcard_4(Pattern, Rest, Base, Result)
- end;
-wildcard_4(Pattern, [File|Rest], Base, Result) ->
- case wildcard_5(Pattern, File) of
+ [Base++F || F <- Files, match_part(Pattern, F)];
true ->
- wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]);
- false ->
- wildcard_4(Pattern, Rest, Base, Result)
- end;
-wildcard_4(_Patt, [], _Base, Result) ->
- Result.
+ [Base++F || F <- Files]
+ end.
-wildcard_5([question|Rest1], [_|Rest2]) ->
- wildcard_5(Rest1, Rest2);
-wildcard_5([accept], _) ->
+match_part([question|Rest1], [_|Rest2]) ->
+ match_part(Rest1, Rest2);
+match_part([accept], _) ->
+ true;
+match_part([double_star], _) ->
true;
-wildcard_5([star|Rest], File) ->
+match_part([star|Rest], File) ->
do_star(Rest, File);
-wildcard_5([{one_of, Ordset}|Rest], [C|File]) ->
- case ordsets:is_element(C, Ordset) of
- true -> wildcard_5(Rest, File);
- false -> false
- end;
-wildcard_5([{alt, Alts}], File) ->
+match_part([{one_of, Ordset}|Rest], [C|File]) ->
+ gb_sets:is_element(C, Ordset) andalso match_part(Rest, File);
+match_part([{alt, Alts}], File) ->
do_alt(Alts, File);
-wildcard_5([C|Rest1], [C|Rest2]) when is_integer(C) ->
- wildcard_5(Rest1, Rest2);
-wildcard_5([X|_], [Y|_]) when is_integer(X), is_integer(Y) ->
+match_part([C|Rest1], [C|Rest2]) when is_integer(C) ->
+ match_part(Rest1, Rest2);
+match_part([X|_], [Y|_]) when is_integer(X), is_integer(Y) ->
false;
-wildcard_5([], []) ->
+match_part([], []) ->
true;
-wildcard_5([], [_|_]) ->
+match_part([], [_|_]) ->
false;
-wildcard_5([_|_], []) ->
+match_part([_|_], []) ->
false.
-do_star(Pattern, [X|Rest]) ->
- case wildcard_5(Pattern, [X|Rest]) of
- true -> true;
- false -> do_star(Pattern, Rest)
- end;
+will_always_match([accept]) -> true;
+will_always_match(_) -> false.
+
+prepare_base(Base0) ->
+ Base1 = filename:join(Base0, "x"),
+ "x"++Base2 = lists:reverse(Base1),
+ lists:reverse(Base2).
+
+do_double_star(Base, [H|T], Rest, Result, Mod, Root) ->
+ Full = case Root of
+ false -> filename:join(Base, H);
+ true -> H
+ end,
+ Result1 = case do_list_dir(Full, Mod) of
+ {ok, Files} ->
+ do_double_star(Full, Files, Rest, Result, Mod, false);
+ _ -> Result
+ end,
+ Result2 = case Root andalso Rest == [] of
+ true -> Result1;
+ false -> do_wildcard_3(Full, Rest, Result1, Mod)
+ end,
+ do_double_star(Base, T, Rest, Result2, Mod, Root);
+do_double_star(_Base, [], _Rest, Result, _Mod, _Root) ->
+ Result.
+
+do_star(Pattern, [_|Rest]=File) ->
+ match_part(Pattern, File) orelse do_star(Pattern, Rest);
do_star(Pattern, []) ->
- wildcard_5(Pattern, []).
+ match_part(Pattern, []).
do_alt([Alt|Rest], File) ->
- case wildcard_5(Alt, File) of
- true -> true;
- false -> do_alt(Rest, File)
- end;
+ match_part(Alt, File) orelse do_alt(Rest, File);
do_alt([], _File) ->
false.
-do_list_dir(current, Mod) -> eval_list_dir(".", Mod);
do_list_dir(Dir, Mod) -> eval_list_dir(Dir, Mod).
-join(current, File) -> File;
-join(Base, File) -> filename:join(Base, File).
-
%%% Compiling a wildcard.
-compile_wildcard(Pattern) ->
- ?HANDLE_ERROR(do_compile_wildcard(Pattern)).
-
-do_compile_wildcard(Pattern) ->
- {compiled_wildcard,compile_wildcard_1(Pattern)}.
+%% Only for debugging.
+compile_wildcard(Pattern) when is_list(Pattern) ->
+ {compiled_wildcard,?HANDLE_ERROR(compile_wildcard(Pattern, "."))}.
-compile_wildcard_1(Pattern) ->
+compile_wildcard(Pattern, Cwd0) ->
[Root|Rest] = filename:split(Pattern),
case filename:pathtype(Root) of
relative ->
- compile_wildcard_2([Root|Rest], current);
+ Cwd = filename:join([Cwd0]),
+ compile_wildcard_2([Root|Rest], {cwd,Cwd});
_ ->
- compile_wildcard_2(Rest, [Root])
+ compile_wildcard_2(Rest, {root,0,Root})
end.
compile_wildcard_2([Part|Rest], Root) ->
case compile_part(Part) of
Part ->
- compile_wildcard_2(Rest, join(Root, Part));
+ compile_wildcard_2(Rest, compile_join(Root, Part));
Pattern ->
compile_wildcard_3(Rest, [Pattern,Root])
end;
-compile_wildcard_2([], Root) -> {exists,Root}.
+compile_wildcard_2([], {root,PrefixLen,Root}) ->
+ {{exists,Root},PrefixLen}.
compile_wildcard_3([Part|Rest], Result) ->
compile_wildcard_3(Rest, [compile_part(Part)|Result]);
compile_wildcard_3([], Result) ->
- lists:reverse(Result).
+ case lists:reverse(Result) of
+ [{root,PrefixLen,Root}|Compiled] ->
+ {[Root|Compiled],PrefixLen};
+ [{cwd,Root}|Compiled] ->
+ {[Root|Compiled],length(filename:join(Root, "x"))-1}
+ end.
+
+compile_join({cwd,"."}, File) ->
+ {root,0,File};
+compile_join({cwd,Cwd}, File0) ->
+ File = filename:join([File0]),
+ Root = filename:join(Cwd, File),
+ PrefixLen = length(Root) - length(File),
+ {root,PrefixLen,Root};
+compile_join({root,PrefixLen,Root}, File) ->
+ {root,PrefixLen,filename:join(Root, File)}.
compile_part(Part) ->
compile_part(Part, false, []).
@@ -409,13 +411,17 @@ compile_part_to_sep(Part) ->
compile_part(Part, true, []).
compile_part([], true, _) ->
- error(missing_delimiter);
+ badpattern(missing_delimiter);
compile_part([$,|Rest], true, Result) ->
{ok, $,, lists:reverse(Result), Rest};
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) ->
@@ -441,8 +447,6 @@ compile_part([], _Upto, Result) ->
compile_charset([$]|Rest], Ordset) ->
compile_charset1(Rest, ordsets:add_element($], Ordset));
-compile_charset([$-|Rest], Ordset) ->
- compile_charset1(Rest, ordsets:add_element($-, Ordset));
compile_charset([], _Ordset) ->
error;
compile_charset(List, Ordset) ->
@@ -451,7 +455,7 @@ compile_charset(List, Ordset) ->
compile_charset1([Lower, $-, Upper|Rest], Ordset) when Lower =< Upper ->
compile_charset1(Rest, compile_range(Lower, Upper, Ordset));
compile_charset1([$]|Rest], Ordset) ->
- {ok, {one_of, Ordset}, Rest};
+ {ok, {one_of, gb_sets:from_ordset(Ordset)}, Rest};
compile_charset1([X|Rest], Ordset) ->
compile_charset1(Rest, ordsets:add_element(X, Ordset));
compile_charset1([], _Ordset) ->
@@ -477,8 +481,8 @@ compile_alt(Pattern, Result) ->
error
end.
-error(Reason) ->
- erlang:error({badpattern,Reason}).
+badpattern(Reason) ->
+ error({badpattern,Reason}).
eval_read_file_info(File, file) ->
file:read_file_info(File);
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index a6b42cc68c..66e54ef221 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -61,15 +61,15 @@
%% (for WIN32): absname("/") -> "D:/"
--spec absname(Filename) -> file:filename() when
- Filename :: file:name().
+-spec absname(Filename) -> file:filename_all() when
+ Filename :: file:name_all().
absname(Name) ->
{ok, Cwd} = file:get_cwd(),
absname(Name, Cwd).
--spec absname(Filename, Dir) -> file:filename() when
- Filename :: file:name(),
- Dir :: file:filename().
+-spec absname(Filename, Dir) -> file:filename_all() when
+ Filename :: file:name_all(),
+ Dir :: file:name_all().
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) ->
@@ -122,9 +122,9 @@ absname_vr([[X, $:]|Name], _, _AbsBase) ->
%% This is just a join/2, but assumes that
%% AbsBase must be absolute and Name must be relative.
--spec absname_join(Dir, Filename) -> file:filename() when
- Dir :: file:filename(),
- Filename :: file:name().
+-spec absname_join(Dir, Filename) -> file:filename_all() when
+ Dir :: file:name_all(),
+ Filename :: file:name_all().
absname_join(AbsBase, Name) ->
join(AbsBase, flatten(Name)).
@@ -136,8 +136,8 @@ absname_join(AbsBase, Name) ->
%% basename("/usr/foo/") -> "foo" (trailing slashes ignored)
%% basename("/") -> []
--spec basename(Filename) -> file:filename() when
- Filename :: file:name().
+-spec basename(Filename) -> file:filename_all() when
+ Filename :: file:name_all().
basename(Name) when is_binary(Name) ->
case os:type() of
{win32,_} ->
@@ -201,9 +201,9 @@ skip_prefix(Name, _) ->
%% rootname(basename("xxx.jam")) -> "xxx"
%% rootname(basename("xxx.erl")) -> "xxx"
--spec basename(Filename, Ext) -> file:filename() when
- Filename :: file:name(),
- Ext :: file:name().
+-spec basename(Filename, Ext) -> file:filename_all() when
+ Filename :: file:name_all(),
+ Ext :: file:name_all().
basename(Name, Ext) when is_binary(Name), is_list(Ext) ->
basename(Name,filename_string_to_binary(Ext));
basename(Name, Ext) when is_list(Name), is_binary(Ext) ->
@@ -251,8 +251,8 @@ basename([], _Ext, Tail, _DrvSep2) ->
%% Example: dirname("/usr/src/kalle.erl") -> "/usr/src",
%% dirname("kalle.erl") -> "."
--spec dirname(Filename) -> file:filename() when
- Filename :: file:name().
+-spec dirname(Filename) -> file:filename_all() when
+ Filename :: file:name_all().
dirname(Name) when is_binary(Name) ->
{Dsep,Drivesep} = separators(),
SList = case Dsep of
@@ -344,8 +344,8 @@ dirjoin1([H|T],Acc,Sep) ->
%%
%% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src"
--spec extension(Filename) -> file:filename() when
- Filename :: file:name().
+-spec extension(Filename) -> file:filename_all() when
+ Filename :: file:name_all().
extension(Name) when is_binary(Name) ->
{Dsep,_} = separators(),
SList = case Dsep of
@@ -387,8 +387,8 @@ extension([], Result, _OsType) ->
%% Joins a list of filenames with directory separators.
--spec join(Components) -> file:filename() when
- Components :: [file:filename()].
+-spec join(Components) -> file:filename_all() when
+ Components :: [file:name_all()].
join([Name1, Name2|Rest]) ->
join([join(Name1, Name2)|Rest]);
join([Name]) when is_list(Name) ->
@@ -400,9 +400,9 @@ 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().
+-spec join(Name1, Name2) -> file:filename_all() when
+ Name1 :: file:name_all(),
+ Name2 :: file:name_all().
join(Name1, Name2) when is_list(Name1), is_list(Name2) ->
OsType = major_os_type(),
case pathtype(Name2) of
@@ -488,7 +488,7 @@ maybe_remove_dirsep(Name, _) ->
%% a given base directory, which is is assumed to be normalised
%% by a previous call to join/{1,2}.
--spec append(file:filename(), file:name()) -> file:filename().
+-spec append(file:filename_all(), file:name_all()) -> file:filename_all().
append(Dir, Name) when is_binary(Dir), is_binary(Name) ->
<<Dir/binary,$/:8,Name/binary>>;
append(Dir, Name) when is_binary(Dir) ->
@@ -511,7 +511,7 @@ append(Dir, Name) ->
%% Example: a:bar.erl, /temp/foo.erl
-spec pathtype(Path) -> 'absolute' | 'relative' | 'volumerelative' when
- Path :: file:name().
+ Path :: file:name_all().
pathtype(Atom) when is_atom(Atom) ->
pathtype(atom_to_list(Atom));
pathtype(Name) when is_list(Name) or is_binary(Name) ->
@@ -564,8 +564,8 @@ win32_pathtype(_) -> relative.
%% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle"
%% rootname("/jam.src/foo.erl") -> "/jam.src/foo"
--spec rootname(Filename) -> file:filename() when
- Filename :: file:name().
+-spec rootname(Filename) -> file:filename_all() when
+ Filename :: file:name_all().
rootname(Name) when is_binary(Name) ->
list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128
rootname(Name0) ->
@@ -594,9 +594,9 @@ rootname([], Root, _Ext, _OsType) ->
%% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam"
%% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo"
--spec rootname(Filename, Ext) -> file:filename() when
- Filename :: file:name(),
- Ext :: file:name().
+-spec rootname(Filename, Ext) -> file:filename_all() when
+ Filename :: file:name_all(),
+ Ext :: file:name_all().
rootname(Name, Ext) when is_binary(Name), is_binary(Ext) ->
list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext)));
rootname(Name, Ext) when is_binary(Name) ->
@@ -623,8 +623,8 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) ->
%% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"]
-spec split(Filename) -> Components when
- Filename :: file:name(),
- Components :: [file:filename()].
+ Filename :: file:name_all(),
+ Components :: [file:name_all()].
split(Name) when is_binary(Name) ->
case os:type() of
{win32, _} -> win32_splitb(Name);
@@ -717,8 +717,8 @@ split([], Comp, Components, OsType) ->
%% will be converted to backslashes. On all platforms, the
%% name will be normalized as done by join/1.
--spec nativename(Path) -> file:filename() when
- Path :: file:filename().
+-spec nativename(Path) -> file:filename_all() when
+ Path :: file:name_all().
nativename(Name0) ->
Name = join([Name0]), %Normalize.
case os:type() of
@@ -878,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
@@ -915,16 +915,14 @@ 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.
--spec flatten(Filename) -> file:filename() when
- Filename :: file:name().
+-spec flatten(Filename) -> file:filename_all() when
+ Filename :: file:name_all().
flatten(Bin) when is_binary(Bin) ->
Bin;
flatten(List) ->
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/io.erl b/lib/stdlib/src/io.erl
index 9f65bbfa3a..c92e9e3ade 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-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,28 +22,30 @@
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]).
+%% Implemented in native code
+-export([printable_range/0]).
--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().
%%-------------------------------------------------------------------------
@@ -66,6 +68,11 @@ o_request(Io, Request, Func) ->
Other
end.
+%% Request what the user considers printable characters
+-spec printable_range() -> 'unicode' | 'latin1'.
+printable_range() ->
+ erlang:nif_error(undefined).
+
%% Put chars takes mixed *unicode* list from R13 onwards.
-spec put_chars(CharData) -> 'ok' when
CharData :: unicode:chardata().
@@ -73,9 +80,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,36 +131,34 @@ 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().
+ Data :: string() | unicode:unicode_binary().
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().
+ Data :: string() | 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().
+ Data :: string() | 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().
+ Data :: string() | unicode:unicode_binary().
get_line(Io, Prompt) ->
request(Io, {get_line,unicode,Prompt}).
@@ -219,11 +224,10 @@ 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?
read(Prompt) ->
read(default_input(), Prompt).
@@ -231,8 +235,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 +253,41 @@ read(Io, Prompt) ->
Other
end.
--spec read(IoDevice, Prompt, StartLine) -> Result when
+-spec read(IoDevice, Prompt, StartLocation) -> 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(),
+ 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(),
+ 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 +335,9 @@ fread(Prompt, Format) ->
IoDevice :: device(),
Prompt :: prompt(),
Format :: format(),
- Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}.
+ Result :: {'ok', Terms :: [term()]}
+ | {'error', {'fread', FreadError :: io_lib:fread_error()}}
+ | server_no_data().
fread(Io, Prompt, Format) ->
case request(Io, {fread,Prompt,Format}) of
@@ -348,7 +372,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 +380,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 +414,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 +465,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 +492,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 +517,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..a9b6d4131e 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-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
@@ -46,52 +47,71 @@
%% 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_atom/1,write_string/1,write_string/2,write_unicode_string/1,
- write_unicode_string/2, write_char/1, write_unicode_char/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_latin1_string/1,
+ write_latin1_string/2, write_char/1, write_latin1_char/1]).
--export([quote_atom/2, char_list/1, unicode_char_list/1,
- deep_char_list/1, deep_unicode_char_list/1,
- printable_list/1, printable_unicode_list/1]).
+-export([write_string_as_latin1/1, write_string_as_latin1/2,
+ write_char_as_latin1/1]).
+
+-export([quote_atom/2, char_list/1, latin1_char_list/1,
+ deep_char_list/1, deep_latin1_char_list/1,
+ printable_list/1, printable_latin1_list/1, printable_unicode_list/1]).
%% Utilities for collecting characters.
-export([collect_chars/3, collect_chars/4,
collect_line/2, collect_line/3, collect_line/4,
get_until/3, get_until/4]).
--export_type([chars/0, continuation/0]).
+%% The following functions were used by Yecc's include-file.
+-export([write_unicode_string/1, write_unicode_char/1,
+ deep_unicode_char_list/1]).
+
+-export_type([chars/0, latin1_string/0, continuation/0, fread_error/0]).
%%----------------------------------------------------------------------
-type chars() :: [char() | chars()].
+-type latin1_string() :: [unicode:latin1_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'.
%%----------------------------------------------------------------------
%% Interface calls to sub-modules.
--spec fwrite(Format, Data) -> chars() | UnicodeList when
+-spec fwrite(Format, Data) -> chars() when
Format :: io:format(),
- Data :: [term()],
- UnicodeList :: [unicode:unicode_char()],
Data :: [term()].
fwrite(Format, Args) ->
@@ -104,7 +124,7 @@ fwrite(Format, Args) ->
| {'more', RestFormat :: string(),
Nchars :: non_neg_integer(),
InputStack :: chars()}
- | {'error', What :: term()}.
+ | {'error', {'fread', What :: fread_error()}}.
fread(Chars, Format) ->
io_lib_fread:fread(Chars, Format).
@@ -117,15 +137,14 @@ fread(Chars, Format) ->
| {'done', Result, LeftOverChars :: string()},
Result :: {'ok', InputList :: [term()]}
| 'eof'
- | {'error', What :: term()}.
+ | {'error', {'fread', What :: fread_error()}}.
fread(Cont, Chars, Format) ->
io_lib_fread:fread(Cont, Chars, Format).
--spec format(Format, Data) -> chars() | UnicodeList when
+-spec format(Format, Data) -> chars() when
Format :: io:format(),
- Data :: [term()],
- UnicodeList :: [unicode:unicode_char()].
+ Data :: [term()].
format(Format, Args) ->
case catch io_lib_format:fwrite(Format, Args) of
@@ -159,27 +178,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).
+
+-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]).
-format_prompt(Format, Args) ->
+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 +276,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 +317,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,14 +331,19 @@ 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;
name_char(_) -> false.
+%%% There are two functions to write Unicode strings:
+%%% - they both escape control characters < 160;
+%%% - write_string() never escapes characters >= 160;
+%%% - write_string_as_latin1() also escapes characters >= 255.
+
%% write_string([Char]) -> [Char]
%% Generate the list of characters needed to print a string.
@@ -325,13 +356,33 @@ write_string(S) ->
-spec write_string(string(), char()) -> chars().
write_string(S, Q) ->
- [Q|write_string1(latin1, S, Q)].
+ [Q|write_string1(unicode_as_unicode, S, Q)].
+%% Backwards compatibility.
write_unicode_string(S) ->
- write_unicode_string(S, $"). %"
+ write_string(S).
+
+-spec write_latin1_string(Latin1String) -> latin1_string() when
+ Latin1String :: latin1_string().
-write_unicode_string(S, Q) ->
- [Q|write_string1(unicode, S, Q)].
+write_latin1_string(S) ->
+ write_latin1_string(S, $"). %"
+
+-spec write_latin1_string(latin1_string(), char()) -> latin1_string().
+
+write_latin1_string(S, Q) ->
+ [Q|write_string1(latin1, S, Q)].
+
+-spec write_string_as_latin1(String) -> latin1_string() when
+ String :: string().
+
+write_string_as_latin1(S) ->
+ write_string_as_latin1(S, $"). %"
+
+-spec write_string_as_latin1(string(), char()) -> latin1_string().
+
+write_string_as_latin1(S, Q) ->
+ [Q|write_string1(unicode_as_latin1, S, Q)].
write_string1(_,[], Q) ->
[Q];
@@ -344,7 +395,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
@@ -360,6 +415,11 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters.
C3 = (C band 7) + $0,
[$\\,C1,C2,C3|Tail].
+%%% There are two functions to write a Unicode character:
+%%% - they both escape control characters < 160;
+%%% - write_char() never escapes characters >= 160;
+%%% - write_char_as_latin1() also escapes characters >= 255.
+
%% write_char(Char) -> [char()].
%% Generate the list of characters needed to print a character constant.
%% Must special case SPACE, $\s, here.
@@ -368,35 +428,63 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters.
Char :: char().
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_char(C) when is_integer(C), C >= $\000 ->
+ [$$|string_char(unicode_as_unicode, C, -1, [])].
-write_unicode_char(Ch) when Ch =< 255 ->
- write_char(Ch);
-write_unicode_char(Uni) ->
- [$$|string_char(unicode,Uni, -1, [])].
+%% Backwards compatibility.
+write_unicode_char(C) ->
+ write_char(C).
-%% char_list(CharList)
-%% deep_char_list(CharList)
-%% Return true if CharList is a (possibly deep) list of characters, else
-%% false.
+-spec write_latin1_char(Latin1Char) -> latin1_string() when
+ Latin1Char :: unicode:latin1_char().
+
+write_latin1_char(Lat1) when is_integer(Lat1), Lat1 >= $\000, Lat1 =< $\377 ->
+ [$$|string_char(latin1, Lat1, -1, [])].
+
+-spec write_char_as_latin1(Char) -> latin1_string() when
+ Char :: char().
+
+write_char_as_latin1(Uni) when is_integer(Uni), Uni >= $\000 ->
+ [$$|string_char(unicode_as_latin1,Uni, -1, [])].
+
+%% latin1_char_list(CharList)
+%% deep_latin1_char_list(CharList)
+%% Return true if CharList is a (possibly deep) list of Latin-1
+%% characters, else false.
+
+-spec latin1_char_list(Term) -> boolean() when
+ Term :: term().
+
+latin1_char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
+ latin1_char_list(Cs);
+latin1_char_list([]) -> true;
+latin1_char_list(_) -> false. %Everything else is false
-spec char_list(Term) -> boolean() when
Term :: term().
-char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
+char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800;
+ is_integer(C), C > 16#DFFF, C < 16#FFFE;
+ is_integer(C), C > 16#FFFF, C =< 16#10FFFF ->
char_list(Cs);
char_list([]) -> true;
char_list(_) -> false. %Everything else is false
--spec unicode_char_list(term()) -> boolean().
+-spec deep_latin1_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;
- is_integer(C), C > 16#FFFF, C =< 16#10FFFF ->
- unicode_char_list(Cs);
-unicode_char_list([]) -> true;
-unicode_char_list(_) -> false. %Everything else is false
+deep_latin1_char_list(Cs) ->
+ deep_latin1_char_list(Cs, []).
+
+deep_latin1_char_list([C|Cs], More) when is_list(C) ->
+ deep_latin1_char_list(C, [Cs|More]);
+deep_latin1_char_list([C|Cs], More) when is_integer(C), C >= $\000, C =< $\377 ->
+ deep_latin1_char_list(Cs, More);
+deep_latin1_char_list([], [Cs|More]) ->
+ deep_latin1_char_list(Cs, More);
+deep_latin1_char_list([], []) -> true;
+deep_latin1_char_list(_, _More) -> %Everything else is false
+ false.
-spec deep_char_list(Term) -> boolean() when
Term :: term().
@@ -406,65 +494,72 @@ deep_char_list(Cs) ->
deep_char_list([C|Cs], More) when is_list(C) ->
deep_char_list(C, [Cs|More]);
-deep_char_list([C|Cs], More) when is_integer(C), C >= $\000, C =< $\377 ->
+deep_char_list([C|Cs], More)
+ when is_integer(C), C >= 0, C < 16#D800;
+ is_integer(C), C > 16#DFFF, C < 16#FFFE;
+ is_integer(C), C > 16#FFFF, C =< 16#10FFFF ->
deep_char_list(Cs, More);
deep_char_list([], [Cs|More]) ->
deep_char_list(Cs, More);
deep_char_list([], []) -> true;
-deep_char_list(_, _More) -> %Everything else is false
+deep_char_list(_, _More) -> %Everything else is false
false.
--spec deep_unicode_char_list(term()) -> boolean().
+deep_unicode_char_list(Term) ->
+ deep_char_list(Term).
-deep_unicode_char_list(Cs) ->
- deep_unicode_char_list(Cs, []).
+%% printable_latin1_list([Char]) -> boolean()
+%% Return true if CharList is a list of printable Latin1 characters, else
+%% false.
-deep_unicode_char_list([C|Cs], More) when is_list(C) ->
- deep_unicode_char_list(C, [Cs|More]);
-deep_unicode_char_list([C|Cs], More)
- when is_integer(C), C >= 0, C < 16#D800;
- is_integer(C), C > 16#DFFF, C < 16#FFFE;
- is_integer(C), C > 16#FFFF, C =< 16#10FFFF ->
- deep_unicode_char_list(Cs, More);
-deep_unicode_char_list([], [Cs|More]) ->
- deep_unicode_char_list(Cs, More);
-deep_unicode_char_list([], []) -> true;
-deep_unicode_char_list(_, _More) -> %Everything else is false
- false.
+-spec printable_latin1_list(Term) -> boolean() when
+ Term :: term().
+
+printable_latin1_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 ->
+ printable_latin1_list(Cs);
+printable_latin1_list([C|Cs]) when is_integer(C), C >= $\240, C =< $\377 ->
+ printable_latin1_list(Cs);
+printable_latin1_list([$\n|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([$\r|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([$\t|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([$\v|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([$\b|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([$\f|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([$\e|Cs]) -> printable_latin1_list(Cs);
+printable_latin1_list([]) -> true;
+printable_latin1_list(_) -> false. %Everything else is false
%% printable_list([Char]) -> boolean()
%% Return true if CharList is a list of printable characters, else
-%% false.
+%% false. The notion of printable in Unicode terms is somewhat floating.
+%% Everything that is not a control character and not invalid unicode
+%% will be considered printable.
+%% What the user has noted as printable characters is what actually
+%% specifies when this function will return true. If the VM is started
+%% with +pc latin1, only the latin1 range will be deemed as printable
+%% if on the other hand +pc unicode is given, all characters in the Unicode
+%% character set are deemed printable. latin1 is default.
-spec printable_list(Term) -> boolean() when
Term :: term().
-printable_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 ->
- printable_list(Cs);
-printable_list([C|Cs]) when is_integer(C), C >= $\240, C =< $\377 ->
- printable_list(Cs);
-printable_list([$\n|Cs]) -> printable_list(Cs);
-printable_list([$\r|Cs]) -> printable_list(Cs);
-printable_list([$\t|Cs]) -> printable_list(Cs);
-printable_list([$\v|Cs]) -> printable_list(Cs);
-printable_list([$\b|Cs]) -> printable_list(Cs);
-printable_list([$\f|Cs]) -> printable_list(Cs);
-printable_list([$\e|Cs]) -> printable_list(Cs);
-printable_list([]) -> true;
-printable_list(_) -> false. %Everything else is false
-
-%% printable_unicode_list([Char]) -> boolean()
-%% Return true if CharList is a list of printable characters, else
-%% false. The notion of printable in Unicode terms is somewhat floating.
-%% Everything that is not a control character and not invalid unicode
-%% will be considered printable.
+printable_list(L) ->
+ %% There will be more alternatives returns from io:printable range
+ %% in the future. To not have a catch-all clause is deliberate.
+ case io:printable_range() of
+ latin1 ->
+ printable_latin1_list(L);
+ unicode ->
+ printable_unicode_list(L)
+ end.
--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);
-printable_unicode_list([C|Cs])
- when is_integer(C), C >= 16#A0, C < 16#D800;
+printable_unicode_list([C|Cs])
+ when is_integer(C), C >= 16#A0, C < 16#D800;
is_integer(C), C > 16#DFFF, C < 16#FFFE;
is_integer(C), C > 16#FFFF, C =< 16#10FFFF ->
printable_unicode_list(Cs);
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index 49a00a4ec7..56e15a17ec 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-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,7 +22,7 @@
-export([fwrite/2,fwrite_g/1,indentation/2]).
-%% fwrite(Format, ArgList) -> [Char].
+%% fwrite(Format, ArgList) -> string().
%% Format the arguments in ArgList after string Format. Just generate
%% an error if there is an error in the arguments.
%%
@@ -58,14 +58,22 @@ collect_cseq(Fmt0, Args0) ->
{P,Fmt2,Args2} = precision(Fmt1, Args1),
{Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
{Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
- {C,As,Fmt5,Args5} = collect_cc(Fmt4, Args4),
- {{C,As,F,Ad,P,Pad,Encoding},Fmt5,Args5}.
+ {Strings,Fmt5,Args5} = strings(Fmt4, Args4),
+ {C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5),
+ {{C,As,F,Ad,P,Pad,Encoding,Strings},Fmt6,Args6}.
encoding([$t|Fmt],Args) ->
+ true = hd(Fmt) =/= $l,
{unicode,Fmt,Args};
encoding(Fmt,Args) ->
{latin1,Fmt,Args}.
+strings([$l|Fmt],Args) ->
+ true = hd(Fmt) =/= $t,
+ {false,Fmt,Args};
+strings(Fmt,Args) ->
+ {true,Fmt,Args}.
+
field_width([$-|Fmt0], Args0) ->
{F,Fmt,Args} = field_value(Fmt0, Args0),
field_width(-F, Fmt, Args);
@@ -128,18 +136,18 @@ collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}.
pcount(Cs) -> pcount(Cs, 0).
-pcount([{$p,_As,_F,_Ad,_P,_Pad,_Enc}|Cs], Acc) -> pcount(Cs, Acc+1);
-pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc}|Cs], Acc) -> pcount(Cs, Acc+1);
+pcount([{$p,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
+pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
pcount([_|Cs], Acc) -> pcount(Cs, Acc);
pcount([], Acc) -> Acc.
-%% build([Control], Pc, Indentation) -> [Char].
+%% build([Control], Pc, Indentation) -> string().
%% 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.
-build([{C,As,F,Ad,P,Pad,Enc}|Cs], Pc0, I) ->
- S = control(C, As, F, Ad, P, Pad, Enc, I),
+build([{C,As,F,Ad,P,Pad,Enc,Str}|Cs], Pc0, I) ->
+ S = control(C, As, F, Ad, P, Pad, Enc, Str, I),
Pc1 = decr_pc(C, Pc0),
if
Pc1 > 0 -> [S|build(Cs, Pc1, indentation(S, I))];
@@ -154,7 +162,7 @@ decr_pc($p, Pc) -> Pc - 1;
decr_pc($P, Pc) -> Pc - 1;
decr_pc(_, Pc) -> Pc.
-%% indentation([Char], Indentation) -> Indentation.
+%% indentation(String, Indentation) -> Indentation.
%% Calculate the indentation of the end of a string given its start
%% indentation. We assume tabs at 8 cols.
@@ -167,64 +175,63 @@ indentation([C|Cs], I) ->
indentation([], I) -> I.
%% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar,
-%% Indentation) ->
-%% [Char]
+%% Encoding, Indentation) -> String
%% 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) ->
+control($w, [A], F, Adj, P, Pad, _Enc, _Str, _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($W, [A,Depth], F, Adj, P, Pad, _Enc, _I) when is_integer(Depth) ->
+control($p, [A], F, Adj, P, Pad, Enc, Str, I) ->
+ print(A, -1, F, Adj, P, Pad, Enc, Str, I);
+control($W, [A,Depth], F, Adj, P, Pad, _Enc, _Str, _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($s, [A], F, Adj, P, Pad, _Enc, _I) when is_atom(A) ->
+control($P, [A,Depth], F, Adj, P, Pad, Enc, Str, I) when is_integer(Depth) ->
+ print(A, Depth, F, Adj, P, Pad, Enc, Str, I);
+control($s, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_atom(A) ->
string(atom_to_list(A), F, Adj, P, Pad);
-control($s, [L0], F, Adj, P, Pad, latin1, _I) ->
+control($s, [L0], F, Adj, P, Pad, latin1, _Str, _I) ->
L = iolist_to_chars(L0),
string(L, F, Adj, P, Pad);
-control($s, [L0], F, Adj, P, Pad, unicode, _I) ->
- L = unicode:characters_to_list(L0),
+control($s, [L0], F, Adj, P, Pad, unicode, _Str, _I) ->
+ L = cdata_to_chars(L0),
uniconv(string(L, F, Adj, P, Pad));
-control($e, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) ->
+control($e, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
fwrite_e(A, F, Adj, P, Pad);
-control($f, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) ->
+control($f, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
fwrite_f(A, F, Adj, P, Pad);
-control($g, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) ->
+control($g, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
fwrite_g(A, F, Adj, P, Pad);
-control($b, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($b, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
unprefixed_integer(A, F, Adj, base(P), Pad, true);
-control($B, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($B, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
unprefixed_integer(A, F, Adj, base(P), Pad, false);
-control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A),
+control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A),
is_atom(Prefix) ->
prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true);
-control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true);
-control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A),
+control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A),
is_atom(Prefix) ->
prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false);
-control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false);
-control($+, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($+, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
Base = base(P),
Prefix = [integer_to_list(Base), $#],
prefixed_integer(A, F, Adj, Base, Pad, Prefix, true);
-control($#, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($#, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
Base = base(P),
Prefix = [integer_to_list(Base), $#],
prefixed_integer(A, F, Adj, Base, Pad, Prefix, false);
-control($c, [A], F, Adj, P, Pad, unicode, _I) when is_integer(A) ->
+control($c, [A], F, Adj, P, Pad, unicode, _Str, _I) when is_integer(A) ->
char(A, F, Adj, P, Pad);
-control($c, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) ->
+control($c, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
char(A band 255, F, Adj, P, Pad);
-control($~, [], F, Adj, P, Pad, _Enc, _I) -> char($~, F, Adj, P, Pad);
-control($n, [], F, Adj, P, Pad, _Enc, _I) -> newline(F, Adj, P, Pad);
-control($i, [_A], _F, _Adj, _P, _Pad, _Enc, _I) -> [].
+control($~, [], F, Adj, P, Pad, _Enc, _Str, _I) -> char($~, F, Adj, P, Pad);
+control($n, [], F, Adj, P, Pad, _Enc, _Str, _I) -> newline(F, Adj, P, Pad);
+control($i, [_A], _F, _Adj, _P, _Pad, _Enc, _Str, _I) -> [].
-ifdef(UNICODE_AS_BINARIES).
uniconv(C) ->
@@ -256,13 +263,21 @@ 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, Str, I) ->
+ print(T, D, 80, Adj, P, Pad, E, Str, I);
+print(T, D, F, Adj, none, Pad, E, Str, I) ->
+ print(T, D, F, Adj, I+1, Pad, E, Str, I);
+print(T, D, F, right, P, _Pad, Enc, Str, _I) ->
+ Options = [{column, P},
+ {line_length, F},
+ {depth, D},
+ {encoding, Enc},
+ {strings, Str}],
+ io_lib_pretty:print(T, Options).
%% fwrite_e(Float, Field, Adjust, Precision, PadChar)
@@ -554,6 +569,25 @@ iolist_to_chars([]) ->
iolist_to_chars(B) when is_binary(B) ->
binary_to_list(B).
+%% cdata() :: clist() | cbinary()
+%% clist() :: maybe_improper_list(char() | cbinary() | clist(),
+%% cbinary() | nil())
+%% cbinary() :: unicode:unicode_binary() | unicode:latin1_binary()
+
+%% cdata_to_chars(cdata()) -> io_lib:deep_char_list()
+
+cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 ->
+ [C | cdata_to_chars(Cs)];
+cdata_to_chars([I|Cs]) ->
+ [cdata_to_chars(I) | cdata_to_chars(Cs)];
+cdata_to_chars([]) ->
+ [];
+cdata_to_chars(B) when is_binary(B) ->
+ case catch unicode:characters_to_list(B) of
+ L when is_list(L) -> L;
+ _ -> binary_to_list(B)
+ end.
+
%% string(String, Field, Adjust, Precision, PadChar)
string(S, none, _Adj, none, _Pad) -> S;
@@ -608,7 +642,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) -> string().
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..92a34995b8 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.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
@@ -22,7 +22,7 @@
-export([fread/2,fread/3]).
--import(lists, [reverse/1,reverse/2]).
+-import(lists, [reverse/1]).
-define(is_whitespace(C),
((C) =:= $\s orelse (C) =:= $\t
@@ -43,7 +43,7 @@
| {'done', Result, LeftOverChars :: string()},
Result :: {'ok', InputList :: io_lib:chars()}
| 'eof'
- | {'error', What :: term()}.
+ | {'error', {'read', 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..7637ad7a3d 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-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
@@ -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 option() :: {column, column()}
+ | {line_length, line_length()}
+ | {depth, depth()}
+ | {max_chars, max_chars()}
+ | {record_print_fun, rec_print_fun()}
+ | {strings, boolean()}
+ | {encoding, latin1 | utf8 | unicode}.
+-type options() :: [option()].
+
+-spec print(term(), rec_print_fun()) -> chars();
+ (term(), options()) -> 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()),
+ Strings = proplists:get_value(strings, Options, true),
+ print(Term, Col, Ll, D, M, RecDefFun, Encoding, Strings);
print(Term, RecDefFun) ->
print(Term, -1, RecDefFun).
+-spec print(term(), depth(), rec_print_fun()) -> chars().
+
print(Term, Depth, RecDefFun) ->
print(Term, 1, 80, Depth, RecDefFun).
+-spec print(term(), column(), line_length(), depth()) -> 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, true).
+-spec print(term(), column(), line_length(), depth(), rec_print_fun()) ->
+ 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().
+
+print(Term, Col, Ll, D, M, RecDefFun) ->
+ print(Term, Col, Ll, D, M, RecDefFun, latin1, true).
+
+print(_, _, _, 0, _M, _RF, _Enc, _Str) -> "...";
+print(Term, Col, Ll, D, M, RecDefFun, Enc, Str) when Col =< 0 ->
+ print(Term, 1, Ll, D, M, RecDefFun, Enc, Str);
+print(Term, Col, Ll, D, M0, RecDefFun, Enc, Str) when is_tuple(Term);
+ is_list(Term);
+ is_bitstring(Term) ->
+ If = {_S, Len} = print_length(Term, D, RecDefFun, Enc, Str),
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, _Str) ->
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, _Str) ->
{"[]", 2};
-print_length({}, _D, _RF) ->
+print_length({}, _D, _RF, _Enc, _Str) ->
{"{}", 2};
-print_length(List, D, RF) when is_list(List) ->
- case printable_list(List, D) of
+print_length(List, D, RF, Enc, Str) when is_list(List) ->
+ case Str andalso 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, Str)
end;
-print_length(Fun, _D, _RF) when is_function(Fun) ->
+print_length(Fun, _D, _RF, _Enc, _Str) 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, Str) 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, Str);
RDefs ->
- print_length_record(R, D, RF, RDefs)
+ print_length_record(R, D, RF, RDefs, Enc, Str)
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, Str) when is_tuple(Tuple) ->
+ print_length_tuple(Tuple, D, RF, Enc, Str);
+print_length(<<>>, _D, _RF, _Enc, _Str) ->
{"<<>>", 4};
-print_length(<<_/bitstring>>, 1, _RF) ->
+print_length(<<_/bitstring>>, 1, _RF, _Enc, _Str) ->
{"<<...>>", 7};
-print_length(<<_/bitstring>>=Bin, D, _RF) ->
+print_length(<<_/bitstring>>=Bin, D, _RF, Enc, Str) ->
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 Str andalso 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_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_string(Prefix, $"), %"
+ {[$<,$<, S | "/utf8...>>"], 12 + length(S)};
false ->
S = io_lib:write(Bin, D),
{{bin,S}, iolist_size(S)}
@@ -346,51 +385,53 @@ 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, _Str) ->
S = io_lib:write(Term),
- {S, iolist_size(S)}.
+ {S, lists:flatlength(S)}.
-print_length_tuple(_Tuple, 1, _RF) ->
+print_length_tuple(_Tuple, 1, _RF, _Enc, _Str) ->
{"{...}", 5};
-print_length_tuple(Tuple, D, RF) ->
- L = print_length_list1(tuple_to_list(Tuple), D, RF),
+print_length_tuple(Tuple, D, RF, Enc, Str) ->
+ L = print_length_list1(tuple_to_list(Tuple), D, RF, Enc, Str),
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, _Str) ->
{"{...}", 5};
-print_length_record(Tuple, D, RF, RDefs) ->
+print_length_record(Tuple, D, RF, RDefs, Enc, Str) ->
Name = [$# | io_lib:write_atom(element(1, Tuple))],
NameL = length(Name),
- L = print_length_fields(RDefs, D - 1, tl(tuple_to_list(Tuple)), RF),
+ Elements = tl(tuple_to_list(Tuple)),
+ L = print_length_fields(RDefs, D - 1, Elements, RF, Enc, Str),
{{record, [{Name,NameL} | L]}, list_length(L, NameL + 2)}.
-print_length_fields([], _D, [], _RF) ->
+print_length_fields([], _D, [], _RF, _Enc, _Str) ->
[];
-print_length_fields(_, 1, _, _RF) ->
+print_length_fields(_, 1, _, _RF, _Enc, _Str) ->
{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, Str) ->
+ [print_length_field(Def, D - 1, E, RF, Enc, Str) |
+ print_length_fields(Defs, D - 1, Es, RF, Enc, Str)].
-print_length_field(Def, D, E, RF) ->
+print_length_field(Def, D, E, RF, Enc, Str) ->
Name = io_lib:write_atom(Def),
- {S, L} = print_length(E, D, RF),
+ {S, L} = print_length(E, D, RF, Enc, Str),
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, Str) ->
+ L = print_length_list1(List, D, RF, Enc, Str),
{{list, L}, list_length(L, 2)}.
-print_length_list1([], _D, _RF) ->
+print_length_list1([], _D, _RF, _Enc, _Str) ->
[];
-print_length_list1(_, 1, _RF) ->
+print_length_list1(_, 1, _RF, _Enc, _Str) ->
{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, Str) ->
+ [print_length(E, D - 1, RF, Enc, Str) |
+ print_length_list1(Es, D - 1, RF, Enc, Str)];
+print_length_list1(E, D, RF, Enc, Str) ->
+ print_length(E, D - 1, RF, Enc, Str).
list_length([], Acc) ->
Acc;
@@ -409,51 +450,53 @@ 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) ->
+printable_list(L, _D, latin1) ->
+ io_lib:printable_latin1_list(L);
+printable_list(L, _D, _Uni) ->
io_lib:printable_list(L).
-%% Truncated lists could break some existing code.
-% printable_list(L, D) ->
-% Len = ?CHARS * (D - 1),
-% case printable_list1(L, Len) of
-% all ->
-% true;
-% N when is_integer(N), Len - N >= D - 1 ->
-% {L1, _} = lists:split(Len - N, L),
-% {true, L1};
-% N when is_integer(N) ->
-% 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, Len, 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, latin1) ->
N = erlang:min(20, Len),
L = binary_to_list(Bin, 1, N),
- case printable_list1(L, N) of
+ case printable_latin1_list(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 valid_utf8(Bin,Len) of
+ true ->
+ case printable_unicode(Bin, Len, [], io:printable_range()) 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;
+ false ->
+ printable_bin(Bin, Len, D, latin1)
end.
printable_bin1(_Bin, _Start, 0) ->
@@ -461,7 +504,7 @@ printable_bin1(_Bin, _Start, 0) ->
printable_bin1(Bin, Start, Len) ->
N = erlang:min(10000, Len),
L = binary_to_list(Bin, Start, Start + N - 1),
- case printable_list1(L, N) of
+ case printable_latin1_list(L, N) of
all ->
printable_bin1(Bin, Start + N, Len - N);
NC when is_integer(NC) ->
@@ -469,20 +512,60 @@ printable_bin1(Bin, Start, Len) ->
end.
%% -> all | integer() >=0. Adopted from io_lib.erl.
-% printable_list1([_ | _], 0) -> 0;
-printable_list1([C | Cs], N) when is_integer(C), C >= $\s, C =< $~ ->
- printable_list1(Cs, N - 1);
-printable_list1([C | Cs], N) when is_integer(C), C >= $\240, C =< $\377 ->
- printable_list1(Cs, N - 1);
-printable_list1([$\n | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([$\r | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([$\t | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([$\v | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([$\b | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([$\f | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([$\e | Cs], N) -> printable_list1(Cs, N - 1);
-printable_list1([], _) -> all;
-printable_list1(_, N) -> N.
+% printable_latin1_list([_ | _], 0) -> 0;
+printable_latin1_list([C | Cs], N) when C >= $\s, C =< $~ ->
+ printable_latin1_list(Cs, N - 1);
+printable_latin1_list([C | Cs], N) when C >= $\240, C =< $\377 ->
+ printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\n | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\r | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\t | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\v | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\b | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\f | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([$\e | Cs], N) -> printable_latin1_list(Cs, N - 1);
+printable_latin1_list([], _) -> all;
+printable_latin1_list(_, N) -> N.
+
+valid_utf8(<<>>,_) ->
+ true;
+valid_utf8(_,0) ->
+ true;
+valid_utf8(<<_/utf8, R/binary>>,N) ->
+ valid_utf8(R,N-1);
+valid_utf8(_,_) ->
+ false.
+
+printable_unicode(<<C/utf8, R/binary>>=Bin, I, L, Range) when I > 0 ->
+ case printable_char(C,Range) of
+ true ->
+ printable_unicode(R, I - 1, [C | L],Range);
+ false ->
+ {I, Bin, lists:reverse(L)}
+ end;
+printable_unicode(Bin, I, L,_) ->
+ {I, Bin, lists:reverse(L)}.
+
+printable_char($\n,_) -> true;
+printable_char($\r,_) -> true;
+printable_char($\t,_) -> true;
+printable_char($\v,_) -> true;
+printable_char($\b,_) -> true;
+printable_char($\f,_) -> true;
+printable_char($\e,_) -> true;
+printable_char(C,latin1) ->
+ C >= $\s andalso C =< $~ orelse
+ C >= 16#A0 andalso C =< 16#FF;
+printable_char(C,unicode) ->
+ C >= $\s andalso C =< $~ orelse
+ C >= 16#A0 andalso C < 16#D800 orelse
+ C > 16#DFFF andalso C < 16#FFFE orelse
+ C > 16#FFFF andalso C =< 16#10FFFF.
+
+write_string(S, latin1) ->
+ io_lib:write_latin1_string(S, $"); %"
+write_string(S, _Uni) ->
+ io_lib:write_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..8351376691 100644
--- a/lib/stdlib/src/lib.erl
+++ b/lib/stdlib/src/lib.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
@@ -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'.
@@ -43,7 +44,7 @@ flush_receive() ->
Args :: [term()].
error_message(Format, Args) ->
- io:format(<<"** ~s **\n">>, [io_lib:format(Format, Args)]).
+ io:format(<<"** ~ts **\n">>, [io_lib:format(Format, Args)]).
%% Return the name of the script that starts (this) erlang
%%
@@ -83,10 +84,14 @@ sendw(To, Msg) ->
%% eval_str(InStr) -> {ok, OutStr} | {error, ErrStr'}
%% InStr must represent a body
+%% Note: If InStr is a binary it has to be a Latin-1 string.
+%% If you have a UTF-8 encoded binary you have to call
+%% unicode:characters_to_list/1 before the call to eval_str().
-define(result(F,D), lists:flatten(io_lib:format(F, D))).
--spec eval_str(string() | binary()) -> {'ok', string()} | {'error', string()}.
+-spec eval_str(string() | unicode:latin1_binary()) ->
+ {'ok', string()} | {'error', string()}.
eval_str(Str) when is_list(Str) ->
case erl_scan:tokens([], Str, 0) of
@@ -104,12 +109,12 @@ eval_str(Str) when is_list(Str) ->
{error, ?result("*** eval: ~p", [Other])}
end;
{error, {_Line, Mod, Args}} ->
- Msg = ?result("*** ~s",[Mod:format_error(Args)]),
+ Msg = ?result("*** ~ts",[Mod:format_error(Args)]),
{error, Msg}
end;
false ->
{error, ?result("Non-white space found after "
- "end-of-form :~s", [Rest])}
+ "end-of-form :~ts", [Rest])}
end
end;
eval_str(Bin) when is_binary(Bin) ->
@@ -128,32 +133,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 +226,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 +316,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 +361,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 +385,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 +400,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_latin1_list(As);
+printable_list(_, As) ->
+ io_lib:printable_list(As).
+
mfa_to_string(M, F, A) ->
io_lib:fwrite(<<"~s/~w">>, [mf_to_string({M, F}, A), A]).
@@ -472,3 +502,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 e73c087753..961c060019 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.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
@@ -33,9 +33,6 @@
keysort/2, keymerge/3, rkeymerge/3, rukeymerge/3,
ukeysort/2, ukeymerge/3, keymap/3]).
-%% Bifs: member/2, reverse/2
-%% Bifs: keymember/3, keysearch/3, keyfind/3
-
-export([merge/3, rmerge/3, sort/2, umerge/3, rumerge/3, usort/2]).
-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,
@@ -43,6 +40,60 @@
mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2,
split/2]).
+%%% BIFs
+-export([keyfind/3, keymember/3, keysearch/3, member/2, reverse/2]).
+
+%% Shadowed by erl_bif_types: lists:keyfind/3
+-spec keyfind(Key, N, TupleList) -> Tuple | false when
+ Key :: term(),
+ N :: pos_integer(),
+ TupleList :: [Tuple],
+ Tuple :: tuple().
+
+keyfind(_, _, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:keymember/3
+-spec keymember(Key, N, TupleList) -> boolean() when
+ Key :: term(),
+ N :: pos_integer(),
+ TupleList :: [Tuple],
+ Tuple :: tuple().
+
+keymember(_, _, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:keysearch/3
+-spec keysearch(Key, N, TupleList) -> {value, Tuple} | false when
+ Key :: term(),
+ N :: pos_integer(),
+ TupleList :: [Tuple],
+ Tuple :: tuple().
+
+keysearch(_, _, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:member/2
+-spec member(Elem, List) -> boolean() when
+ Elem :: T,
+ List :: [T],
+ T :: term().
+
+member(_, _) ->
+ erlang:nif_error(undef).
+
+%% Shadowed by erl_bif_types: lists:reverse/2
+-spec reverse(List1, Tail) -> List2 when
+ List1 :: [T],
+ Tail :: term(),
+ List2 :: [T],
+ T :: term().
+
+reverse(_, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%% member(X, L) -> (true | false)
%% test if X is a member of the list L
%% Now a BIF!
@@ -84,7 +135,7 @@ append([]) -> [].
subtract(L1, L2) -> L1 -- L2.
-%% reverse(L) reverse all elements in the list L. Is now a BIF!
+%% reverse(L) reverse all elements in the list L. reverse/2 is now a BIF!
-spec reverse(List1) -> List2 when
List1 :: [T],
@@ -581,6 +632,7 @@ flatlength([_|T], L) ->
flatlength([], L) -> L.
%% keymember(Key, Index, [Tuple]) Now a BIF!
+%% keyfind(Key, Index, [Tuple]) A BIF!
%% keysearch(Key, Index, [Tuple]) Now a BIF!
%% keydelete(Key, Index, [Tuple])
%% keyreplace(Key, Index, [Tuple], NewTuple)
@@ -1126,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/math.erl b/lib/stdlib/src/math.erl
index b2ea6195c5..c3fb684ec3 100644
--- a/lib/stdlib/src/math.erl
+++ b/lib/stdlib/src/math.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
@@ -20,6 +20,116 @@
-export([pi/0]).
+%%% BIFs
+
+-export([sin/1, cos/1, tan/1, asin/1, acos/1, atan/1, atan2/2, sinh/1,
+ cosh/1, tanh/1, asinh/1, acosh/1, atanh/1, exp/1, log/1,
+ log10/1, pow/2, sqrt/1, erf/1, erfc/1]).
+
+-spec acos(X) -> float() when
+ X :: number().
+acos(_) ->
+ erlang:nif_error(undef).
+
+-spec acosh(X) -> float() when
+ X :: number().
+acosh(_) ->
+ erlang:nif_error(undef).
+
+-spec asin(X) -> float() when
+ X :: number().
+asin(_) ->
+ erlang:nif_error(undef).
+
+-spec asinh(X) -> float() when
+ X :: number().
+asinh(_) ->
+ erlang:nif_error(undef).
+
+-spec atan(X) -> float() when
+ X :: number().
+atan(_) ->
+ erlang:nif_error(undef).
+
+-spec atan2(X, Y) -> float() when
+ X :: number(),
+ Y :: number().
+atan2(_, _) ->
+ erlang:nif_error(undef).
+
+-spec atanh(X) -> float() when
+ X :: number().
+atanh(_) ->
+ erlang:nif_error(undef).
+
+-spec cos(X) -> float() when
+ X :: number().
+cos(_) ->
+ erlang:nif_error(undef).
+
+-spec cosh(X) -> float() when
+ X :: number().
+cosh(_) ->
+ erlang:nif_error(undef).
+
+-spec erf(X) -> float() when
+ X :: number().
+erf(_) ->
+ erlang:nif_error(undef).
+
+-spec erfc(X) -> float() when
+ X :: number().
+erfc(_) ->
+ erlang:nif_error(undef).
+
+-spec exp(X) -> float() when
+ X :: number().
+exp(_) ->
+ erlang:nif_error(undef).
+
+-spec log(X) -> float() when
+ X :: number().
+log(_) ->
+ erlang:nif_error(undef).
+
+-spec log10(X) -> float() when
+ X :: number().
+log10(_) ->
+ erlang:nif_error(undef).
+
+-spec pow(X, Y) -> float() when
+ X :: number(),
+ Y :: number().
+pow(_, _) ->
+ erlang:nif_error(undef).
+
+-spec sin(X) -> float() when
+ X :: number().
+sin(_) ->
+ erlang:nif_error(undef).
+
+-spec sinh(X) -> float() when
+ X :: number().
+sinh(_) ->
+ erlang:nif_error(undef).
+
+-spec sqrt(X) -> float() when
+ X :: number().
+sqrt(_) ->
+ erlang:nif_error(undef).
+
+-spec tan(X) -> float() when
+ X :: number().
+tan(_) ->
+ erlang:nif_error(undef).
+
+-spec tanh(X) -> float() when
+ X :: number().
+tanh(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
-spec pi() -> float().
pi() -> 3.1415926535897932.
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 4389fd457c..4e2ce39ec2 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -100,7 +100,7 @@ format_error({?ERR_GUARDREMOTECALL, Module, Name, Arithy}) ->
[Module,Name,Arithy]));
format_error({?ERR_GUARDELEMENT, Str}) ->
lists:flatten(
- io_lib:format("the language element ~s (in guard) cannot be translated "
+ io_lib:format("the language element ~ts (in guard) cannot be translated "
"into match_spec", [Str]));
format_error({?ERR_GUARDBINCONSTRUCT, Var}) ->
lists:flatten(
@@ -126,7 +126,7 @@ format_error({?ERR_BODYREMOTECALL, Module, Name, Arithy}) ->
[Module,Name,Arithy]));
format_error({?ERR_BODYELEMENT, Str}) ->
lists:flatten(
- io_lib:format("the language element ~s (in body) cannot be translated "
+ io_lib:format("the language element ~ts (in body) cannot be translated "
"into match_spec", [Str]));
format_error({?ERR_BODYBINCONSTRUCT, Var}) ->
lists:flatten(
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index b9fbef9ed0..a4f4035c79 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,18 +347,57 @@ 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, _, _) ->
- {deprecated,"the inviso application has been deprecated and will be removed in R16"};
+ {removed,"the inviso application was removed in R16"};
%% Added in R15B01.
obsolete_1(gs, _, _) ->
- {deprecated,"the gs application has been deprecated and will be removed in R16; use the wx application instead"};
+ {deprecated,"the gs application has been deprecated and will be removed in R17; use the wx application instead"};
obsolete_1(ssh, sign_data, 2) ->
{deprecated,"deprecated (will be removed in R16A); use public_key:pem_decode/1, public_key:pem_entry_decode/1 "
"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..1eb6fc2e86 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-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
@@ -29,7 +29,8 @@
start/3, start/4, start/5, start_link/3, start_link/4, start_link/5,
hibernate/3,
init_ack/1, init_ack/2,
- init_p/3,init_p/5,format/1,initial_call/1,translate_initial_call/1]).
+ init_p/3,init_p/5,format/1,format/2,initial_call/1,
+ translate_initial_call/1]).
%% Internal exports.
-export([wake_up/3]).
@@ -184,6 +185,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 +282,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 +294,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 +342,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) ->
@@ -664,34 +693,41 @@ check(Res) -> Res.
-spec format(CrashReport) -> string() when
CrashReport :: [term()].
-
-format([OwnReport,LinkReport]) ->
- OwnFormat = format_report(OwnReport),
- LinkFormat = format_report(LinkReport),
- S = io_lib:format(" crasher:~n~s neighbours:~n~s",[OwnFormat,LinkFormat]),
- lists:flatten(S).
-
-format_report(Rep) when is_list(Rep) ->
- format_rep(Rep);
-format_report(Rep) ->
- io_lib:format("~p~n", [Rep]).
-
-format_rep([{initial_call,InitialCall}|Rep]) ->
- [format_mfa(InitialCall)|format_rep(Rep)];
-format_rep([{error_info,{Class,Reason,StackTrace}}|Rep]) ->
- [format_exception(Class, Reason, StackTrace)|format_rep(Rep)];
-format_rep([{Tag,Data}|Rep]) ->
- [format_tag(Tag, Data)|format_rep(Rep)];
-format_rep(_) ->
+format(CrashReport) ->
+ format(CrashReport, latin1).
+
+-spec format(CrashReport, Encoding) -> string() when
+ CrashReport :: [term()],
+ Encoding :: latin1 | unicode | utf8.
+
+format([OwnReport,LinkReport], Encoding) ->
+ OwnFormat = format_report(OwnReport, Encoding),
+ LinkFormat = format_report(LinkReport, Encoding),
+ Str = io_lib:format(" crasher:~n~ts neighbours:~n~ts",
+ [OwnFormat, LinkFormat]),
+ lists:flatten(Str).
+
+format_report(Rep, Enc) when is_list(Rep) ->
+ format_rep(Rep,Enc);
+format_report(Rep, Enc) ->
+ io_lib:format("~"++modifier(Enc)++"p~n", [Rep]).
+
+format_rep([{initial_call,InitialCall}|Rep], Enc) ->
+ [format_mfa(InitialCall)|format_rep(Rep, Enc)];
+format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Enc) ->
+ [format_exception(Class, Reason, StackTrace, Enc)|format_rep(Rep, Enc)];
+format_rep([{Tag,Data}|Rep], Enc) ->
+ [format_tag(Tag, Data)|format_rep(Rep, Enc)];
+format_rep(_, _Enc) ->
[].
-format_exception(Class, Reason, StackTrace) ->
- PF = pp_fun(),
+format_exception(Class, Reason, StackTrace, Enc) ->
+ PF = pp_fun(Enc),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
%% EI = " exception: ",
EI = " ",
[EI, lib:format_exception(1+length(EI), Class, Reason,
- StackTrace, StackFun, PF), "\n"].
+ StackTrace, StackFun, PF, Enc), "\n"].
format_mfa({M,F,Args}=StartF) ->
try
@@ -703,10 +739,14 @@ format_mfa({M,F,Args}=StartF) ->
format_tag(initial_call, StartF)
end.
-pp_fun() ->
+pp_fun(Enc) ->
+ P = modifier(Enc) ++ "p",
fun(Term, I) ->
- io_lib:format("~." ++ integer_to_list(I) ++ "p", [Term])
+ io_lib:format("~." ++ integer_to_list(I) ++ P, [Term])
end.
format_tag(Tag, Data) ->
io_lib:format(" ~p: ~80.18p~n", [Tag, Data]).
+
+modifier(latin1) -> "";
+modifier(_) -> "t".
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index e3eda5d932..634724019f 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -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..48f6622565 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-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
@@ -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.
@@ -386,25 +386,25 @@ format_error(nomatch_pattern) ->
format_error(nomatch_filter) ->
io_lib:format("filter evaluates to 'false'", []);
format_error({Line, Mod, Reason}) when is_integer(Line) ->
- io_lib:format("~p: ~s~n",
+ io_lib:format("~p: ~ts~n",
[Line, lists:flatten(Mod:format_error(Reason))]);
%% file_sorter errors
format_error({bad_object, FileName}) ->
- io_lib:format("the temporary file \"~s\" holding answers is corrupt",
+ io_lib:format("the temporary file \"~ts\" holding answers is corrupt",
[FileName]);
format_error(bad_object) ->
io_lib:format("the keys could not be extracted from some term", []);
format_error({file_error, FileName, Reason}) ->
- io_lib:format("\"~s\": ~p~n",[FileName, file:format_error(Reason)]);
+ io_lib:format("\"~ts\": ~tp~n",[FileName, file:format_error(Reason)]);
format_error({premature_eof, FileName}) ->
- io_lib:format("\"~s\": end-of-file was encountered inside some binary term",
+ io_lib:format("\"~ts\": end-of-file was encountered inside some binary term",
[FileName]);
format_error({tmpdir_usage, Why}) ->
io_lib:format("temporary file was needed for ~w~n", [Why]);
format_error({error, Module, Reason}) ->
Module:format_error(Reason);
format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
-spec(info(QH) -> Info when
QH :: query_handle_or_list(),
@@ -3709,7 +3709,7 @@ maybe_error_logger(Name, Why) ->
Trimmer = fun(M, _F, _A) -> M =:= erl_eval end,
Formater = fun(Term, I) -> io_lib:print(Term, I, 80, -1) end,
X = lib:format_stacktrace(1, Stacktrace, Trimmer, Formater),
- error_logger:Name("qlc: temporary file was needed for ~w\n~s\n",
+ error_logger:Name("qlc: temporary file was needed for ~w\n~ts\n",
[Why, lists:flatten(X)]).
expand_stacktrace() ->
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index 21504d707b..26bc4d1bdf 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.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
@@ -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.
@@ -217,7 +214,7 @@ compile_messages(Forms, FormsNoShadows, Options, State) ->
end,
{_,BGens} = qual_fold(BGenF, [], [], FormsNoShadows, State),
GenForm = used_genvar_check(FormsNoShadows, State),
- ?DEBUG("GenForm = ~s~n", [catch erl_pp:form(GenForm)]),
+ ?DEBUG("GenForm = ~ts~n", [catch erl_pp:form(GenForm)]),
WarnFun = fun(Id, LC, A) -> {tag_lines(LC, get_lcid_no(Id)), A} end,
{WForms,ok} = qlc_mapfold(WarnFun, ok, Forms, State),
{Es,Ws} = compile_forms(WForms ++ [GenForm], Options),
@@ -340,7 +337,7 @@ compile_errors(FormsNoShadows) ->
{[], _Warnings} ->
[];
{Errors, _Warnings} ->
- ?DEBUG("got errors ~p~n", [Errors]),
+ ?DEBUG("got errors ~tp~n", [Errors]),
lists:flatmap(fun({_File,Es}) -> Es end, Errors)
end.
@@ -2186,7 +2183,7 @@ try_ms(E, P, Fltr, State) ->
{function,L,foo,0,[{clause,L,[],[],[MS0]}]} = lists:last(X),
MS = erl_parse:normalise(var2const(MS0)),
XMS = ets:match_spec_compile(MS),
- true = is_binary(XMS),
+ true = ets:is_compiled_ms(XMS),
{ok, MS, MS0}
end of
{'EXIT', _Reason} ->
@@ -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...
@@ -2752,7 +2742,7 @@ family(L) ->
display_forms(Forms) ->
io:format("Forms ***~n"),
lists:foreach(fun(Form) ->
- io:format("~s~n", [catch erl_pp:form(Form)])
+ io:format("~ts~n", [catch erl_pp:form(Form)])
end, Forms),
io:format("End Forms ***~n").
-else.
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index afe917b151..4bbf5de8a5 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.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
@@ -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 246d535943..c5109ec455 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.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
@@ -30,11 +30,65 @@
| {newline, nl_spec()}| bsr_anycrlf
| bsr_unicode.
-%% Emulator builtins in this module:
-%% re:compile/1
-%% re:compile/2
-%% re:run/2
-%% re:run/3
+%%% BIFs
+
+-export([compile/1, compile/2, run/2, run/3]).
+
+-spec compile(Regexp) -> {ok, MP} | {error, ErrSpec} when
+ Regexp :: iodata(),
+ MP :: mp(),
+ ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}.
+
+compile(_) ->
+ erlang:nif_error(undef).
+
+-spec compile(Regexp, Options) -> {ok, MP} | {error, ErrSpec} when
+ Regexp :: iodata() | unicode:charlist(),
+ Options :: [Option],
+ Option :: compile_option(),
+ MP :: mp(),
+ ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}.
+
+compile(_, _) ->
+ erlang:nif_error(undef).
+
+-spec run(Subject, RE) -> {match, Captured} | nomatch when
+ Subject :: iodata() | unicode:charlist(),
+ RE :: mp() | iodata(),
+ Captured :: [CaptureData],
+ CaptureData :: {integer(), integer()}.
+
+run(_, _) ->
+ erlang:nif_error(undef).
+
+-spec run(Subject, RE, Options) -> {match, Captured} |
+ match |
+ nomatch when
+ Subject :: iodata() | unicode:charlist(),
+ RE :: mp() | iodata() | unicode:charlist(),
+ Options :: [Option],
+ Option :: anchored | global | notbol | noteol | notempty
+ | {offset, non_neg_integer()} |
+ {newline, NLSpec :: nl_spec()} |
+ bsr_anycrlf | bsr_unicode | {capture, ValueSpec} |
+ {capture, ValueSpec, Type} | CompileOpt,
+ Type :: index | list | binary,
+ ValueSpec :: all | all_but_first | first | none | ValueList,
+ ValueList :: [ValueID],
+ ValueID :: integer() | string() | atom(),
+ CompileOpt :: compile_option(),
+ Captured :: [CaptureData] | [[CaptureData]],
+ CaptureData :: {integer(), integer()}
+ | ListConversionData
+ | binary(),
+ ListConversionData :: string()
+ | {error, string(), binary()}
+ | {incomplete, string(), binary()}.
+
+run(_, _, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
-spec split(Subject, RE) -> SplitList when
Subject :: iodata() | unicode:charlist(),
@@ -355,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..c6c706c3a7 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
@@ -22,7 +22,7 @@
-export([whereis_evaluator/0, whereis_evaluator/1]).
-export([start_restricted/1, stop_restricted/0]).
-export([local_allowed/3, non_local_allowed/3]).
--export([prompt_func/1]).
+-export([prompt_func/1, strings/1]).
-define(LINEMAX, 30).
-define(CHAR_MAX, 60).
@@ -30,6 +30,7 @@
-define(DEF_RESULTS, 20).
-define(DEF_CATCH_EXCEPTION, false).
-define(DEF_PROMPT_FUNC, default).
+-define(DEF_STRINGS, true).
-define(RECORDS, shell_records).
@@ -128,7 +129,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: ~tp\n",
[RShMod,What]))),
Error
end.
@@ -139,16 +140,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 +174,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
@@ -204,7 +186,7 @@ server(StartSync) ->
%% Check if we're in user restricted mode.
RShErr =
case application:get_env(stdlib, restricted_shell) of
- {ok,RShMod} ->
+ {ok,RShMod} when is_atom(RShMod) ->
io:fwrite(<<"Restricted ">>, []),
case code:ensure_loaded(RShMod) of
{module,RShMod} ->
@@ -212,6 +194,8 @@ server(StartSync) ->
{error,What} ->
{RShMod,What}
end;
+ {ok, Term} ->
+ {Term,not_an_atom};
undefined ->
undefined
end,
@@ -230,9 +214,9 @@ 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: ~tp.\n"
+ "Only the commands q() and init:stop() will be allowed!\n"),
+ [RShMod2,What2]),
application:set_env(stdlib, restricted_shell, ?MODULE)
end,
@@ -244,7 +228,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 +247,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 +261,40 @@ 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)
+ of
+ {ok,Toks,_EndPos} ->
+ erl_parse:parse_exprs(Toks);
+ {eof,_EndPos} ->
+ eof;
+ {error,ErrorInfo,_EndPos} ->
+ %% Skip the rest of the line:
+ Opts = io:getopts(),
+ TmpOpts = lists:keyreplace(echo, 1, Opts,
+ {echo, false}),
+ _ = io:setopts(TmpOpts),
+ _ = io:get_line(''),
+ _ = io:setopts(Opts),
+ {error,ErrorInfo};
+ Else ->
+ Else
+ end
+ )
+ end,
Pid = spawn_link(Parse),
get_command1(Pid, Eval, Bs, RT, Ds).
@@ -337,7 +341,7 @@ get_prompt_func() ->
end.
bad_prompt_func(M) ->
- fwrite_severity(benign, <<"Bad prompt function: ~p">>, [M]).
+ fwrite_severity(benign, "Bad prompt function: ~tp", [M]).
default_prompt(N) ->
%% Don't bother flattening the list irrespective of what the
@@ -453,7 +457,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 +523,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 +575,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 +677,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 +760,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)).
@@ -836,16 +843,6 @@ not_restricted(h, []) ->
true;
not_restricted(b, []) ->
true;
-not_restricted(which, [_]) ->
- true;
-not_restricted(import, [_]) ->
- true;
-not_restricted(import_all, [_]) ->
- true;
-not_restricted(use, [_]) ->
- true;
-not_restricted(use_all, [_]) ->
- true;
not_restricted(history, [_]) ->
true;
not_restricted(results, [_]) ->
@@ -964,7 +961,7 @@ local_func(rd, [{atom,_,RecName},RecDef0], Bs, _Shell, RT, _Lf, _Ef) ->
[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 +985,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),
@@ -1003,47 +1002,6 @@ local_func(rr, [_,_]=As0, Bs0, _Shell, RT, Lf, Ef) ->
local_func(rr, [_,_,_]=As0, Bs0, _Shell, RT, Lf, Ef) ->
{[File,Sel,Options],Bs} = expr_list(As0, Bs0, Lf, Ef),
{value,read_and_add_records(File, Sel, Options, Bs, RT),Bs};
-local_func(which, [{atom,_,M}], Bs, _Shell, _RT, _Lf, _Ef) ->
- case erl_eval:binding({module,M}, Bs) of
- {value, M1} ->
- {value,M1,Bs};
- unbound ->
- {value,M,Bs}
- 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 +1124,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),
@@ -1179,18 +1137,13 @@ listify(E) ->
[E].
check_command(Es, Bs) ->
- erl_eval:check_command(Es, strip_bindings(Bs)).
+ erl_eval:check_command(Es, Bs).
expr(E, Bs, Lf, Ef) ->
- erl_eval:expr(E, strip_bindings(Bs), Lf, Ef).
+ erl_eval:expr(E, Bs, Lf, Ef).
expr_list(Es, Bs, Lf, Ef) ->
- erl_eval:expr_list(Es, strip_bindings(Bs), Lf, Ef).
-
--spec strip_bindings(erl_eval:binding_struct()) -> erl_eval:binding_struct().
-
-strip_bindings(Bs) ->
- Bs -- [B || {{module,_},_}=B <- Bs].
+ erl_eval:expr_list(Es, Bs, Lf, Ef).
%% Note that a sequence number is used here to make sure that if a
%% record is used by another record, then the first record is parsed
@@ -1323,15 +1276,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,39 +1287,36 @@ 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]),
- list_bindings(Bs, RT);
list_bindings([{Name,Val}|Bs], RT) ->
case erl_eval:fun_data(Val) of
{fun_data,_FBs,FCs0} ->
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 +1325,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,14 +1368,35 @@ 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) ->
+ Strings =
+ case application:get_env(stdlib, shell_strings) of
+ {ok, false} ->
+ false;
+ _ ->
+ true
+ end,
+ io_lib_pretty:print(V, ([{column, I}, {line_length, columns()},
+ {depth, ?LINEMAX}, {max_chars, ?CHAR_MAX},
+ {strings, Strings},
+ {record_print_fun, record_print_fun(RT)}]
+ ++ Enc)).
columns() ->
case io:columns() of
{ok,N} -> N;
_ -> 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),
@@ -1457,9 +1419,9 @@ check_env(V) ->
{ok, Val} when is_integer(Val), Val >= 0 ->
ok;
{ok, Val} ->
- Txt = io_lib:fwrite(
- <<"Invalid value of STDLIB configuration parameter ~p: ~p\n">>,
- [V, Val]),
+ Txt = io_lib:fwrite
+ ("Invalid value of STDLIB configuration parameter"
+ "~w: ~tp\n", [V, Val]),
error_logger:info_report(lists:flatten(Txt))
end.
@@ -1485,14 +1447,22 @@ history(L) when is_integer(L), L >= 0 ->
results(L) when is_integer(L), L >= 0 ->
set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS).
--spec catch_exception(Bool) -> Bool when
+-spec catch_exception(Bool) -> boolean() when
Bool :: boolean().
catch_exception(Bool) ->
set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION).
--spec prompt_func(PromptFunc) -> PromptFunc when
- PromptFunc :: 'default' | {module(),atom()}.
+-spec prompt_func(PromptFunc) -> PromptFunc2 when
+ PromptFunc :: 'default' | {module(),atom()},
+ PromptFunc2 :: 'default' | {module(),atom()}.
prompt_func(String) ->
set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC).
+
+-spec strings(Strings) -> Strings2 when
+ Strings :: boolean(),
+ Strings2 :: boolean().
+
+strings(Strings) ->
+ set_env(stdlib, shell_strings, Strings, ?DEF_STRINGS).
diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl
index de0179da59..9c74041f56 100644
--- a/lib/stdlib/src/slave.erl
+++ b/lib/stdlib/src/slave.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
@@ -229,7 +229,7 @@ wait_for_slave(Parent, Host, Name, Node, Args, LinkTo, Prog) ->
Waiter = register_unique_name(0),
case mk_cmd(Host, Name, Args, Waiter, Prog) of
{ok, Cmd} ->
-%% io:format("Command: ~s~n", [Cmd]),
+%% io:format("Command: ~ts~n", [Cmd]),
open_port({spawn, Cmd}, [stream]),
receive
{SlavePid, slave_started} ->
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 94e81188b5..55c8087475 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -17,11 +17,11 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max two major revisions back
- [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14
%% Down to - max two major revisions back
- [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
- {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
- {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+ [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16
+ {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14
}.
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index 30eac4f07d..4ed27ff4eb 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -1,7 +1,8 @@
+%% -*- coding: utf-8 -*-
%%
%% %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
@@ -29,6 +30,30 @@
%%---------------------------------------------------------------------------
+%%% BIFs
+
+-export([to_float/1, to_integer/1]).
+
+-spec to_float(String) -> {Float, Rest} | {error, Reason} when
+ String :: string(),
+ Float :: float(),
+ Rest :: string(),
+ Reason :: no_float | not_a_list.
+
+to_float(_) ->
+ erlang:nif_error(undef).
+
+-spec to_integer(String) -> {Int, Rest} | {error, Reason} when
+ String :: string(),
+ Int :: integer(),
+ Rest :: string(),
+ Reason :: no_integer | not_a_list.
+
+to_integer(_) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
+
%% Robert's bit
%% len(String)
@@ -233,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 %%%
@@ -459,8 +484,8 @@ to_upper_char(C) ->
C.
-spec to_lower(String) -> Result when
- String :: string(),
- Result :: string()
+ String :: io_lib:latin1_string(),
+ Result :: io_lib:latin1_string()
; (Char) -> CharResult when
Char :: char(),
CharResult :: char().
@@ -471,8 +496,8 @@ to_lower(C) when is_integer(C) ->
to_lower_char(C).
-spec to_upper(String) -> Result when
- String :: string(),
- Result :: string()
+ String :: io_lib:latin1_string(),
+ Result :: io_lib:latin1_string()
; (Char) -> CharResult when
Char :: char(),
CharResult :: char().
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/unicode.erl b/lib/stdlib/src/unicode.erl
index e9b90befe6..49529cffd4 100644
--- a/lib/stdlib/src/unicode.erl
+++ b/lib/stdlib/src/unicode.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
@@ -18,39 +18,78 @@
%%
-module(unicode).
-%% Implemented in the emulator:
-%% characters_to_binary/2 (will trap to characters_to_binary_int/2
-%% if InEncoding is not {latin1 | unicode | utf8})
-%% characters_to_list/2 (will trap to characters_to_list_int/2 if
-%% InEncoding is not {latin1 | unicode | utf8})
-%%
-
-export([characters_to_list/1, characters_to_list_int/2,
characters_to_binary/1, characters_to_binary_int/2,
characters_to_binary/3,
bom_to_encoding/1, encoding_to_bom/1]).
-export_type([chardata/0, charlist/0, encoding/0, external_chardata/0,
- external_charlist/0, latin1_chardata/0,
- latin1_charlist/0, unicode_binary/0, unicode_char/0]).
+ external_charlist/0, latin1_char/0, latin1_chardata/0,
+ latin1_charlist/0, latin1_binary/0, unicode_binary/0]).
-type encoding() :: 'latin1' | 'unicode' | 'utf8'
| 'utf16' | {'utf16', endian()}
| 'utf32' | {'utf32', endian()}.
-type endian() :: 'big' | 'little'.
-type unicode_binary() :: binary().
--type unicode_char() :: non_neg_integer().
--type charlist() :: [unicode_char() | unicode_binary() | charlist()].
+-type charlist() ::
+ maybe_improper_list(char() | unicode_binary() | charlist(),
+ unicode_binary() | nil()).
-type chardata() :: charlist() | unicode_binary().
-type external_unicode_binary() :: binary().
-type external_chardata() :: external_charlist() | external_unicode_binary().
--type external_charlist() :: [unicode_char() | external_unicode_binary()
- | external_charlist()].
+-type external_charlist() ::
+ maybe_improper_list(char() |
+ external_unicode_binary() |
+ external_charlist(),
+ external_unicode_binary() | nil()).
-type latin1_binary() :: binary().
-type latin1_char() :: byte().
-type latin1_chardata() :: latin1_charlist() | latin1_binary().
--type latin1_charlist() :: [latin1_char() | latin1_binary()
- | latin1_charlist()].
+-type latin1_charlist() ::
+ maybe_improper_list(latin1_char() |
+ latin1_binary() |
+ latin1_charlist(),
+ latin1_binary() | nil()).
+
+%%% BIFs
+%%%
+%%% characters_to_binary/2 (will trap to characters_to_binary_int/2
+%%% if InEncoding is not {latin1 | unicode | utf8})
+%%% characters_to_list/2 (will trap to characters_to_list_int/2 if
+%%% InEncoding is not {latin1 | unicode | utf8})
+
+-export([bin_is_7bit/1, characters_to_binary/2, characters_to_list/2]).
+
+-spec bin_is_7bit(Binary) -> boolean() when
+ Binary :: binary().
+
+bin_is_7bit(_) ->
+ erlang:nif_error(undef).
+
+-spec characters_to_binary(Data, InEncoding) -> Result when
+ Data :: latin1_chardata() | chardata() | external_chardata(),
+ InEncoding :: encoding(),
+ Result :: binary()
+ | {error, binary(), RestData}
+ | {incomplete, binary(), binary()},
+ RestData :: latin1_chardata() | chardata() | external_chardata().
+
+characters_to_binary(_, _) ->
+ erlang:nif_error(undef).
+
+-spec characters_to_list(Data, InEncoding) -> Result when
+ Data :: latin1_chardata() | chardata() | external_chardata(),
+ InEncoding :: encoding(),
+ Result :: list()
+ | {error, list(), RestData}
+ | {incomplete, list(), binary()},
+ RestData :: latin1_chardata() | chardata() | external_chardata().
+
+characters_to_list(_, _) ->
+ erlang:nif_error(undef).
+
+%%% End of BIFs
-spec characters_to_list(Data) -> Result when
Data :: latin1_chardata() | chardata() | external_chardata(),
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 c383540db7..c40ce8e203 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -610,9 +610,9 @@ get_zip_opt([Unknown | _Rest], _Opts) ->
%% feedback funs
silent(_) -> ok.
-verbose_unzip(FN) -> io:format("extracting: ~p\n", [FN]).
+verbose_unzip(FN) -> io:format("extracting: ~tp\n", [FN]).
-verbose_zip(FN) -> io:format("adding: ~p\n", [FN]).
+verbose_zip(FN) -> io:format("adding: ~tp\n", [FN]).
%% file filter funs
all(_) -> true.
@@ -943,7 +943,7 @@ raw_short_print_info_etc(EOCD, X, Comment, Y, Acc) when is_record(EOCD, eocd) ->
raw_long_print_info_etc(EOCD, X, Comment, Y, Acc).
print_file_name(FileName) ->
- io:format("~s\n", [FileName]).
+ io:format("~ts\n", [FileName]).
%% for printing directory (tt/1)
@@ -960,14 +960,14 @@ raw_long_print_info_etc(EOCD, _, Comment, _, Acc) when is_record(EOCD, eocd) ->
Acc.
print_header(CompSize, MTime, UncompSize, FileName, FileComment) ->
- io:format("~8w ~s ~8w ~2w% ~s ~s\n",
+ io:format("~8w ~s ~8w ~2w% ~ts ~ts\n",
[CompSize, time_to_string(MTime), UncompSize,
get_percent(CompSize, UncompSize), FileName, FileComment]).
print_comment("") ->
ok;
print_comment(Comment) ->
- io:format("Archive comment: ~s\n", [Comment]).
+ io:format("Archive comment: ~ts\n", [Comment]).
get_percent(_, 0) -> 100;
get_percent(CompSize, Size) -> round(CompSize * 100 / Size).
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..0223240479 100644
--- a/lib/stdlib/test/dict_SUITE.erl
+++ b/lib/stdlib/test/dict_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
@@ -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..e308fd0721 100644
--- a/lib/stdlib/test/dict_test_lib.erl
+++ b/lib/stdlib/test/dict_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-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
@@ -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..b2f1aa955a 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-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
@@ -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, otp_10820/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, otp_10820].
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,104 @@ 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) ->
+ B = list_to_binary(Enc),
+ E = epp:read_encoding_from_binary(B),
+ 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) ->
+ Options = [{in_comment_only, false}],
+ B = list_to_binary(Enc),
+ E = epp:read_encoding_from_binary(B, Options),
+ ok = file:write_file(File, Enc),
+ {ok, Fd} = file:open(File, [read]),
+ ok = file:close(Fd),
+ E = epp:read_encoding(File, Options).
+
+otp_10820(doc) ->
+ "OTP-10820. Unicode filenames.";
+otp_10820(suite) ->
+ [];
+otp_10820(Config) when is_list(Config) ->
+ L = [915,953,959,973,957,953,954,959,957,964],
+ Dir = ?config(priv_dir, Config),
+ File = filename:join(Dir, L++".erl"),
+ C1 = <<"%% coding: utf-8\n -module(any).">>,
+ ok = do_otp_10820(File, C1, "+pc latin1"),
+ ok = do_otp_10820(File, C1, "+pc unicode"),
+ C2 = <<"\n-module(any).">>,
+ ok = do_otp_10820(File, C2, "+pc latin1"),
+ ok = do_otp_10820(File, C2, "+pc unicode").
+
+do_otp_10820(File, C, PC) ->
+ {ok,Node} = start_node(erl_pp_helper, "+fnu " ++ PC),
+ ok = rpc:call(Node, file, write_file, [File, C]),
+ {ok,[{attribute,1,file,{File,1}},
+ {attribute,2,module,any},
+ {eof,2}]} = rpc:call(Node, epp, parse_file, [File, [],[]]),
+ true = test_server:stop_node(Node),
+ ok.
+
check(Config, Tests) ->
eval_tests(Config, fun check_test/2, Tests).
@@ -1393,3 +1499,8 @@ ln2({error,M}) ->
{error,ln2(M)};
ln2(M) ->
M.
+
+%% +fnu means a peer node has to be started; slave will not do
+start_node(Name, Xargs) ->
+ ?line PA = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name, peer, [{args, "-pa " ++ PA ++ " " ++ Xargs}]).
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index b0c7d562d5..7ff4c81ea6 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2012. 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
@@ -37,6 +37,7 @@
otp_6977/1,
otp_7550/1,
otp_8133/1,
+ otp_10622/1,
funs/1,
try_catch/1,
eval_expr_5/1,
@@ -79,7 +80,7 @@ all() ->
pattern_expr, match_bin, guard_3, guard_4, lc,
simple_cases, unary_plus, apply_atom, otp_5269,
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
- otp_8133, funs, try_catch, eval_expr_5, zero_width].
+ otp_8133, otp_10622, funs, try_catch, eval_expr_5, zero_width].
groups() ->
[].
@@ -216,13 +217,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 +231,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),
@@ -960,6 +961,7 @@ otp_8133(Config) when is_list(Config) ->
E = fun(N) ->
if
is_integer(N) -> <<N/integer>>;
+
true -> erlang:error(foo)
end
end,
@@ -980,6 +982,48 @@ otp_8133(Config) when is_list(Config) ->
ok),
ok.
+otp_10622(doc) ->
+ ["OTP-10622. Bugs."];
+otp_10622(suite) ->
+ [];
+otp_10622(Config) when is_list(Config) ->
+ check(fun() -> <<0>> = <<"\x{400}">> end,
+ "<<0>> = <<\"\\x{400}\">>. ",
+ <<0>>),
+ check(fun() -> <<"\x{aa}ff"/utf8>> = <<"\x{aa}ff"/utf8>> end,
+ "<<\"\\x{aa}ff\"/utf8>> = <<\"\\x{aa}ff\"/utf8>>. ",
+ <<"�\xaaff">>),
+ %% The same bug as last example:
+ check(fun() -> case <<"foo"/utf8>> of
+ <<"foo"/utf8>> -> true
+ end
+ end,
+ "case <<\"foo\"/utf8>> of <<\"foo\"/utf8>> -> true end.",
+ true),
+ check(fun() -> <<"\x{400}"/utf8>> = <<"\x{400}"/utf8>> end,
+ "<<\"\\x{400}\"/utf8>> = <<\"\\x{400}\"/utf8>>. ",
+ <<208,128>>),
+ error_check("<<\"\\x{aaa}\">> = <<\"\\x{aaa}\">>.",
+ {badmatch,<<"\xaa">>}),
+
+ check(fun() -> [a || <<"\x{aaa}">> <= <<2703:16>>] end,
+ "[a || <<\"\\x{aaa}\">> <= <<2703:16>>]. ",
+ []),
+ check(fun() -> [a || <<"\x{aa}"/utf8>> <= <<"\x{aa}"/utf8>>] end,
+ "[a || <<\"\\x{aa}\"/utf8>> <= <<\"\\x{aa}\"/utf8>>]. ",
+ [a]),
+ check(fun() -> [a || <<"\x{aa}x"/utf8>> <= <<"\x{aa}y"/utf8>>] end,
+ "[a || <<\"\\x{aa}x\"/utf8>> <= <<\"\\x{aa}y\"/utf8>>]. ",
+ []),
+ check(fun() -> [a || <<"\x{aaa}">> <= <<"\x{aaa}">>] end,
+ "[a || <<\"\\x{aaa}\">> <= <<\"\\x{aaa}\">>]. ",
+ []),
+ check(fun() -> [a || <<"\x{aaa}"/utf8>> <= <<"\x{aaa}"/utf8>>] end,
+ "[a || <<\"\\x{aaa}\"/utf8>> <= <<\"\\x{aaa}\"/utf8>>]. ",
+ [a]),
+
+ ok.
+
funs(doc) ->
["Simple cases, just to cover some code."];
funs(suite) ->
@@ -1042,6 +1086,10 @@ funs(Config) when is_list(Config) ->
"begin M = lists, F = fun M:reverse/1,"
" [1,2] = F([2,1]), ok end.",
ok),
+
+ %% Test that {M,F} is not accepted as a fun.
+ error_check("{" ?MODULE_STRING ",module_info}().",
+ {badfun,{?MODULE,module_info}}),
ok.
run_many_args({S, As}) ->
@@ -1077,11 +1125,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 +1156,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 +1208,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 9f9d97b619..f8345559c4 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-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
@@ -50,14 +50,16 @@
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,
otp_8051/1,
format_warn/1,
on_load_successful/1, on_load_failing/1,
- too_many_arguments/1
+ too_many_arguments/1,
+ basic_errors/1,bin_syntax_errors/1
]).
% Default timetrap timeout (set in init_per_testcase).
@@ -80,10 +82,10 @@ 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].
+ too_many_arguments, basic_errors, bin_syntax_errors].
groups() ->
[{unused_vars_warn, [],
@@ -1307,44 +1309,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],
- {error,[{2,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}],
- [{8,erl_lint,deprecated_tuple_fun},
- {14,erl_lint,deprecated_tuple_fun},
- {20,erl_lint,deprecated_tuple_fun},
- {28,erl_lint,deprecated_tuple_fun}]}},
+ {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},
+ {10,erl_lint,illegal_guard_expr},
+ {12,erl_lint,illegal_guard_expr},
+ {14,erl_lint,illegal_guard_expr}],
+ []}},
{guard6,
<<"-record(apa,{a=a,b=foo:bar()}).
apa() ->
@@ -1364,7 +1352,17 @@ guard(Config) when is_list(Config) ->
(is_record(X, apa)*2)].
">>,
[],
- []}],
+ []},
+ {guard8,
+ <<"t(A) when erlang:is_foobar(A) -> ok;
+ t(A) when A ! ok -> ok;
+ t(A) when A ++ [x] -> ok."
+ >>,
+ [],
+ {errors,[{1,erl_lint,illegal_guard_expr},
+ {2,erl_lint,illegal_guard_expr},
+ {3,erl_lint,illegal_guard_expr}],[]}}
+ ],
?line [] = run(Config, Ts1),
ok.
@@ -1652,6 +1650,7 @@ otp_5276(Config) when is_list(Config) ->
-deprecated([{'_','_',never}]).
-deprecated([{{badly,formed},1}]).
-deprecated([{'_','_',next_major_release}]).
+ -deprecated([{atom_to_list,1}]).
-export([t/0]).
frutt() -> ok.
t() -> ok.
@@ -1662,8 +1661,9 @@ otp_5276(Config) when is_list(Config) ->
{3,erl_lint,{invalid_deprecated,'foo bar'}},
{5,erl_lint,{bad_deprecated,{f,'_'}}},
{8,erl_lint,{invalid_deprecated,{'_','_',never}}},
- {9,erl_lint,{invalid_deprecated,{{badly,formed},1}}}],
- [{12,erl_lint,{unused_function,{frutt,0}}}]}}],
+ {9,erl_lint,{invalid_deprecated,{{badly,formed},1}}},
+ {11,erl_lint,{bad_deprecated,{atom_to_list,1}}}],
+ [{13,erl_lint,{unused_function,{frutt,0}}}]}}],
?line [] = run(Config, Ts),
ok.
@@ -1745,7 +1745,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
@@ -1777,7 +1777,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}}}]}},
@@ -1909,9 +1909,23 @@ otp_5362(Config) when is_list(Config) ->
warn_deprecated_function,
warn_bif_clash]},
{errors,
- [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}},
- ],
+ {call_deprecated_function,
+ <<"t(X) -> erlang:hash(X, 2000).">>,
+ [],
+ {warnings,
+ [{1,erl_lint,{deprecated,{erlang,hash,2},
+ {erlang,phash2,2},"in a future release"}}]}},
+
+ {call_removed_function,
+ <<"t(X) -> regexp:match(X).">>,
+ [],
+ {warnings,
+ [{1,erl_lint,{removed,{regexp,match,1},
+ "removed in R15; use the re module instead"}}]}}
+
+ ],
?line [] = run(Config, Ts),
ok.
@@ -2200,27 +2214,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\").
@@ -2400,6 +2396,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) ->
@@ -2848,10 +2866,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.
@@ -2980,6 +2998,77 @@ too_many_arguments(Config) when is_list(Config) ->
ok.
+%% Test some basic errors to improve coverage.
+basic_errors(Config) ->
+ Ts = [{redefine_module,
+ <<"-module(redefine_module).">>,
+ [],
+ {errors,[{1,erl_lint,redefine_module}],[]}},
+
+ {attr_after_function,
+ <<"f() -> ok.
+ -attr(x).">>,
+ [],
+ {errors,[{2,erl_lint,{attribute,attr}}],[]}},
+
+ {redefine_function,
+ <<"f() -> ok.
+ f() -> ok.">>,
+ [],
+ {errors,[{2,erl_lint,{redefine_function,{f,0}}}],[]}},
+
+ {redefine_record,
+ <<"-record(r, {a}).
+ -record(r, {a}).
+ f(#r{}) -> ok.">>,
+ [],
+ {errors,[{2,erl_lint,{redefine_record,r}}],[]}},
+
+ {illegal_record_info,
+ <<"f1() -> record_info(42, record).
+ f2() -> record_info(shoe_size, record).">>,
+ [],
+ {errors,[{1,erl_lint,illegal_record_info},
+ {2,erl_lint,illegal_record_info}],[]}},
+
+ {illegal_expr,
+ <<"f() -> a:b.">>,
+ [],
+ {errors,[{1,erl_lint,illegal_expr}],[]}},
+
+ {illegal_pattern,
+ <<"f(A+B) -> ok.">>,
+ [],
+ {errors,[{1,erl_lint,illegal_pattern}],[]}}
+ ],
+ [] = run(Config, Ts),
+ ok.
+
+%% Test binary syntax errors
+bin_syntax_errors(Config) ->
+ Ts = [{bin_syntax_errors,
+ <<"t(<<X:bad_size>>) -> X;
+ t(<<_:(x ! y)/integer>>) -> ok;
+ t(<<X:all/integer>>) -> X;
+ t(<<X/bad_type>>) -> X;
+ t(<<X/unit:8>>) -> X;
+ t(<<X:7/float>>) -> X;
+ t(<< <<_:8>> >>) -> ok;
+ t(<<(x ! y):8/integer>>) -> ok.
+ ">>,
+ [],
+ {error,[{1,erl_lint,illegal_bitsize},
+ {2,erl_lint,illegal_bitsize},
+ {3,erl_lint,illegal_bitsize},
+ {4,erl_lint,{undefined_bittype,bad_type}},
+ {5,erl_lint,bittype_unit},
+ {7,erl_lint,illegal_pattern},
+ {8,erl_lint,illegal_pattern}],
+ [{6,erl_lint,{bad_bitsize,"float"}}]}}
+ ],
+ [] = run(Config, Ts),
+ ok.
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 64853ca078..9c0a43abcc 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-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,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, otp_10820/1]).
%% Internal export.
-export([ehook/6]).
@@ -74,12 +75,13 @@ 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, otp_10820]}].
init_per_suite(Config) ->
Config.
@@ -438,9 +440,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 +461,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 +538,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 +582,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 +600,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 +662,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 +1038,71 @@ 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).
+
+otp_10820(doc) ->
+ "OTP-10820. Unicode filenames.";
+otp_10820(suite) -> [];
+otp_10820(Config) when is_list(Config) ->
+ C1 = <<"%% coding: utf-8\n -module(any).">>,
+ ok = do_otp_10820(Config, C1, "+pc latin1"),
+ ok = do_otp_10820(Config, C1, "+pc unicode"),
+ C2 = <<"-module(any).">>,
+ ok = do_otp_10820(Config, C2, "+pc latin1"),
+ ok = do_otp_10820(Config, C2, "+pc unicode").
+
+do_otp_10820(Config, C, PC) ->
+ {ok,Node} = start_node(erl_pp_helper, "+fnu " ++ PC),
+ L = [915,953,959,973,957,953,954,959,957,964],
+ FileName = filename(L++".erl", Config),
+ ok = rpc:call(Node, file, write_file, [FileName, C]),
+ {ok, _, []} = rpc:call(Node, compile, file,
+ [FileName, [return,'P',{outdir,?privdir}]]),
+ PFileName = filename(L++".P", Config),
+ {ok, Bin} = rpc:call(Node, file, read_file, [PFileName]),
+ true = test_server:stop_node(Node),
+ true = file_attr_is_string(binary_to_list(Bin)),
+ ok.
+
+file_attr_is_string("-file(\"" ++ _) -> true;
+file_attr_is_string([_ | L]) ->
+ file_attr_is_string(L).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
@@ -1137,9 +1172,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 +1184,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 +1195,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 +1211,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 +1231,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;
@@ -1228,3 +1276,8 @@ filename(Name, Config) ->
fail() ->
io:format("failed~n"),
?t:fail().
+
+%% +fnu means a peer node has to be started; slave will not do
+start_node(Name, Xargs) ->
+ ?line PA = filename:dirname(code:which(?MODULE)),
+ test_server:start_node(Name, peer, [{args, "-pa " ++ PA ++ " " ++ Xargs}]).
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 4298b2c701..ecd181e87c 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-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,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]}].
@@ -117,13 +118,13 @@ check(String) ->
%%% (This should be useful for all format_error functions.)
check_error({error, Info, EndLine}, Module0) ->
- ?line {ErrorLine, Module, Desc} = Info,
- ?line true = (Module == Module0),
- ?line assert_type(EndLine, integer),
- ?line assert_type(ErrorLine, integer),
- ?line true = (ErrorLine =< EndLine),
- ?line String = lists:flatten(Module0:format_error(Desc)),
- ?line true = io_lib:printable_list(String).
+ {ErrorLine, Module, Desc} = Info,
+ true = (Module == Module0),
+ assert_type(EndLine, integer),
+ assert_type(ErrorLine, integer),
+ true = (ErrorLine =< EndLine),
+ String = lists:flatten(Module0:format_error(Desc)),
+ true = io_lib:printable_list(String).
iso88591(doc) -> ["Tests the support for ISO-8859-1 i.e Latin-1"];
iso88591(suite) -> [];
@@ -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() ->
@@ -814,62 +815,51 @@ unicode() ->
erl_scan:string([1089]),
?line {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
erl_scan:string([1089], {1,1}),
- ?line {error,{1,erl_scan,{illegal,character}},1} =
- %% ?line {error,{1,erl_scan,{illegal,atom}},1} =
- erl_scan:string("'a"++[1089]++"b'"),
- ?line {error,{{1,3},erl_scan,{illegal,character}},{1,4}} =
+ ?line {error,{1,erl_scan,{illegal,atom}},1} =
+ erl_scan:string("'a"++[1089]++"b'", 1),
+ ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,6}} =
erl_scan:string("'a"++[1089]++"b'", {1,1}),
?line test("\"a"++[1089]++"b\""),
- ?line {ok,[{char,1,1}],1} = erl_scan:string([$$,$\\,$^,1089]),
+ ?line {ok,[{char,1,1}],1} =
+ erl_scan:string([$$,$\\,$^,1089], 1),
- ?line {error,{1,erl_scan,Error},1} = erl_scan:string("\"qa\x{aaa}"),
- ?line "unterminated string starting with \"qa\\x{AAA}\"" =
+ ?line {error,{1,erl_scan,Error},1} =
+ erl_scan:string("\"qa\x{aaa}", 1),
+ ?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}),
- ?line {error,{{1,4},erl_scan,{illegal,character}},{1,11}} =
+ ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} =
erl_scan:string("'qa\\x{aaa}'",{1,1}),
- Tags = [category, column, length, line, symbol, text],
-
- %% Workaround. No character codes greater than 255! To be changed.
- %% Note: don't remove these tests, just modify them!
-
- ?line {ok,[{integer,1,1089}],1} = erl_scan:string([$$,1089]),
- ?line {ok,[{integer,1,1089}],1} = erl_scan:string([$$,$\\,1089]),
+ ?line {ok,[{char,1,1089}],1} =
+ erl_scan:string([$$,1089], 1),
+ ?line {ok,[{char,1,1089}],1} =
+ erl_scan:string([$$,$\\,1089], 1),
Qs = "$\\x{aaa}",
- ?line {ok,[{integer,1,16#aaa}],1} = erl_scan:string(Qs),
- ?line {ok,[Q2],{1,9}} = erl_scan:string("$\\x{aaa}", {1,1}, text),
- ?line [{category,integer},{column,1},{length,8},
+ ?line {ok,[{char,1,$\x{aaa}}],1} =
+ erl_scan:string(Qs, 1),
+ ?line {ok,[Q2],{1,9}} =
+ erl_scan:string("$\\x{aaa}", {1,1}, [text]),
+ ?line [{category,char},{column,1},{length,8},
{line,1},{symbol,16#aaa},{text,Qs}] =
erl_scan:token_info(Q2),
U1 = "\"\\x{aaa}\"",
- ?line {ok,[T1,T2,T3],{1,10}} = erl_scan:string(U1, {1,1}, text),
- ?line [{category,'['},{column,1},{length,1},{line,1},
- {symbol,'['},{text,"\""}] = erl_scan:token_info(T1, Tags),
- ?line [{category,integer},{column,2},{length,7},
- {line,1},{symbol,16#aaa},{text,"\\x{aaa}"}] =
- erl_scan:token_info(T2, Tags),
- ?line [{category,']'},{column,9},{length,1},{line,1},
- {symbol,']'},{text,"\""}] = erl_scan:token_info(T3, Tags),
- ?line {ok,[{'[',1},{integer,1,16#aaa},{']',1}],1} =
- erl_scan:string(U1, 1),
+ {ok,
+ [{string,[{line,1},{column,1},{text,"\"\\x{aaa}\""}],[2730]}],
+ {1,10}} = erl_scan:string(U1, {1,1}, [text]),
+ {ok,[{string,1,[2730]}],1} = erl_scan:string(U1, 1),
U2 = "\"\\x41\\x{fff}\\x42\"",
- ?line {ok,[{'[',1},{char,1,16#41},{',',1},{integer,1,16#fff},
- {',',1},{char,1,16#42},{']',1}],1} = erl_scan:string(U2, 1),
+ {ok,[{string,1,[$\x41,$\x{fff},$\x42]}],1} = erl_scan:string(U2, 1),
U3 = "\"a\n\\x{fff}\n\"",
- ?line {ok,[{'[',1},{char,1,$a},{',',1},{char,1,$\n},
- {',',2},{integer,2,16#fff},{',',2},{char,2,$\n},
- {']',3}],3} =
- erl_scan:string(U3, 1),
+ {ok,[{string,1,[$a,$\n,$\x{fff},$\n]}],3} = erl_scan:string(U3, 1),
U4 = "\"\\^\n\\x{aaa}\\^\n\"",
- ?line {ok,[{'[',1},{char,1,$\n},{',',2},{integer,2,16#aaa},
- {',',2},{char,2,$\n},{']',3}],3} = erl_scan:string(U4, 1),
+ {ok,[{string,1,[$\n,$\x{aaa},$\n]}],3} = erl_scan:string(U4, 1),
%% Keep these tests:
?line test(Qs),
@@ -879,18 +869,15 @@ unicode() ->
?line test(U4),
Str1 = "\"ab" ++ [1089] ++ "cd\"",
- ?line {ok,[{'[',1},{char,1,$a},{',',1},{char,1,$b},{',',1},
- {integer,1,1089},{',',1},{char,1,$c},{',',1},
- {char,1,$d},{']',1}],1} = erl_scan:string(Str1),
- ?line {ok,[{'[',_},{char,_,$a},{',',_},{char,_,$b},{',',_},
- {integer,_,1089},{',',_},{char,_,$c},{',',_},
- {char,_,$d},{']',_}],{1,8}} = erl_scan:string(Str1, {1,1}),
+ {ok,[{string,1,[$a,$b,1089,$c,$d]}],1} = erl_scan:string(Str1, 1),
+ {ok,[{string,{1,1},[$a,$b,1089,$c,$d]}],{1,8}} =
+ erl_scan:string(Str1, {1,1}),
?line test(Str1),
Comment = "%% "++[1089],
- ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} =
- erl_scan:string(Comment, 1, return),
- ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} =
- erl_scan:string(Comment, {1,1}, return),
+ {ok,[{comment,1,[$%,$%,$\s,1089]}],1} =
+ erl_scan:string(Comment, 1, [return]),
+ {ok,[{comment,{1,1},[$%,$%,$\s,1089]}],{1,5}} =
+ erl_scan:string(Comment, {1,1}, [return]),
ok.
more_chars() ->
@@ -958,6 +945,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),
+ {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} =
+ erl_scan:string("'qa\\x{aaa}'",{1,1}),
+
+ {ok,[{char,1,1089}],1} = erl_scan:string([$$,1089], 1),
+ {ok,[{char,1,1089}],1} = erl_scan:string([$$,$\\,1089],1),
+
+ Qs = "$\\x{aaa}",
+ {ok,[{char,1,2730}],1} = erl_scan:string(Qs,1),
+ {ok,[Q2],{1,9}} = erl_scan:string(Qs,{1,1},[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}, [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),
+
+ U3 = "\"a\n\\x{fff}\n\"",
+ {ok,[{string,1,[97,10,4095,10]}],3} = erl_scan:string(U3, 1),
+
+ U4 = "\"\\^\n\\x{aaa}\\^\n\"",
+ {ok,[{string,1,[10,2730,10]}],3} = erl_scan:string(U4, 1,[]),
+
+ Str1 = "\"ab" ++ [1089] ++ "cd\"",
+ {ok,[{string,1,[97,98,1089,99,100]}],1} =
+ erl_scan:string(Str1,1),
+ {ok,[{string,{1,1},[97,98,1089,99,100]}],{1,8}} =
+ erl_scan:string(Str1, {1,1}),
+
+ 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, [return]) ||
+ OK <- OKL],
+ {ok,[{comment,_,[$%,$%,$\s,OK1]}],{1,5}} =
+ erl_scan:string("%% "++[OK1], {1,1}, [return]),
+ [{error,{1,erl_scan,{illegal,character}},1} =
+ erl_scan:string("%% "++[Illegal], 1, [return]) ||
+ Illegal <- IllegalL],
+ {error,{{1,1},erl_scan,{illegal,character}},{1,5}} =
+ erl_scan:string("%% "++[Illegal1], {1,1}, [return]),
+
+ [{ok,[],1} = erl_scan:string("%% "++[OK], 1, []) ||
+ OK <- OKL],
+ {ok,[],{1,5}} = erl_scan:string("%% "++[OK1], {1,1}, []),
+ [{error,{1,erl_scan,{illegal,character}},1} =
+ erl_scan:string("%% "++[Illegal], 1, []) ||
+ Illegal <- IllegalL],
+ {error,{{1,1},erl_scan,{illegal,character}},{1,5}} =
+ erl_scan:string("%% "++[Illegal1], {1,1}, []),
+
+ [{ok,[{string,{1,1},[OK]}],{1,4}} =
+ erl_scan:string("\""++[OK]++"\"",{1,1}) ||
+ OK <- OKL],
+ [{error,{{1,2},erl_scan,{illegal,character}},{1,3}} =
+ erl_scan:string("\""++[OK]++"\"",{1,1}) ||
+ OK <- IllegalL],
+
+ [{error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
+ erl_scan:string([Illegal],{1,1}) ||
+ Illegal <- IllegalL],
+
+ {ok,[{char,{1,1},OK1}],{1,3}} =
+ erl_scan:string([$$,OK1],{1,1}),
+ {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
+ erl_scan:string([$$,Illegal1],{1,1}),
+
+ {ok,[{char,{1,1},OK1}],{1,4}} =
+ erl_scan:string([$$,$\\,OK1],{1,1}),
+ {error,{{1,1},erl_scan,{illegal,character}},{1,4}} =
+ erl_scan:string([$$,$\\,Illegal1],{1,1}),
+
+ {ok,[{string,{1,1},[55295]}],{1,5}} =
+ erl_scan:string("\"\\"++[OK1]++"\"",{1,1}),
+ {error,{{1,2},erl_scan,{illegal,character}},{1,4}} =
+ erl_scan:string("\"\\"++[Illegal1]++"\"",{1,1}),
+
+ {ok,[{char,{1,1},OK1}],{1,10}} =
+ erl_scan:string("$\\x{D7FF}",{1,1}),
+ {error,{{1,1},erl_scan,{illegal,character}},{1,10}} =
+ erl_scan:string("$\\x{D800}",{1,1}),
+
+ %% 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 38c085616d..cf5fb12686 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_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
@@ -26,6 +26,7 @@
errors/1,
strange_name/1,
emulator_flags/1,
+ emulator_flags_no_shebang/1,
module_script/1,
beam_script/1,
archive_script/1,
@@ -34,7 +35,8 @@
create_and_extract/1,
foldl/1,
overflow/1,
- verify_sections/3
+ verify_sections/3,
+ unicode/1
]).
-include_lib("test_server/include/test_server.hrl").
@@ -44,9 +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,
- archive_script_file_access].
+ archive_script_file_access, unicode].
groups() ->
[].
@@ -64,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) ->
@@ -149,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
@@ -618,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 ->
@@ -810,6 +828,8 @@ normalize_sections(Sections) ->
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
foldl(Config) when is_list(Config) ->
{NewFile, _FileInfo,
_EmuArg, _Source,
@@ -887,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 <<224,170,170>> / <<224,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/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..351bb785e5
--- /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{aaa}"/utf8>>,
+ 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 95f10b1df3..b6c94a6f6d 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.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
@@ -96,7 +96,7 @@
misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1,
heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
- types_do/1, sleeper/0, rpc_externals/0, memory_do/1,
+ types_do/1, sleeper/0, memory_do/1,
ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4
]).
@@ -2170,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) ->
@@ -4208,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) ->
@@ -5795,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.
@@ -5993,33 +5989,103 @@ make_ext_ref() ->
init_externals() ->
case get(externals) of
undefined ->
- SysDistSz = ets:info(sys_dist,size),
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line {ok, Node} = test_server:start_node(plopp, slave, [{args, " -pa " ++ Pa}]),
- ?line Res = case rpc:call(Node, ?MODULE, rpc_externals, []) of
- {badrpc, {'EXIT', E}} ->
- test_server:fail({rpcresult, E});
- R -> R
- end,
- ?line test_server:stop_node(Node),
-
- %% Wait for table 'sys_dist' to stabilize
- repeat_while(fun() ->
- case ets:info(sys_dist,size) of
- SysDistSz -> false;
- Sz ->
- io:format("Waiting for sys_dist to revert size from ~p to size ~p\n",
- [Sz, SysDistSz]),
- receive after 1000 -> true end
- end
- end),
+ OtherNode = {gurka@sallad, 1},
+ Res = {mk_pid(OtherNode, 7645, 8123),
+ mk_port(OtherNode, 187489773),
+ mk_ref(OtherNode, [262143, 1293964255, 3291964278])},
put(externals, Res);
{_,_,_} -> ok
end.
-rpc_externals() ->
- {self(), make_port(), make_ref()}.
+%%
+%% Node container constructor functions
+%%
+
+-define(VERSION_MAGIC, 131).
+-define(PORT_EXT, 102).
+-define(PID_EXT, 103).
+-define(NEW_REFERENCE_EXT, 114).
+
+uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
+ [(Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint32_be(Uint) ->
+ exit({badarg, uint32_be, [Uint]}).
+
+uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
+ [(Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint16_be(Uint) ->
+ exit({badarg, uint16_be, [Uint]}).
+
+uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
+ Uint band 16#ff;
+uint8(Uint) ->
+ exit({badarg, uint8, [Uint]}).
+
+mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
+ <<?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}, 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_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)),
+ 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.
+
make_sub_binary(Bin) when is_binary(Bin) ->
{_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 1de639a166..4a67d68428 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -65,19 +65,26 @@ wildcard_one(Config) when is_list(Config) ->
?line {ok,OldCwd} = file:get_cwd(),
?line Dir = filename:join(?config(priv_dir, Config), "wildcard_one"),
?line ok = file:make_dir(Dir),
+ do_wildcard_1(Dir,
+ fun(Wc) ->
+ filelib:wildcard(Wc, Dir, erl_prim_loader)
+ end),
?line file:set_cwd(Dir),
- ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc) end),
+ do_wildcard_1(Dir,
+ fun(Wc) ->
+ L = filelib:wildcard(Wc),
+ L = filelib:wildcard(Wc, erl_prim_loader),
+ L = filelib:wildcard(Wc, "."),
+ L = filelib:wildcard(Wc, Dir)
+ end),
?line file:set_cwd(OldCwd),
?line ok = file:del_dir(Dir),
ok.
wildcard_two(Config) when is_list(Config) ->
?line Dir = filename:join(?config(priv_dir, Config), "wildcard_two"),
- ?line DirB = unicode:characters_to_binary(Dir, file:native_name_encoding()),
?line ok = file:make_dir(Dir),
?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,Dir, X = filelib:wildcard(Wc, Dir)}]),X end),
- ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,DirB, X = filelib:wildcard(Wc, DirB)}]),
- [unicode:characters_to_list(Y,file:native_name_encoding()) || Y <- X] end),
?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end),
case os:type() of
{win32,_} ->
@@ -130,6 +137,9 @@ do_wildcard_2(Dir, Wcf) ->
?line ["abc","abcdef"] = Wcf("a*{def,}"),
?line ["abc","abcdef"] = Wcf("a*{,def}"),
+ %% Constant wildcard.
+ ["abcdef"] = Wcf("abcdef"),
+
%% Negative tests.
?line [] = Wcf("b*"),
?line [] = Wcf("bufflig"),
@@ -157,6 +167,8 @@ do_wildcard_4(Dir, Wcf) ->
All = ["a-","aA","aB","aC","a[","a]"],
?line Files = mkfiles(lists:reverse(All), Dir),
?line All = Wcf("a[][A-C-]"),
+ ["a-"] = Wcf("a[-]"),
+ ["a["] = Wcf("a["),
?line del(Files),
do_wildcard_5(Dir, Wcf).
@@ -173,11 +185,84 @@ do_wildcard_5(Dir, Wcf) ->
?line ["blurf/nisse"] = Wcf("*/nisse"),
?line [] = Wcf("mountain/*"),
?line [] = Wcf("xa/gurka"),
+ ["blurf/nisse"] = Wcf("blurf/nisse"),
%% 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),
+ do_wildcard_9(Dir, Wcf).
+
+do_wildcard_9(Dir, Wcf) ->
+ Dirs0 = ["lib","lib/app","lib/app/ebin"],
+ Dirs = [filename:join(Dir, D) || D <- Dirs0],
+ [ok = file:make_dir(D) || D <- Dirs],
+ Files0 = [filename:join("lib/app/ebin", F++".bar") ||
+ F <- ["abc","foo","foobar"]],
+ Files = [filename:join(Dir, F) || F <- Files0],
+ [ok = file:write_file(F, <<"some content\n">>) || F <- Files],
+ Files0 = Wcf("lib/app/ebin/*.bar"),
+
+ %% Cleanup.
+ del(Files),
+ [ok = file:del_dir(D) || D <- lists:reverse(Dirs)],
+ ok.
fold_files(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index 8817f5a55b..232df6a13f 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -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,50 +644,45 @@ 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;
_ ->
@@ -814,4 +699,3 @@ t_nativename_bin(Config) when is_list(Config) ->
?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..a637a8543b 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-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
@@ -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 c930d90e1c..dffeadb423 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -231,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.
@@ -1054,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]),
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..9f828c6d2d 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,12 @@
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,
+ printable_range/1,
+ io_lib_print_binary_depth_one/1, otp_10302/1, otp_10755/1,
+ otp_10836/1]).
+
+-export([pretty/2]).
%-define(debug, true).
@@ -62,7 +68,9 @@ 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,
+ printable_range,
+ io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836].
groups() ->
[].
@@ -892,7 +900,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 +2029,188 @@ 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.
+
+
+printable_range(doc) ->
+ "Check that the printable range set by the user actually works";
+printable_range(Suite) when is_list(Suite) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok, UNode} = test_server:start_node(printable_range_unicode, slave,
+ [{args, " +pc unicode -pa " ++ Pa}]),
+ {ok, LNode} = test_server:start_node(printable_range_latin1, slave,
+ [{args, " +pc latin1 -pa " ++ Pa}]),
+ {ok, DNode} = test_server:start_node(printable_range_default, slave,
+ [{args, " -pa " ++ Pa}]),
+ unicode = rpc:call(UNode,io,printable_range,[]),
+ latin1 = rpc:call(LNode,io,printable_range,[]),
+ latin1 = rpc:call(DNode,io,printable_range,[]),
+ test_server:stop_node(UNode),
+ test_server:stop_node(LNode),
+ {ok, UNode} = test_server:start_node(printable_range_unicode, slave,
+ [{args, " +pcunicode -pa " ++ Pa}]),
+ {ok, LNode} = test_server:start_node(printable_range_latin1, slave,
+ [{args, " +pclatin1 -pa " ++ Pa}]),
+ unicode = rpc:call(UNode,io,printable_range,[]),
+ latin1 = rpc:call(LNode,io,printable_range,[]),
+ {error, _} = test_server:start_node(printable_range_unnicode, slave,
+ [{args, " +pcunnicode -pa " ++ Pa}]),
+ PrettyOptions = [{column,1},
+ {line_length,109},
+ {depth,30},
+ {max_chars,60},
+ {record_print_fun,
+ fun(_,_) -> no end},
+ {encoding,unicode}],
+ 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib_pretty,print,
+ [{hello, [1024,1025]},
+ PrettyOptions]))),
+ 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib_pretty,print,
+ [{hello, [1024,1025]},
+ PrettyOptions]))),
+ 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib_pretty,print,
+ [{hello, [1024,1025]},
+ PrettyOptions]))),
+ 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib_pretty,print,
+ [{hello, <<1024/utf8,1025/utf8>>},
+ PrettyOptions]))),
+ 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib_pretty,print,
+ [{hello, <<1024/utf8,1025/utf8>>},
+ PrettyOptions]))),
+ 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib_pretty,print,
+ [{hello, <<1024/utf8,1025/utf8>>},
+ PrettyOptions]))),
+
+ 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib,format,
+ ["~tp",[{hello, [1024,1025]}]]))),
+ 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib,format,
+ ["~tp",[{hello, [1024,1025]}]]))),
+ 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib,format,
+ ["~tp",[{hello, [1024,1025]}]]))),
+ 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib,format,
+ ["~tp",
+ [{hello,
+ <<1024/utf8,1025/utf8>>}]]))),
+ 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib,format,
+ ["~tp",
+ [{hello,
+ <<1024/utf8,1025/utf8>>}]]))),
+ 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib,format,
+ ["~tp",
+ [{hello,
+ <<1024/utf8,1025/utf8>>}]]))),
+ test_server:stop_node(UNode),
+ test_server:stop_node(LNode),
+ test_server:stop_node(DNode),
+ 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) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ {ok, UNode} = test_server:start_node(printable_range_unicode, slave,
+ [{args, " +pc unicode -pa " ++ Pa}]),
+ {ok, LNode} = test_server:start_node(printable_range_latin1, slave,
+ [{args, " +pc latin1 -pa " ++ Pa}]),
+ "\"\x{400}\"" = rpc:call(UNode,?MODULE,pretty,["\x{400}", -1]),
+ "<<\"\x{400}\"/utf8>>" = rpc:call(UNode,?MODULE,pretty,
+ [<<"\x{400}"/utf8>>, -1]),
+
+ "<<\"\x{400}foo\"/utf8>>" = rpc:call(UNode,?MODULE,pretty,
+ [<<"\x{400}foo"/utf8>>, 2]),
+ "[1024]" = rpc:call(LNode,?MODULE,pretty,["\x{400}", -1]),
+ "<<208,128>>" = rpc:call(LNode,?MODULE,pretty,[<<"\x{400}"/utf8>>, -1]),
+
+ "<<208,...>>" = rpc:call(LNode,?MODULE,pretty,[<<"\x{400}foo"/utf8>>, 2]),
+ test_server:stop_node(UNode),
+ test_server:stop_node(LNode),
+
+ "<<\"äppl\"/utf8>>" = pretty(<<"äppl"/utf8>>, 2),
+ "<<\"äppl\"/utf8...>>" = pretty(<<"äpple"/utf8>>, 2),
+ "<<\"apel\">>" = pretty(<<"apel">>, 2),
+ "<<\"apel\"...>>" = pretty(<<"apelsin">>, 2),
+ "<<\"äppl\">>" = fmt("~tp", [<<"äppl">>]),
+ "<<\"äppl\"...>>" = fmt("~tP", [<<"äpple">>, 2]),
+ "<<0,0,0,0,0,0,1,0>>" = fmt("~p", [<<256:64/unsigned-integer>>]),
+ "<<0,0,0,0,0,0,1,0>>" = fmt("~tp", [<<256:64/unsigned-integer>>]),
+
+ Chars = lists:seq(0, 512), % just a few...
+ [] = [C || C <- Chars, S <- io_lib:write_char_as_latin1(C),
+ not is_latin1(S)],
+ L1 = [S || C <- Chars, S <- io_lib:write_char(C),
+ not is_latin1(S)],
+ L1 = lists:seq(256, 512),
+
+ [] = [C || C <- Chars, S <- io_lib:write_string_as_latin1([C]),
+ not is_latin1(S)],
+ L2 = [S || C <- Chars, S <- io_lib:write_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.
+
+otp_10836(doc) ->
+ "OTP-10836. ~ts extended to latin1";
+otp_10836(Suite) when is_list(Suite) ->
+ S = io_lib:format("~ts", [[<<"äpple"/utf8>>, <<"äpple">>]]),
+ "äppleäpple" = lists:flatten(S),
+ ok.
+
+otp_10755(doc) ->
+ "OTP-10755. The 'l' modifier";
+otp_10755(Suite) when is_list(Suite) ->
+ S = "string",
+ "\"string\"" = fmt("~p", [S]),
+ "[115,116,114,105,110,103]" = fmt("~lp", [S]),
+ "\"string\"" = fmt("~P", [S, 2]),
+ "[115|...]" = fmt("~lP", [S, 2]),
+ {'EXIT',{badarg,_}} = (catch fmt("~ltp", [S])),
+ {'EXIT',{badarg,_}} = (catch fmt("~tlp", [S])),
+ {'EXIT',{badarg,_}} = (catch fmt("~ltP", [S])),
+ {'EXIT',{badarg,_}} = (catch fmt("~tlP", [S])),
+ Text =
+ "-module(l_mod).\n"
+ "-export([t/0]).\n"
+ "t() ->\n"
+ " S = \"string\",\n"
+ " io:format(\"~ltp\", [S]),\n"
+ " io:format(\"~tlp\", [S]),\n"
+ " io:format(\"~ltP\", [S, 1]),\n"
+ " io:format(\"~tlP\", [S, 1]).\n",
+ {ok,l_mod,[{_File,Ws}]} = compile_file("l_mod.erl", Text, Suite),
+ ["format string invalid (invalid control ~lt)",
+ "format string invalid (invalid control ~tl)",
+ "format string invalid (invalid control ~lt)",
+ "format string invalid (invalid control ~tl)"] =
+ [lists:flatten(M:format_error(E)) || {_L,M,E} <- Ws],
+ ok.
+
+compile_file(File, Text, Config) ->
+ PrivDir = ?privdir(Config),
+ Fname = filename:join(PrivDir, File),
+ ok = file:write_file(Fname, Text),
+ try compile:file(Fname, [return])
+ after ok %file:delete(Fname)
+ end.
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index 17e69f7c1c..4ca8680fc9 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -716,7 +716,7 @@ binary_options(Config) when is_list(Config) ->
{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\">>"}
+ {getline, "<<\""++binary_to_list(<<"\345\344\366"/utf8>>)++"\\n\"/utf8>>"}
],[])
end,
%% And one with oldshell
@@ -736,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.
@@ -1784,8 +1784,8 @@ get_default_shell() ->
{putline, "whereis(user_drv)."},
{getline, "undefined"}],[]),
old
- catch E:R ->
- ?dbg({E,R}),
+ catch _E:_R ->
+ ?dbg({_E,_R}),
new
end.
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 192268f90e..a9ea78a58b 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -2969,15 +2969,6 @@ lookup1(Config) when is_list(Config) ->
[3] = lookup_keys(Q)
end, [{1,a},{3,3}])">>,
- {cres,
- <<"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}])">>,
- {warnings,[{3,erl_lint,deprecated_tuple_fun}]}},
-
<<"etsc(fun(E) ->
A = 3,
Q = qlc:q([X || X <- ets:table(E),
@@ -3442,14 +3433,6 @@ lookup2(Config) when is_list(Config) ->
[r] = lookup_keys(Q)
end, [{keypos,1}], [#r{}])">>,
{cres,
- <<"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{}])">>,
- {warnings,[{4,erl_lint,deprecated_tuple_fun}]}},
- {cres,
<<"etsc(fun(E) ->
Q = qlc:q([element(1, X) || X <- ets:table(E),
record(X, r)]),
@@ -3468,15 +3451,7 @@ lookup2(Config) when is_list(Config) ->
is_record(X, r)]),
[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),
- {erlang,is_record}(X, r)]),
- [r] = qlc:e(Q),
- [r] = lookup_keys(Q)
- end, [{keypos,1}], [#r{}])">>,
- {warnings,[{4,erl_lint,deprecated_tuple_fun}]}}
+ end, [{keypos,1}], [#r{}])">>
],
?line run(Config, <<"-record(r, {a}).\n">>, TsR),
@@ -6087,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) ->
@@ -6703,7 +6663,7 @@ otp_7714(Config) when is_list(Config) ->
{A,I1} <- ets:table(E1),
{B,I2} <- ets:table(E2),
I1 =:= I2],{join,merge}),
- [{a,1},{a,2},{a,3}] = qlc:e(Q),
+ [{a,1},{a,2},{a,3}] = lists:sort(qlc:e(Q)),
ets:delete(E1),
ets:delete(E2)">>],
?line run(Config, Ts).
@@ -6768,7 +6728,7 @@ otp_6674(Config) when is_list(Config) ->
[{join,lookup}]}}],
[]} = qlc:info(Q, {format,debug}),
{0,1,0,0} = join_info(Q),
- [{1.0,1},{2,2}] = qlc:e(Q),
+ [{1.0,1},{2,2}] = lists:sort(qlc:e(Q)),
ets:delete(E1),
ets:delete(E2)">>,
<<"E1 = ets:new(join, [ordered_set]),
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..c0cf1fc7e8 100644
--- a/lib/stdlib/test/sets_SUITE.erl
+++ b/lib/stdlib/test/sets_SUITE.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
@@ -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..86f009a8f9 100644
--- a/lib/stdlib/test/sets_test_lib.erl
+++ b/lib/stdlib/test/sets_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-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
@@ -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..990b1f5eb2 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. 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
@@ -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,149 @@ 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) ->
+ {ok,Node} = start_node(shell_suite_helper_2,
+ "-pa "++?config(priv_dir,Config)++
+ " +pc unicode"),
+ 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({Node,Test1}),
+
+ Test3 =
+ <<"io:setopts([{encoding,utf8}]).
+ rd(rec, {a = \"\\x{400}\"}).
+ ok = rp(#rec{}).">>,
+ "ok.\nrec\n#rec{a = \"\x{400}\"}.\nok.\n" = t({Node,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({Node,Test4}),
+
+ Test5 =
+ <<"begin
+ io:setopts([{encoding,utf8}]),
+ results(0),
+ A = [1024] = \"\\x{400}\",
+ b(),
+ h()
+ end.">>,
+ "A = \"\x{400}\".\nok.\n" = t({Node,Test5}),
+
+ %% One $" is "lost":
+ true =
+ "\x{400}\": command not found" =:=
+ prompt_err({Node,
+ <<"io:setopts([{encoding,utf8}]). v(\"\x{400}\")."/utf8>>,
+ unicode}),
+
+ "ok.\ndefault\n* Bad prompt function: \"\x{400}\".\n" =
+ t({Node,<<"io:setopts([{encoding,utf8}]). "
+ "shell:prompt_func(\"\x{400}\")."/utf8>>,
+ unicode}),
+ rpc:call(Node,shell, prompt_func, [default]),
+ _ = 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 <<\"�\">> / <<\"�\">>.\n" = t({Node,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({Node,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({Node,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({Node,Test13}),
+
+ test_server:stop_node(Node),
+ ok.
+
scan(B) ->
F = fun(Ts) ->
case erl_parse:parse_term(Ts) of
@@ -2770,37 +2894,46 @@ 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 ->
[]
end.
+t({Node,Bin,Enc}) when is_atom(Node),is_binary(Bin), is_atom(Enc) ->
+ t0({Bin,Enc}, fun() -> start_new_shell(Node) 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 +2977,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 +3000,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 +3053,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 +3069,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..c06ba545e7 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.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
@@ -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..bea2b3fb2a 100644
--- a/lib/stdlib/test/timer_SUITE.erl
+++ b/lib/stdlib/test/timer_SUITE.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
@@ -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/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 6524d83689..c1467697e3 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 1.18.3
+STDLIB_VSN = 1.19.1
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index fdfa414ad2..8ae69539a7 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>2012</year>
+ <year>2007</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,34 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 1.6.11</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Miscellaneous updates due to Unicode support. </p>
+ <p>
+ Own Id: OTP-10820</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Syntax_Tools 1.6.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 1.6.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile
index dca5e78be9..c9fbad8f9a 100644
--- a/lib/syntax_tools/src/Makefile
+++ b/lib/syntax_tools/src/Makefile
@@ -26,7 +26,7 @@ EBIN = ../ebin
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
-ERL_COMPILE_FLAGS += +warn_unused_vars +nowarn_shadow_vars +warn_unused_import # +warn_missing_spec +warn_untyped_record
+ERL_COMPILE_FLAGS += +nowarn_shadow_vars +warn_unused_import -Werror # +warn_missing_spec +warn_untyped_record
SOURCES=erl_syntax.erl erl_prettypr.erl erl_syntax_lib.erl \
erl_comment_scan.erl erl_recomment.erl erl_tidy.erl \
@@ -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 b3ced34c14..131be4e8e4 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -186,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;
@@ -419,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 b833e1c069..dae7530ce7 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -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})
@@ -272,7 +282,7 @@ join_lines([], Txt, L, Col, Ind) ->
%% =====================================================================
%% Utility functions for internal use
-filename([C|T]) when is_integer(C), C > 0, C =< 255 ->
+filename([C|T]) when is_integer(C), C > 0 ->
[C | filename(T)];
filename([]) ->
[];
@@ -281,7 +291,7 @@ filename(N) ->
exit(error).
error_read_file(Name) ->
- report_error("error reading file `~s'.", [Name]).
+ report_error("error reading file `~ts'.", [Name]).
report_error(S, Vs) ->
error_logger:error_msg(lists:concat([?MODULE, ": ", S, "\n"]), Vs).
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index f4bbf975c3..1ffcf31134 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -60,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{}.
%% =====================================================================
@@ -231,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
@@ -342,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
@@ -445,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("[]");
@@ -639,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
%%
@@ -811,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),
@@ -966,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_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 151f04b03b..bdb2b5bcd7 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -161,6 +161,7 @@
is_char/2,
char_value/1,
char_literal/1,
+ char_literal/2,
clause/2,
clause/3,
clause_body/1,
@@ -234,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,
@@ -271,6 +268,7 @@
is_string/2,
string_value/1,
string_literal/1,
+ string_literal/2,
text/1,
text_string/1,
try_expr/2,
@@ -449,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>
@@ -514,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
@@ -580,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;
@@ -1628,6 +1617,7 @@ float_literal(Node) ->
%%
%% @see char_value/1
%% @see char_literal/1
+%% @see char_literal/2
%% @see is_char/2
%% type(Node) = char
@@ -1687,13 +1677,34 @@ char_value(Node) ->
%% =====================================================================
%% @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).
+
+
+%% =====================================================================
+%% @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_char(char_value(Node));
+char_literal(Node, utf8) ->
+ io_lib:write_char(char_value(Node));
+char_literal(Node, latin1) ->
+ io_lib:write_char_as_latin1(char_value(Node)).
%% =====================================================================
@@ -1708,6 +1719,7 @@ char_literal(Node) ->
%%
%% @see string_value/1
%% @see string_literal/1
+%% @see string_literal/2
%% @see is_string/2
%% @see char/1
@@ -1768,13 +1780,32 @@ string_value(Node) ->
%% =====================================================================
%% @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).
+
+
+%% =====================================================================
+%% @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_string(string_value(Node));
+string_literal(Node, unicode) ->
+ io_lib:write_string(string_value(Node));
+string_literal(Node, latin1) ->
+ io_lib:write_string_as_latin1(string_value(Node)).
%% =====================================================================
@@ -3003,9 +3034,6 @@ 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.
@@ -3051,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)]
@@ -3065,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),
@@ -3210,53 +3225,6 @@ module_qualifier_body(Node) ->
%% =====================================================================
-%% @doc Creates an abstract qualified name. The result represents
-%% "<code><em>S1</em>.<em>S2</em>. ... .<em>Sn</em></code>", if
-%% `Segments' is `[S1, S2, ..., Sn]'.
-%%
-%% @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).
-
-
-%% =====================================================================
-%% @doc Returns the list of name segments of a
-%% `qualified_name' 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.
-
-
-%% =====================================================================
%% @doc Creates an abstract function definition. If `Clauses'
%% is `[C1, ..., Cn]', the result represents
%% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em>
@@ -4124,7 +4092,6 @@ record_access(Argument, Field) ->
%% @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(),
@@ -4601,50 +4568,6 @@ binary_comp_body(Node) ->
%% =====================================================================
-%% @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}.
-
-
-%% =====================================================================
-%% @doc Returns the body subtree of a `query_expr' 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.
-
-
-%% =====================================================================
%% @doc Creates an abstract Mnemosyne rule. If `Clauses' is
%% `[C1, ..., Cn]', the results represents
%% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em>
@@ -6068,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 ->
@@ -6312,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 ->
@@ -6444,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]]) ->
@@ -6788,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 36cd35f15d..2c94ac776d 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -2223,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 59cf6c0a92..0c149634f6 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -151,7 +151,7 @@ dir_1(Dir, Regexp, Env) ->
lists:foreach(fun (X) -> dir_2(X, Regexp, Dir, Env) end,
Files);
{error, _} ->
- report_error("error reading directory `~s'",
+ report_error("error reading directory `~ts'",
[filename(Dir)]),
exit(error)
end.
@@ -180,7 +180,7 @@ dir_2(Name, Regexp, Dir, Env) ->
dir_3(Name, Dir, Regexp, Env) ->
Dir1 = filename:join(Dir, Name),
- verbose("tidying directory `~s'.", [Dir1], Env#dir.options),
+ verbose("tidying directory `~ts'.", [Dir1], Env#dir.options),
dir_1(Dir1, Regexp, Env).
dir_4(File, Regexp, Env) ->
@@ -189,7 +189,7 @@ dir_4(File, Regexp, Env) ->
Opts = [{outfile, File}, {dir, ""} | Env#dir.options],
case catch file(File, Opts) of
{'EXIT', Value} ->
- warn("error tidying `~s'.~n~p", [File,Value], Opts);
+ warn("error tidying `~ts'.~n~p", [File,Value], Opts);
_ ->
ok
end;
@@ -314,7 +314,7 @@ file_2(Name, Opts) ->
end.
read_module(Name, Opts) ->
- verbose("reading module `~s'.", [filename(Name)], Opts),
+ verbose("reading module `~ts'.", [filename(Name)], Opts),
case epp_dodger:parse_file(Name, [no_fail]) of
{ok, Forms} ->
check_forms(Forms, Name),
@@ -335,7 +335,7 @@ check_forms(Fs, Name) ->
"unknown error"
end,
report_error({Name, erl_syntax:get_pos(F),
- "\n ~s"}, [S]),
+ "\n ~ts"}, [S]),
exit(error);
_ ->
ok
@@ -357,24 +357,26 @@ write_module(Tree, Name, Opts) ->
{value, directory} ->
ok;
{value, _} ->
- report_error("`~s' is not a directory.",
+ report_error("`~ts' is not a directory.",
[filename(Dir)]),
exit(error);
none ->
case file:make_dir(Dir) of
ok ->
- verbose("created directory `~s'.",
+ verbose("created directory `~ts'.",
[filename(Dir)], Opts),
ok;
E ->
report_error("failed to create "
- "directory `~s'.",
+ "directory `~ts'.",
[filename(Dir)]),
exit({make_dir, E})
end
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);
@@ -382,9 +384,9 @@ write_module(Tree, Name, Opts) ->
ok
end,
Printer = proplists:get_value(printer, Opts),
- FD = open_output_file(File),
- verbose("writing to file `~s'.", [File], Opts),
- V = (catch {ok, output(FD, Printer, Tree, Opts)}),
+ FD = open_output_file(File, Encoding),
+ verbose("writing to file `~ts'.", [File], Opts),
+ V = (catch {ok, output(FD, Printer, Tree, Opts++Encoding)}),
ok = file:close(FD),
case V of
{ok, _} ->
@@ -432,8 +434,8 @@ file_type(Name, Links) ->
throw(R)
end.
-open_output_file(FName) ->
- case catch file:open(FName, [write]) of
+open_output_file(FName, Options) ->
+ case catch file:open(FName, [write]++Options) of
{ok, FD} ->
FD;
{error, R} ->
@@ -469,7 +471,7 @@ backup_file_1(Name, Opts) ->
filename:basename(Name) ++ Suffix),
case catch file:rename(Name, Dest) of
ok ->
- verbose("made backup of file `~s'.", [Name], Opts);
+ verbose("made backup of file `~ts'.", [Name], Opts);
{error, R} ->
error_backup_file(Name),
exit({error, R});
@@ -1802,7 +1804,7 @@ get_free_vars_1([{free, B} | _Bs]) -> B;
get_free_vars_1([_ | Bs]) -> get_free_vars_1(Bs);
get_free_vars_1([]) -> [].
-filename([C | T]) when is_integer(C), C > 0, C =< 255 ->
+filename([C | T]) when is_integer(C), C > 0 ->
[C | filename(T)];
filename([H|T]) ->
filename(H) ++ filename(T);
@@ -1837,17 +1839,17 @@ report_export_vars(F, L, Type, Opts) ->
[Type], Opts).
error_read_file(Name) ->
- report_error("error reading file `~s'.", [filename(Name)]).
+ report_error("error reading file `~ts'.", [filename(Name)]).
error_write_file(Name) ->
- report_error("error writing to file `~s'.", [filename(Name)]).
+ report_error("error writing to file `~ts'.", [filename(Name)]).
error_backup_file(Name) ->
- report_error("could not create backup of file `~s'.",
+ report_error("could not create backup of file `~ts'.",
[filename(Name)]).
error_open_output(Name) ->
- report_error("cannot open file `~s' for output.", [filename(Name)]).
+ report_error("cannot open file `~ts' for output.", [filename(Name)]).
verbosity(Opts) ->
case proplists:get_bool(quiet, Opts) of
@@ -1906,9 +1908,9 @@ format({"", L, D}, Vs) when is_integer(L), L > 0 ->
format({"", _L, D}, Vs) ->
format(D, Vs);
format({F, L, D}, Vs) when is_integer(L), L > 0 ->
- [io_lib:fwrite("~s:~w: ", [filename(F), L]), format(D, Vs)];
+ [io_lib:fwrite("~ts:~w: ", [filename(F), L]), format(D, Vs)];
format({F, _L, D}, Vs) ->
- [io_lib:fwrite("~s: ", [filename(F)]), format(D, Vs)];
+ [io_lib:fwrite("~ts: ", [filename(F)]), format(D, Vs)];
format(S, Vs) when is_list(S) ->
[io_lib:fwrite(S, Vs), $\n].
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 37e561cbbe..d385c2b690 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -341,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)].
@@ -459,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}.
%% =====================================================================
@@ -2512,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
@@ -2526,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.
@@ -2648,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)
@@ -2706,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.
@@ -2728,21 +2749,22 @@ read_module(Name, Options) ->
end.
read_module_1(Name, Options) ->
- verbose("reading module `~s'.", [filename(Name)], Options),
- Forms = read_module_2(Name, Options),
+ verbose("reading module `~ts'.", [filename(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)
@@ -2772,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 `~ts' at line ~w:\n ~ts",
[filename(File), erl_syntax:get_pos(F), S]),
exit(error);
_ ->
@@ -2819,18 +2841,18 @@ write_module(Tree, Name, Dir, Opts) ->
{value, directory} ->
ok;
{value, _} ->
- report_error("`~s' is not a directory.",
+ report_error("`~ts' is not a directory.",
[Dir1]),
exit(error);
none ->
case file:make_dir(Dir1) of
ok ->
- verbose("created directory `~s'.",
+ verbose("created directory `~ts'.",
[Dir1], Opts),
ok;
E ->
report_error("failed to create "
- "directory `~s'.",
+ "directory `~ts'.",
[Dir1]),
exit({make_dir, E})
end
@@ -2847,7 +2869,8 @@ write_module(Tree, Name, Dir, Opts) ->
end,
Printer = proplists:get_value(printer, Opts),
FD = open_output_file(File),
- verbose("writing to file `~s'.", [File], Opts),
+ ok = output_encoding(FD, Opts),
+ verbose("writing to file `~ts'.", [File], Opts),
V = (catch {ok, output(FD, Printer, Tree, Opts)}),
ok = file:close(FD),
case V of
@@ -2888,7 +2911,7 @@ backup_file_1(Name, Opts) ->
filename:basename(Name1) ++ Suffix),
case catch file:rename(Name1, Dest) of
ok ->
- verbose("made backup of file `~s'.", [Name1], Opts);
+ verbose("made backup of file `~ts'.", [Name1], Opts);
{error, R} ->
error_backup_file(Name1),
exit({error, R});
@@ -2933,7 +2956,7 @@ timestamp() ->
"~2.2.0w:~2.2.0w:~2.2.0w.",
[Yr, Mth, Dy, Hr, Mt, Sc])).
-filename([C | T]) when is_integer(C), C > 0, C =< 255 ->
+filename([C | T]) when is_integer(C), C > 0 ->
[C | filename(T)];
filename([H|T]) ->
filename(H) ++ filename(T);
@@ -3013,19 +3036,19 @@ warning_apply_2(Module, Target) ->
"possibly unsafe in `~s'.", [Module, Target]).
error_open_output(Name) ->
- report_error("cannot open file `~s' for output.", [filename(Name)]).
+ report_error("cannot open file `~ts' for output.", [filename(Name)]).
error_read_file(Name) ->
- report_error("error reading file `~s'.", [filename(Name)]).
+ report_error("error reading file `~ts'.", [filename(Name)]).
error_read_file_info(Name) ->
- report_error("error getting file info: `~s'.", [filename(Name)]).
+ report_error("error getting file info: `~ts'.", [filename(Name)]).
error_write_file(Name) ->
- report_error("error writing to file `~s'.", [filename(Name)]).
+ report_error("error writing to file `~ts'.", [filename(Name)]).
error_backup_file(Name) ->
- report_error("could not create backup of file `~s'.",
+ report_error("could not create backup of file `~ts'.",
[filename(Name)]).
verbose(S, Opts) ->
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 8f774c5d75..01569687bf 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.9
+SYNTAX_TOOLS_VSN = 1.6.11
diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml
index 6a9add044a..b35929f1e6 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,208 @@
<file>notes.xml</file>
</header>
+<section><title>Test_Server 3.6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The unicode update of test_server for R16A introduced a
+ few potential errors when logging to files. Sometimes ~tp
+ or ~ts was used for formatting also when writing to files
+ that were not opened with the {encoding,utf8} option. If
+ then the argument contained unicode characters above 255,
+ the file descriptor would crash. This has been corrected
+ by the following modifications: <list> <item> Since the
+ 'unexpected_io' log file is used only when the test case
+ HTML file is not available (e.g. between test cases),
+ this file is now also a HTML file and as other
+ test_server HTML logs it is always UTF-8 encoded </item>
+ <item> Since it is possible to change which information
+ is going to which log file (with
+ test_server_ctrl:set_levels/3), we do not have full
+ control over which information is written to which file.
+ This means that any printout could be written to the
+ 'major' log file (suite.log), which was earlier encoded
+ as latin1. To avoid crashing this file descriptor due to
+ unicode strings, the 'major' log file is now also encoded
+ in UTF-8 (possible incopatibility). </item> <item> The
+ cross_cover.info file is no longer a text file which can
+ be read with file:consult/1, instead it is written as a
+ pure binary file using term_to_binary when writing and
+ binary_to_term when reading. </item> <item> The encoding
+ of the file named 'last_name', which only content is the
+ path to the last run.&lt;timestamp&gt; directory, is now
+ dependent on the file name mode of the VM. If file names
+ are expected to be unicode, then the 'last_name' file is
+ UTF-8 encoded, else it is latin1 encoded. </item> </list></p>
+ <p>
+ Also, ~tp has been changed back to ~p unless it is
+ somehow likely that the argument includes strings. It is
+ not obvious that this is the correct thing to do, but
+ some decission had to be taken...</p>
+ <p>
+ Own Id: OTP-10780</p>
+ </item>
+ <item>
+ <p>
+ Using the force_stop flag/option to interrupt a test run
+ caused a crash in Common Test. This problem has been
+ solved.</p>
+ <p>
+ Own Id: OTP-10832</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Test_Server 3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Line numbering of erlang files that were not correctly
+ indented could be wrong after coverting to html with
+ erl2html2:convert/[2,3] (the source code pointed to from
+ the test case). This has been corrected.</p>
+ <p>
+ Also, there are now link targets for each line and not
+ only for each 10th line, and link targets for functions
+ now include the arity and not only the function name
+ (e.g. func/1 has a link target "func-1").</p>
+ <p>
+ Own Id: OTP-9710 Aux Id: seq11945, kunagi-201 [112] </p>
+ </item>
+ <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>
+ 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>
+ If the test suite itself was included in code coverage
+ analysis, then the test_server would not manage to set
+ data_dir correctly for the test. This has been corrected.</p>
+ <p>
+ Own Id: OTP-9956 Aux Id: kunagi-207 [118] </p>
+ </item>
+ <item>
+ <p>
+ Any call to test_server:break/1 should cancel all active
+ timetramps. However, in some cases
+ Suite:end_per_testcase/2 is executed on a different
+ process than the test case itself, and if
+ test_server:break/1 was called from there, the timetraps
+ were not cancelled. This has been corrected.</p>
+ <p>
+ Own Id: OTP-10046 Aux Id: kunagi-174 [85] </p>
+ </item>
+ <item>
+ <p>When a test case failed because of a timetrap time
+ out, the <c>Config</c> data for the case was lost in the
+ following call to <c>end_per_testcase/2</c>, and also in
+ calls to the CT Hook function
+ <c>post_end_per_testcase/4</c>. This problem has been
+ solved and the <c>Config</c> data is now correctly passed
+ to the above functions after a timetrap timeout
+ failure.</p>
+ <p>
+ Own Id: OTP-10070 Aux Id: kunagi-175 [86] </p>
+ </item>
+ <item>
+ <p>In test_server, the same process would supervise the
+ currently running test case and be group leader (and IO
+ server) for the test case. Furthermore, when running
+ parallel test cases, new temporary supervisor/group
+ leader processes were spawned and the process that was
+ group leader for sequential test cases would not be
+ active. That would lead to several problems:</p>
+ <p>* Processes started by init_per_suite will inherit the
+ group leader of the init_per_suite process (and that
+ group leader would not process IO requests when parallel
+ test cases was running). If later a parallel test case
+ caused such a processto print using (for example)
+ io:format/2, the calling would hang.</p>
+ <p>* Similarly, if a process was spawned from a parallel
+ test case, it would inherit the temporary group leader
+ for that parallel test case. If that spawned process
+ later - when the group of parallel tests have finished -
+ attempted to print something, its group leader would be
+ dead and there would be <c>badarg</c> exception.</p>
+ <p>Those problems have been solved by having group
+ leaders separate from the processes that supervises the
+ test cases, and keeping temporary group leader process
+ for parallel test cases alive until no more process in
+ the system use them as group leaders.</p>
+ <p>Also, a new <c>unexpected_io.log</c> log file
+ (reachable from the summary page of each test suite) has
+ been introduced. All unexpected IO will be printed into
+ it(for example, IO to a group leader for a parallel test
+ case that has finished).</p>
+ <p>
+ Own Id: OTP-10101 Aux Id: OTP-10125 </p>
+ </item>
+ <item>
+ <p>
+ The stability of <c>common_test</c> and
+ <c>test_server</c> when running test cases in parallel
+ has been improved.</p>
+ <p>
+ Own Id: OTP-10480 Aux Id: kunagi-318 [229] </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added a general framework for executing benchmarks of
+ Erlang/OTP. Benchmarks for the Erlang VM and mnesia have
+ been incorporated in the framework. </p>
+ <p>
+ For details about how to add more benchmarks see
+ $ERL_TOP/HOWTO/BENCHMARKS.md in the source distribution.</p>
+ <p>
+ Own Id: OTP-10156</p>
+ </item>
+ <item>
+ <p>
+ Update common test modules to handle unicode <list>
+ <item> Use UTF-8 encoding for all HTML files, except the
+ HTML version of the test suite generated with
+ erl2html2:convert, which will have the same encoding as
+ the original test suite (.erl) file. </item> <item>
+ Encode link targets in HTML files with
+ test_server_ctrl:uri_encode/1. </item> <item> Use unicode
+ modifier 't' with ~s when appropriate. </item> <item> Use
+ unicode:characters_to_list and
+ unicode:characters_to_binary for conversion between
+ binaries and strings instead of binary_to_list and
+ list_to_binary. </item> </list></p>
+ <p>
+ Own Id: OTP-10783</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Test_Server 3.5.3</title>
<section><title>Improvements and New Features</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 41bc0bcc75..af96f1fe7e 100644
--- a/lib/test_server/doc/src/test_server_ctrl.xml
+++ b/lib/test_server/doc/src/test_server_ctrl.xml
@@ -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>
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 513720dc04..ebc5f5b71b 100644
--- a/lib/test_server/src/Makefile
+++ b/lib/test_server/src/Makefile
@@ -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
@@ -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
@@ -92,7 +91,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -I../include
+ERL_COMPILE_FLAGS += -I../include -Werror
# ----------------------------------------------------
# Targets
@@ -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/config.guess b/lib/test_server/src/config.guess
index 38a833903b..f475ceb413 100755
--- a/lib/test_server/src/config.guess
+++ b/lib/test_server/src/config.guess
@@ -1,14 +1,12 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-05-17'
+timestamp='2013-02-12'
# 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
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@@ -17,26 +15,22 @@ timestamp='2007-05-17'
# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
#
-# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# Please send patches with a ChangeLog entry to [email protected].
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -56,8 +50,7 @@ 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.
+Copyright 1992-2013 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."
@@ -144,7 +137,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
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*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -170,7 +163,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 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
+ | grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -180,7 +173,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
;;
esac
# The OS release
@@ -201,6 +194,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -223,7 +220,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -269,7 +266,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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 ;;
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
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
@@ -295,12 +295,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-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:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -324,14 +324,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
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/[^.]*//'`
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
@@ -375,23 +394,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -461,8 +480,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ # 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 ] || \
@@ -475,7 +494,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -532,7 +551,7 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[45])
+ *:AIX:*:[4567])
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
@@ -575,52 +594,52 @@ EOF
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" ;;
+ 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
+ esac ;;
+ esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ sed 's/^ //' << EOF >$dummy.c
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
+ #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);
+ 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);
- }
+ 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
@@ -640,7 +659,7 @@ EOF
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
+ grep -q __LP64__
then
HP_ARCH="hppa2.0w"
else
@@ -711,22 +730,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -750,14 +769,14 @@ EOF
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 ;;
+ 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}"
+ 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}
@@ -769,40 +788,51 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
amd64)
echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
+ exit ;;
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
+ i*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ # 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)
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
- EM64T | authenticamd)
+ authenticamd | genuineintel | EM64T)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
+ IA64)
+ echo ia64-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 ;;
+ 8664:Windows_NT:*)
+ echo x86_64-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
@@ -832,20 +862,68 @@ EOF
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
- arm*:Linux:*:*)
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
echo ${UNAME_MACHINE}-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 -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -856,74 +934,36 @@ EOF
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:*:*)
+ mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
- #undef mips64
- #undef mips64el
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
+ CPU=${UNAME_MACHINE}el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
+ CPU=${UNAME_MACHINE}
#else
CPU=
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
+ or1k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
+ padre:Linux:*:*)
+ echo sparc-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}
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
@@ -933,14 +973,17 @@ EOF
*) echo hppa-unknown-linux-gnu ;;
esac
exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -948,81 +991,18 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- tile:Linux:*:*)
- echo tile-unknown-linux-gnu
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-gnu
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- xtensa:Linux:*:*)
- echo xtensa-unknown-linux-gnu
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-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
@@ -1030,11 +1010,11 @@ EOF
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,
+ # 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.
+ # Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1051,7 +1031,7 @@ EOF
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
@@ -1066,7 +1046,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1094,10 +1074,13 @@ EOF
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 ;;
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1132,8 +1115,18 @@ EOF
/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; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ 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; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
@@ -1146,7 +1139,7 @@ EOF
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
@@ -1166,10 +1159,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- echo i586-unisys-sysv4
- 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
@@ -1195,11 +1188,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv${UNAME_RELEASE}
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1209,6 +1202,12 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1236,6 +1235,16 @@ EOF
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
unknown) UNAME_PROCESSOR=powerpc ;;
esac
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1251,7 +1260,10 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
@@ -1296,13 +1308,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ 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`
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1317,11 +1329,14 @@ EOF
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ 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_
@@ -1339,11 +1354,11 @@ main ()
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
- "4"
+ "4"
#else
- ""
+ ""
#endif
- ); exit (0);
+ ); exit (0);
#endif
#endif
@@ -1477,9 +1492,9 @@ 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
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
If the version you run ($0) is already up to date, please
send the following data and any information you think might be
diff --git a/lib/test_server/src/config.sub b/lib/test_server/src/config.sub
index f43233b104..bb6edbdb47 100755
--- a/lib/test_server/src/config.sub
+++ b/lib/test_server/src/config.sub
@@ -1,44 +1,40 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2007-04-29'
+timestamp='2013-02-12'
-# 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
+# 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 3 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.
+# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Please send patches with a ChangeLog entry to [email protected].
#
# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
# 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.
@@ -72,8 +68,7 @@ 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.
+Copyright 1992-2013 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."
@@ -120,12 +115,18 @@ esac
# 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* | \
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -148,10 +149,13 @@ case $os in
-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)
+ -apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
+ -bluegene*)
+ os=-cnk
+ ;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
@@ -166,10 +170,10 @@ case $os in
os=-chorusos
basic_machine=$1
;;
- -chorusrdb)
- os=-chorusrdb
+ -chorusrdb)
+ os=-chorusrdb
basic_machine=$1
- ;;
+ ;;
-hiux*)
os=-hiuxwe2
;;
@@ -214,6 +218,12 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
-lynx*)
os=-lynxos
;;
@@ -238,24 +248,34 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 | aarch64_be \
| 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 \
+ | arc \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | be32 | be64 \
| bfin \
| c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
+ | epiphany \
| fido | fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
- | mips64vr | mips64vrel \
+ | mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
@@ -266,31 +286,45 @@ case $basic_machine in
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
+ | moxie \
| mt \
| msp430 \
- | nios | nios2 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | or32 \
+ | open8 \
+ | or1k | or32 \
| pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
+ | rl78 | rx \
| score \
- | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | 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 \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
- | z8k)
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
basic_machine=$basic_machine-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -300,6 +334,21 @@ case $basic_machine in
basic_machine=mt-unknown
;;
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-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.
@@ -314,29 +363,37 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* | aarch64_be-* \
| 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-* \
+ | be32-* | be64-* \
| bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
| 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-* \
+ | hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
+ | mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
@@ -347,31 +404,41 @@ case $basic_machine in
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
- | nios-* | nios2-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | 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-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
| tron-* \
- | v850-* | v850e-* | vax-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
| we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
- | xstormy16-* | xtensa-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
| ymp-* \
- | z8k-*)
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -389,7 +456,7 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
- abacus)
+ abacus)
basic_machine=abacus-unknown
;;
adobe68k)
@@ -435,6 +502,10 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-bsd
;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -443,10 +514,35 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -475,8 +571,8 @@ case $basic_machine in
basic_machine=craynv-cray
os=-unicosmp
;;
- cr16c)
- basic_machine=cr16c-unknown
+ cr16 | cr16-*)
+ basic_machine=cr16-unknown
os=-elf
;;
crds | unos)
@@ -514,6 +610,10 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
@@ -629,7 +729,6 @@ case $basic_machine in
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
@@ -668,6 +767,14 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
m88k-omron*)
basic_machine=m88k-omron
;;
@@ -679,6 +786,13 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
mingw32)
basic_machine=i386-pc
os=-mingw32
@@ -715,10 +829,18 @@ case $basic_machine in
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -783,6 +905,12 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@@ -813,6 +941,14 @@ case $basic_machine in
basic_machine=i860-intel
os=-osf
;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
pbd)
basic_machine=sparc-tti
;;
@@ -857,9 +993,10 @@ case $basic_machine in
;;
power) basic_machine=power-ibm
;;
- ppc) basic_machine=powerpc-unknown
+ ppc | ppcbe) basic_machine=powerpc-unknown
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
@@ -884,7 +1021,11 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
- rdos)
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
basic_machine=i386-pc
os=-rdos
;;
@@ -953,6 +1094,9 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sun2)
basic_machine=m68000-sun
;;
@@ -1009,17 +1153,9 @@ case $basic_machine in
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
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
;;
tx39)
basic_machine=mipstx39-unknown
@@ -1027,10 +1163,6 @@ case $basic_machine in
tx39el)
basic_machine=mipstx39el-unknown
;;
- tile*)
- basic_machine=tile-tilera
- os=-linux-gnu
- ;;
toad1)
basic_machine=pdp10-xkl
os=-tops20
@@ -1092,6 +1224,9 @@ case $basic_machine in
xps | xps100)
basic_machine=xps100-honeywell
;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1100,6 +1235,10 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
none)
basic_machine=none-none
os=-none
@@ -1138,7 +1277,7 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
- sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1185,9 +1324,12 @@ esac
if [ x"$os" != x"" ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
@@ -1208,21 +1350,23 @@ case $os in
# 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* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
+ | -aos* | -aros* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
+ | -bitrig* | -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* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
@@ -1230,7 +1374,7 @@ case $os in
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1269,7 +1413,7 @@ case $os in
-opened*)
os=-openedition
;;
- -os400*)
+ -os400*)
os=-os400
;;
-wince*)
@@ -1318,7 +1462,7 @@ case $os in
-sinix*)
os=-sysv4
;;
- -tpf*)
+ -tpf*)
os=-tpf
;;
-triton*)
@@ -1354,12 +1498,14 @@ case $os in
-aros*)
os=-aros
;;
- -kaos*)
- os=-kaos
- ;;
-zvmoe)
os=-zvmoe
;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
+ ;;
-none)
;;
*)
@@ -1382,10 +1528,10 @@ else
# system, and we'll never get to this point.
case $basic_machine in
- score-*)
+ score-*)
os=-elf
;;
- spu-*)
+ spu-*)
os=-elf
;;
*-acorn)
@@ -1397,8 +1543,20 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
- c4x-* | tic4x-*)
- os=-coff
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
@@ -1418,14 +1576,11 @@ case $basic_machine in
;;
m68000-sun)
os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
;;
m68*-cisco)
os=-aout
;;
- mep-*)
+ mep-*)
os=-elf
;;
mips*-cisco)
@@ -1434,6 +1589,9 @@ case $basic_machine in
mips*-*)
os=-elf
;;
+ or1k-*)
+ os=-elf
+ ;;
or32-*)
os=-coff
;;
@@ -1452,7 +1610,7 @@ case $basic_machine in
*-ibm)
os=-aix
;;
- *-knuth)
+ *-knuth)
os=-mmixware
;;
*-wec)
@@ -1557,7 +1715,7 @@ case $basic_machine in
-sunos*)
vendor=sun
;;
- -aix*)
+ -cnk*|-aix*)
vendor=ibm
;;
-beos*)
@@ -1628,3 +1786,4 @@ exit
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
+
diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl
index 6891e87e48..9c0ca64173 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-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,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,192 @@ convert(File, Dest) ->
%%
%% FIXME: The colours should *really* be set with
%% stylesheets...
+ %%
+ %% The html file is written with the same encoding as the input file.
+ Encoding = encoding(File),
Header = ["<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\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=",html_encoding(Encoding),"\"/>\n"
+ "<title>", to_raw_list(File,Encoding), "</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,encoding(File),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,Encoding,Functions) ->
+ build_html(SFd,DFd,Encoding,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,Encoding,{ok,Str},L,[{F,A,L}|Functions],_IsFuncDef) ->
+ FALink = http_uri:encode(F++"-"++integer_to_list(A)),
+ file:write(DFd,["<a name=\"",to_raw_list(FALink,Encoding),"\"/>"]),
+ build_html(SFd,DFd,Encoding,{ok,Str},L,Functions,true);
+build_html(SFd,DFd,Encoding,{ok,Str},L,[{clause,L}|Functions],_IsFuncDef) ->
+ build_html(SFd,DFd,Encoding,{ok,Str},L,Functions,true);
+build_html(SFd,DFd,Encoding,{ok,Str},L,Functions,IsFuncDef) ->
+ LStr = line_number(L),
+ Str1 = line(Str,IsFuncDef),
+ file:write(DFd,[LStr,Str1]),
+ build_html(SFd,DFd,Encoding,file:read_line(SFd),L+1,Functions,false);
+build_html(_SFd,_DFd,_Encoding,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,": "].
-footer(_Lines) ->
+line(Str,IsFuncDef) ->
+ Str1 = htmlize(Str),
+ possibly_enhance(Str1,IsFuncDef).
+
+%%%-----------------------------------------------------------------
+%%% 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) ->
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end.
+
+%%%-----------------------------------------------------------------
+%%% Covert encoding atom to string for use in HTML header
+html_encoding(latin1) ->
+ "iso-8859-1";
+html_encoding(utf8) ->
+ "utf-8".
+
+%%%-----------------------------------------------------------------
+%%% Convert a string to a list of raw printable characters in the
+%%% given encoding. This is necessary since the files (source and
+%%% destination) are both opened in raw mode (default encoding). Byte
+%%% by byte is read from source and written to the destination. This
+%%% conversion is needed when printing data that is not first read
+%%% from the source.
+%%%
+%%% Example: if the encoding of the file is utf8, and we have a string
+%%% containing "�" = [229], then we need to convert this to [195,165]
+%%% before writing. Note that this conversion is only necessary
+%%% because the destination file is not (necessarily) opened with utf8
+%%% encoding - it is opened with default encoding in order to allow
+%%% raw file mode and byte by byte copying from source.
+to_raw_list(X,latin1) when is_list(X) ->
+ X;
+to_raw_list(X,utf8) when is_list(X) ->
+ binary_to_list(unicode:characters_to_binary(X)).
diff --git a/lib/test_server/src/test_server.app.src b/lib/test_server/src/test_server.app.src
index faf7db835e..163f370a47 100644
--- a/lib/test_server/src/test_server.app.src
+++ b/lib/test_server/src/test_server.app.src
@@ -1,7 +1,7 @@
% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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,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 17c5f5b253..4c39c604a2 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-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
@@ -20,15 +20,12 @@
-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]).
@@ -60,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(),
@@ -118,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}
@@ -299,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",[]),
@@ -313,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",[]),
@@ -325,15 +123,16 @@ cover_compile({App,all,Include,Cross}) ->
io:format("\nWARNING: All modules in \'~w\' are excluded\n"
"Only cover compiling modules in include list "
"and the modules\nin the cross cover file:\n"
- "~p\n", [App,CompileMods]),
+ "~tp\n", [App,CompileMods]),
do_cover_compile(CompileMods),
io:fwrite("done\n\n",[]),
{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]),
@@ -344,7 +143,7 @@ cover_compile({App,Exclude,Include,Cross}) ->
[App,length(CompileMods)]),
io:format("\nWARNING: Can't find lib_dir for \'~w\'\n"
"Only cover compiling modules in include list: "
- "~p\n", [App,Include]),
+ "~tp\n", [App,Include]),
do_cover_compile(CompileMods),
io:fwrite("done\n\n",[]),
{ok,Include}
@@ -354,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",[]),
@@ -377,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
@@ -416,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
@@ -432,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} ->
@@ -483,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) ->
@@ -502,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
@@ -513,7 +347,7 @@ unstick_all_sticky(Node) ->
false
end
end,
- cover:modules()).
+ rpc:call(MainCoverNode,cover,modules,[])).
stick_all_sticky(Node,Sticky) ->
lists:foreach(
@@ -524,7 +358,7 @@ stick_all_sticky(Node,Sticky) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData,RejectIoReqs) ->
+%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData) ->
%% {Time,Value,Loc,Opts,Comment} | {died,Reason,unknown,Comment}
%%
%% Time = float() (seconds)
@@ -538,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,12 +392,9 @@ stick_all_sticky(Node,Sticky) ->
%% ScaleTimetrap indicates if test_server should attemp to automatically
%% compensate timetraps for runtime delays introduced by e.g. tools like
%% cover.
-%%
-%% RejectIoReqs (bool) is information about whether printouts to stdout
-%% should be visible in the minor log file or not.
run_test_case_apply({CaseNum,Mod,Func,Args,Name,
- RunInit,TimetrapData,RejectIoReqs}) ->
+ RunInit,TimetrapData}) ->
purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]),
case os:getenv("TS_RUN_VALGRIND") of
false ->
@@ -576,40 +406,29 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,
test_server_h:testcase({Mod,Func,1}),
ProcBef = erlang:system_info(process_count),
Result = run_test_case_apply(Mod, Func, Args, Name, RunInit,
- TimetrapData, RejectIoReqs),
+ TimetrapData),
ProcAft = erlang:system_info(process_count),
purify_new_leaks(),
DetFail = get(test_server_detected_fail),
{Result,DetFail,ProcBef,ProcAft}.
-run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData, RejectIoReqs) ->
- case get(test_server_job_dir) of
- undefined ->
- %% i'm a local target
- do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
- TimetrapData, RejectIoReqs);
- 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, RejectIoReqs);
- _other ->
- do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
- TimetrapData, RejectIoReqs)
- end
- end.
-do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
- TimetrapData, RejectIoReqs) ->
+-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) ->
{ok,Cwd} = file:get_cwd(),
Args2Print = case Args of
[Args1] when is_list(Args1) ->
@@ -617,16 +436,13 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
_ ->
Args
end,
- print(minor, "Test case started with:\n~s:~s(~p)\n", [Mod,Func,Args2Print]),
- print(minor, "Current directory is ~p\n", [Cwd]),
+ print(minor, "Test case started with:\n~w:~w(~tp)\n", [Mod,Func,Args2Print]),
+ print(minor, "Current directory is ~tp\n", [Cwd]),
print_timestamp(minor,"Started at "),
print(minor, "", [], internal_raw),
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() ->
@@ -634,10 +450,10 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
RunInit, TimetrapData,
LogOpts, TCCallback)
end),
- group_leader(OldGLeader, self()),
put(test_server_detected_fail, []),
- run_test_case_msgloop(Ref, Pid, false, RejectIoReqs, 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
@@ -648,32 +464,23 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
%% 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, RejectIoReqs, 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,RejectIoReqs,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,RejectIoReqs,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);
@@ -683,142 +490,49 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, 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,RejectIoReqs,Terminate,
- NewComment,CurrConf,Status);
- {permit_io,FromPid} ->
- put({permit_io,FromPid},true),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {io_request,From,ReplyAs,{put_chars,Bytes}} ->
- run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Bytes,From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {io_request,From,ReplyAs,{put_chars,unicode,Bytes}} ->
- run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
- unicode_to_latin1(Bytes),From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {io_request,From,ReplyAs,{put_chars,latin1,Bytes}} ->
- run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Bytes,From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {structured_io,ClientPid,Msg} ->
- output(Msg, ClientPid),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {capture,NewCapture} ->
- run_test_case_msgloop(Ref,Pid,NewCapture,RejectIoReqs,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,RejectIoReqs,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,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {printout,Detail,Format,Args} ->
- print(Detail,Format,Args),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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,RejectIoReqs,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,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
- {set_curr_conf,From,NewCurrConf} ->
- From ! {self(),set_curr_conf,ok},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,NewCurrConf,Status);
- {make_priv_dir,From} when CurrConf == undefined ->
- From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate,
- Comment,CurrConf,Status);
+ 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 ->
@@ -832,212 +546,63 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate,
end
end,
From ! {self(),make_priv_dir,Result},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,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,RejectIoReqs,
- {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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- {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,RejectIoReqs,
- {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,RejectIoReqs,
- 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
@@ -1046,8 +611,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate,
ignore ->
ok
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
- 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
@@ -1062,77 +626,123 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate,
ignore ->
ok
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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,RejectIoReqs,
- 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(From,ReplyAs,CaptureStdout,RejectIoReqs,
- Msg,From,Func) ->
- case Msg of
- {'EXIT',_} ->
- From ! {io_reply,ReplyAs,{error,Func}};
- _ ->
- From ! {io_reply,ReplyAs,ok}
- end,
- Proceed = if RejectIoReqs -> get({permit_io,From});
- true -> true
- end,
- if Proceed ->
- if CaptureStdout /= false ->
- CaptureStdout ! {captured,Msg};
- true ->
- ok
- end,
- output({minor,Msg},From);
- true ->
- ok
- end.
+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)"
+ "~w:end_per_testcase(~w, ~p)"
" crashed!\n\tReason: ~p\n",
[Mod,Func,Conf,Why]};
_ ->
@@ -1145,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}}}
+ "WARNING! ~w:end_per_testcase(~w, ~p)"
+ " 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});
_ ->
@@ -1181,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};
@@ -1207,12 +802,12 @@ spawn_fw_call(Mod,{end_per_testcase,Func},EndConf,Pid,
{Result,E}
end,
group_leader() ! {printout,12,
- "WARNING! ~p:end_per_testcase(~p, ~p)"
+ "WARNING! ~w:end_per_testcase(~w, ~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});
_ ->
@@ -1230,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}]),
@@ -1249,17 +841,9 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) ->
spawn_link(FwCall);
spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
- {Mod1,Func1} =
- case {Mod,Func,CurrConf} of
- {undefined,undefined,{{M,F},_}} -> {M,F};
- _ -> {Mod,Func}
- end,
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(Mod1,Func1,[],
+ case catch fw_error_notify(Mod,Func,[],
Error,Loc) of
{'EXIT',FwErrorNotifyErr} ->
exit({fw_notify_done,error_notification,
@@ -1267,8 +851,8 @@ spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) ->
_ ->
ok
end,
- Conf = [{tc_status,{failed,timetrap_timeout}}],
- case catch do_end_tc_call(Mod1,Func1, 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});
@@ -1333,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};
@@ -1426,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,_} ->
@@ -1447,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;
@@ -1475,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 ->
@@ -1530,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 ->
@@ -1553,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, []};
@@ -1569,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};
@@ -1589,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 ->
@@ -1656,10 +1194,10 @@ 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",
+ "\tLocation: ~ts\n\tReason: ~p\n",
[FormattedLoc, Other]},
{skip,{failed,{Mod,init_per_testcase,Other}}};
_:Reason0 ->
@@ -1667,10 +1205,10 @@ 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",
+ "\tLocation: ~ts\n\tReason: ~p\n",
[FormattedLoc,Reason]},
{skip,{failed,{Mod,init_per_testcase,Reason}}}
end.
@@ -1690,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;
@@ -1707,16 +1244,15 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
"<br />")
end,
set_loc(erlang:get_stacktrace()),
- comment(io_lib:format("~s<font color=\"red\">"
+ comment(io_lib:format("~ts<font color=\"red\">"
"WARNING: ~w thrown!"
"</font>\n",[Comment0,EndFunc])),
group_leader() ! {printout,12,
"WARNING: ~w thrown!\n"
"Reason: ~p\n"
- "Line: ~s\n",
+ "Line: ~ts\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(),
@@ -1730,16 +1266,15 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
Cmt -> Cmt ++ test_server_ctrl:xhtml("<br>",
"<br />")
end,
- comment(io_lib:format("~s<font color=\"red\">"
+ comment(io_lib:format("~ts<font color=\"red\">"
"WARNING: ~w crashed!"
"</font>\n",[Comment0,EndFunc])),
group_leader() ! {printout,12,
"WARNING: ~w crashed!\n"
"Reason: ~p\n"
- "Line: ~s\n",
+ "Line: ~ts\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.
@@ -1752,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}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1833,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
@@ -1846,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).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1894,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}) ->
@@ -1908,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
@@ -2125,10 +1608,10 @@ break(CBM, TestCase, Comment) ->
end,
io:format(user,
"\n\n\n--- SEMIAUTOMATIC TESTING ---"
- "\nThe test case~s executes on process ~w"
- "\n\n\n~s"
+ "\nThe test case~ts executes on process ~w"
+ "\n\n\n~ts"
"\n\n\n-----------------------------\n\n"
- "Continue with --> ~w:continue(~s).\n",
+ "Continue with --> ~w:continue(~ts).\n",
[TCName,self(),Comment,CBM,CntArg]),
case whereis(PName) of
undefined ->
@@ -2170,28 +1653,19 @@ continue(Pid) when is_pid(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()]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2519,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
@@ -2542,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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2712,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,
@@ -2722,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,
@@ -2752,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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2761,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,
@@ -2778,26 +2266,49 @@ stop_node(Slave) ->
slave:stop(Slave),
receive
{nodedown, Slave} ->
- format(minor, "Stopped slave node: ~p", [Slave]),
- format(major, "=node_stop ~p", [Slave]),
+ format(minor, "Stopped slave node: ~w", [Slave]),
+ format(major, "=node_stop ~w", [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.",
+ format("=== WARNING: Node ~w does not seem to terminate.",
[Slave]),
+ erlang:monitor_node(Slave, false),
+ receive {nodedown, Slave} -> ok after 0 -> ok end,
false
end;
{error, _Reason} ->
%% Either, the node is already dead or it was started
%% with the {cleanup,false} option, or it was started
%% in some other way than test_server:start_node/3
- format("=== WARNING: Attempt to stop a nonexisting slavenode (~p)~n"
+ format("=== WARNING: Attempt to stop a nonexisting slavenode (~w)~n"
"=== Trying to kill it anyway!!!",
[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: ~w", [Slave]),
+ format(major, "=node_stop ~w", [Slave]),
+ if Cover -> do_cover_for_node(Slave,stop,false);
+ true -> ok
+ end,
+ true
+ after 30000 ->
+ format("=== WARNING: Node ~w 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.
@@ -2884,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()
@@ -2947,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
@@ -2961,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
@@ -2975,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().
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -3094,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 df2187bc04..5d4d392166 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -17,7 +17,6 @@
%% %CopyrightEnd%
%%
-module(test_server_ctrl).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% The Erlang Test Server %%
@@ -34,118 +33,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]).
@@ -165,20 +52,19 @@
-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]).
+-export([uri_encode/1,uri_encode/2]).
%%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([i/0, p/1, p/3, pi/2, pi/4, t/0, t/1]).
@@ -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.html").
-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;
@@ -341,12 +229,12 @@ parse_cmd_line(['SPEC',Spec|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
parse_cmd_line(Cmds, TermList++SpecList, [Name|Names], Param,
Trc, Cov, TCCB);
{error,Reason} ->
- io:format("Can't open ~s: ~p\n",
- [cast_to_list(Spec), file:format_error(Reason)]),
+ io:format("Can't open ~w: ~p\n",[Spec, file:format_error(Reason)]),
parse_cmd_line(Cmds, SpecList, Names, Param, Trc, Cov, TCCB)
end;
parse_cmd_line(['NAME',Name|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
- parse_cmd_line(Cmds, SpecList, [{name,Name}|Names], Param, Trc, Cov, TCCB);
+ parse_cmd_line(Cmds, SpecList, [{name,atom_to_list(Name)}|Names],
+ Param, Trc, Cov, TCCB);
parse_cmd_line(['SKIPMOD',Mod|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
parse_cmd_line(Cmds, [{skip,{Mod,"by command line"}}|SpecList], Names,
Param, Trc, Cov, TCCB);
@@ -354,14 +242,14 @@ parse_cmd_line(['SKIPCASE',Mod,Case|Cmds], SpecList, Names, Param, Trc, Cov, TCC
parse_cmd_line(Cmds, [{skip,{Mod,Case,"by command line"}}|SpecList], Names,
Param, Trc, Cov, TCCB);
parse_cmd_line(['DIR',Dir|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
- Name = cast_to_list(filename:basename(Dir)),
+ Name = filename:basename(Dir),
parse_cmd_line(Cmds, [{topcase,{dir,Name}}|SpecList], [Name|Names],
Param, Trc, Cov, TCCB);
parse_cmd_line(['MODULE',Mod|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
- parse_cmd_line(Cmds, [{topcase,{Mod,all}}|SpecList], [Mod|Names],
+ parse_cmd_line(Cmds,[{topcase,{Mod,all}}|SpecList],[atom_to_list(Mod)|Names],
Param, Trc, Cov, TCCB);
parse_cmd_line(['CASE',Mod,Case|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
- parse_cmd_line(Cmds, [{topcase,{Mod,Case}}|SpecList], [Mod|Names],
+ parse_cmd_line(Cmds,[{topcase,{Mod,Case}}|SpecList],[atom_to_list(Mod)|Names],
Param, Trc, Cov, TCCB);
parse_cmd_line(['PARAMETERS',Param|Cmds], SpecList, Names, _Param, Trc, Cov, TCCB) ->
parse_cmd_line(Cmds, SpecList, Names, Param, Trc, Cov, TCCB);
@@ -372,17 +260,17 @@ parse_cmd_line(['COVER',App,CF,Analyse|Cmds], SpecList, Names, Param, Trc, _Cov,
parse_cmd_line(['TESTCASE_CALLBACK',Mod,Func|Cmds], SpecList, Names, Param, Trc, Cov, _) ->
parse_cmd_line(Cmds, SpecList, Names, Param, Trc, Cov, {Mod,Func});
parse_cmd_line([Obj|_Cmds], _SpecList, _Names, _Param, _Trc, _Cov, _TCCB) ->
- io:format("~p: Bad argument: ~p\n", [?MODULE,Obj]),
+ io:format("~w: Bad argument: ~w\n", [?MODULE,Obj]),
io:format(" Use the `ts' module to start tests.\n", []),
io:format(" (If you ARE using `ts', there is a bug in `ts'.)\n", []),
halt(1);
parse_cmd_line([], SpecList, Names, Param, Trc, Cov, TCCB) ->
- NameList = lists:reverse(Names, [suite]),
+ NameList = lists:reverse(Names, ["suite"]),
Name = case lists:keysearch(name, 1, NameList) of
{value,{name,N}} -> N;
false -> hd(NameList)
end,
- {lists:reverse(SpecList), cast_to_list(Name), Param, Trc, Cov, TCCB}.
+ {lists:reverse(SpecList), Name, Param, Trc, Cov, TCCB}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% cast_to_list(X) -> string()
@@ -430,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'
@@ -470,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),
@@ -528,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}).
@@ -544,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) ->
@@ -613,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;
@@ -639,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
@@ -803,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
@@ -904,7 +681,7 @@ handle_call({abort_current_testcase,Reason}, _From, State) ->
handle_call({finish,Fini}, _From, State) ->
case State#state.jobs of
[] ->
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Fini) end,
State#state.idle_notify),
State2 = State#state{finish=false},
{stop,shutdown,{ok,self()}, State2};
@@ -922,14 +699,11 @@ handle_call({finish,Fini}, _From, State) ->
handle_call({idle_notify,Fun}, {Cli,_Ref}, State) ->
case State#state.jobs of
- [] ->
- Fun(Cli),
- {reply, {ok,self()}, State};
- _ ->
- Subscribed = State#state.idle_notify,
- {reply, {ok,self()},
- State#state{idle_notify=[{Cli,Fun}|Subscribed]}}
- end;
+ [] -> self() ! report_idle;
+ _ -> ok
+ end,
+ Subscribed = State#state.idle_notify,
+ {reply, {ok,self()}, State#state{idle_notify=[{Cli,Fun}|Subscribed]}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call(start_get_totals, From, State) -> {ok,Pid}
@@ -1059,13 +833,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}
@@ -1217,42 +991,42 @@ 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(report_idle, State) ->
+ Finish = State#state.finish,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Finish) end,
+ State#state.idle_notify),
+ {noreply,State#state{idle_notify=[]}};
+
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
normal ->
fine;
killed ->
- io:format("Suite ~s was killed\n", [Name]);
+ io:format("Suite ~ts was killed\n", [Name]);
_Other ->
- io:format("Suite ~s was killed with reason ~p\n",
+ io:format("Suite ~ts was killed with reason ~p\n",
[Name,Reason])
end,
State2 = State#state{jobs=NewJobs},
+ Finish = State2#state.finish,
case NewJobs of
[] ->
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Finish) end,
State2#state.idle_notify),
- case State2#state.finish of
+ case Finish of
false ->
{noreply,State2#state{idle_notify=[]}};
_ -> % true | abort
@@ -1262,9 +1036,9 @@ handle_info({'EXIT',Pid,Reason}, State) ->
{stop,shutdown,State2#state{finish=false}}
end;
_ -> % pending jobs
- case State2#state.finish of
+ case Finish of
abort -> % abort test now!
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Finish) end,
State2#state.idle_notify),
{stop,shutdown,State2#state{finish=false}};
_ -> % true | false
@@ -1288,7 +1062,7 @@ handle_info({tcp,_MainSock,<<1,Request/binary>>}, State) ->
%% The local job proc will soon be killed by the closed socket or
%% because the job is finished. Then the above clause ('EXIT') will
%% handle the problem.
- io:format("Suite ~s was killed on remote target with reason"
+ io:format("Suite ~ts was killed on remote target with reason"
" ~p\n", [Name,Reason]);
_ ->
ignore
@@ -1310,14 +1084,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}.
@@ -1378,24 +1146,22 @@ kill_all_jobs([]) ->
spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
CreatePrivDir, TCCallback, ExtraTools) ->
- spawn_link(
- fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
+ 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}, RejectIoReqs,
- 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_reject_io_reqs, RejectIoReqs),
put(test_server_create_priv_dir, CreatePrivDir),
put(test_server_random_seed, proplists:get_value(random_seed, ExtraTools)),
put(test_server_testcase_callback, TCCallback),
@@ -1411,23 +1177,30 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, RejectIoReqs,
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 =
@@ -1438,15 +1211,19 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, RejectIoReqs,
{SkippedN,SkipStr} =
case get(test_server_skipped) of
{0,_} -> {0,""};
- {Skipped,_} -> {Skipped,io_lib:format(", ~p Skipped", [Skipped])}
+ {Skipped,_} -> {Skipped,io_lib:format(", ~w Skipped", [Skipped])}
end,
OkN = get(test_server_ok),
FailedN = get(test_server_failed),
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"
+ "<td>~.3fs</td><td><b>~ts</b></td><td>~w Ok, ~w Failed~ts of ~w</td></tr>\n"
"</tfoot>\n",
- [Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]).
+ [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) ->
@@ -1464,11 +1241,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;
@@ -1487,8 +1264,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);
@@ -1513,7 +1290,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
{ok,TermList} ->
do_spec_list(TermList,TimetrapSpec);
{error,Reason} ->
- io:format("Can't open ~s: ~p\n", [SpecName,Reason]),
+ io:format("Can't open ~ts: ~p\n", [SpecName,Reason]),
{error,{cant_open_spec,Reason}}
end.
@@ -1615,7 +1392,7 @@ generate_nodenames(Num) ->
generate_nodenames2(0, _Hosts, Acc) ->
Acc;
generate_nodenames2(N, Hosts, Acc) ->
- Host=cast_to_list(lists:nth((N rem (length(Hosts)))+1, Hosts)),
+ Host=lists:nth((N rem (length(Hosts)))+1, Hosts),
Name=list_to_atom(temp_nodename("nod", []) ++ "@" ++ Host),
generate_nodenames2(N-1, Hosts, [Name|Acc]).
@@ -1747,32 +1524,26 @@ do_test_cases(TopCases, SkipCases,
TestSpec =
add_init_and_end_per_suite(TestSpec0, undefined, undefined, FwMod),
TI = get_target_info(),
- print(1, "Starting test~s",
+ print(1, "Starting test~ts",
[print_if_known(N, {", ~w test cases",[N]},
{" (with repeated test cases)",[]})]),
Test = get(test_server_name),
TestName = if is_list(Test) ->
- lists:flatten(io_lib:format("~s", [Test]));
+ lists:flatten(io_lib:format("~ts", [Test]));
true ->
- lists:flatten(io_lib:format("~p", [Test]))
+ lists:flatten(io_lib:format("~tp", [Test]))
end,
TestDescr = "Test " ++ TestName ++ " results",
test_server_sup:framework_call(report, [tests_start,{Test,N}]),
+
{Header,Footer} =
- case test_server_sup:framework_call(get_html_wrapper,
+ case test_server_sup:framework_call(get_html_wrapper,
[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",
- "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n",
- "<html>\n",
- "<head><title>", TestDescr, "</title>\n",
- "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
- "</head>\n",
- "<body bgcolor=\"white\" text=\"black\" ",
- "link=\"blue\" vlink=\"purple\" alink=\"red\">",
+ {[html_header(TestDescr),
"<h2>Results for test ", TestName, "</h2>\n"],
"\n</body>\n</html>\n"};
{basic_html,Html0,Html1} ->
@@ -1794,16 +1565,16 @@ do_test_cases(TopCases, SkipCases,
print(html, xhtml("\n<p><b>Host info:</b><br>\n",
"\n<p><b>Host info:</b><br />\n")),
print_who(test_server_sup:hoststr(), test_server_sup:get_username()),
- print(html, xhtml("<br>Used Erlang v~s in <tt>~s</tt></p>\n",
- "<br />Used Erlang v~s in \"~s\"</p>\n"),
+ print(html, xhtml("<br>Used Erlang v~ts in <tt>~ts</tt></p>\n",
+ "<br />Used Erlang v~ts in \"~ts\"</p>\n"),
[erlang:system_info(version), code:root_dir()]),
if FwMod == ?MODULE ->
print(html, xhtml("\n<p><b>Target Info:</b><br>\n",
"\n<p><b>Target Info:</b><br />\n")),
print_who(TI#target_info.host, TI#target_info.username),
- print(html, xhtml("<br>Used Erlang v~s in <tt>~s</tt></p>\n",
- "<br />Used Erlang v~s in \"~s\"</p>\n"),
+ print(html,xhtml("<br>Used Erlang v~ts in <tt>~ts</tt></p>\n",
+ "<br />Used Erlang v~ts in \"~ts\"</p>\n"),
[TI#target_info.version, TI#target_info.root_dir]);
true ->
case test_server_sup:framework_call(target_info, []) of
@@ -1811,7 +1582,7 @@ do_test_cases(TopCases, SkipCases,
length(TargetInfo) > 0 ->
print(html, xhtml("\n<p><b>Target info:</b><br>\n",
"\n<p><b>Target info:</b><br />\n")),
- print(html, "~s</p>\n", [TargetInfo]);
+ print(html, "~ts</p>\n", [TargetInfo]);
_ ->
ok
end
@@ -1819,37 +1590,38 @@ 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=\"~ts\">Full textual log</a></li>\n"
+ "<li><a href=\"~ts\">Coverage log</a></li>\n"
+ "<li><a href=\"~ts\">Unexpected I/O log</a></li>\n</ul></p>\n",
+ [?suitelog_name,?coverlog_name,?unexpected_io_log]),
print(html,
- "<p>~s</p>\n" ++
+ "<p>~ts</p>\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_if_known(N, {"<i>Executing <b>~w</b> test cases...</i>"
+ ++ xhtml("\n<br>\n", "\n<br />\n"),[N]},
{"",[]})]),
- print(major, "=cases ~p", [get(test_server_cases)]),
- print(major, "=user ~s", [TI#target_info.username]),
- print(major, "=host ~s", [TI#target_info.host]),
+ print(major, "=cases ~w", [get(test_server_cases)]),
+ print(major, "=user ~ts", [TI#target_info.username]),
+ print(major, "=host ~ts", [TI#target_info.host]),
%% If there are no hosts specified,use only the local host
case controller_call(get_hosts) of
[] ->
- print(major, "=hosts ~s", [TI#target_info.host]),
+ print(major, "=hosts ~ts", [TI#target_info.host]),
controller_call({set_hosts, [TI#target_info.host]});
Hosts ->
Str = lists:flatten(lists:map(fun(X) -> [X," "] end, Hosts)),
- print(major, "=hosts ~s", [Str])
+ print(major, "=hosts ~ts", [Str])
end,
- print(major, "=emulator_vsn ~s", [TI#target_info.version]),
- print(major, "=emulator ~s", [TI#target_info.emulator]),
- print(major, "=otp_release ~s", [TI#target_info.otp_release]),
+ print(major, "=emulator_vsn ~ts", [TI#target_info.version]),
+ print(major, "=emulator ~ts", [TI#target_info.emulator]),
+ print(major, "=otp_release ~ts", [TI#target_info.otp_release]),
print(major, "=started ~s",
[lists:flatten(timestamp_get(""))]),
@@ -1881,7 +1653,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 =
@@ -1896,20 +1668,40 @@ 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"),
+ FilenameMode = file:native_name_encoding(),
+ ok = write_file(filename:join(Dir, ?last_file),
+ TestDir1 ++ "\n",
+ FilenameMode),
+ ok = write_file(?last_file, TestDir1 ++ "\n", FilenameMode),
put(test_server_log_dir_base,TestDir1),
MajorName = filename:join(TestDir1, ?suitelog_name),
HtmlName = MajorName ++ ?html_ext,
- {ok,Major} = file:open(MajorName, [write]),
- {ok,Html} = file:open(HtmlName, [write]),
- put(test_server_major_fd,Major),
- put(test_server_html_fd,Html),
+ UnexpectedName = filename:join(TestDir1, ?unexpected_io_log),
+ {ok,Major} = open_utf8_file(MajorName),
+ {ok,Html} = open_html_file(HtmlName),
+ {ok,Unexpected} = open_html_file(UnexpectedName),
+ test_server_io:set_fd(major, Major),
+ test_server_io:set_fd(html, Html),
+ test_server_io:set_fd(unexpected_io, Unexpected),
+
+ {UnexpHeader,UnexpFooter} =
+ case test_server_sup:framework_call(get_html_wrapper,
+ ["Unexpected I/O log",false,
+ TestDir, undefined],"") of
+ UEmpty when (UEmpty == "") ; (element(2,UEmpty) == "") ->
+ {html_header("Unexpected I/O log"),"\n</body>\n</html>\n"};
+ {basic_html,UH,UF} ->
+ {UH,UF};
+ {xhtml,UH,UF} ->
+ {UH,UF}
+ end,
+ io:put_chars(Unexpected, UnexpHeader++"\n<pre>\n"),
+ put(test_server_unexpected_footer,UnexpFooter),
make_html_link(filename:absname(?last_test ++ ?html_ext),
HtmlName, filename:basename(Dir)),
@@ -1920,33 +1712,30 @@ 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),
PwdL = filename:split(filename:dirname(LinkName)),
Href = case lists:prefix(PwdL, TargetL) of
true ->
- filename:join(lists:nthtail(length(PwdL), TargetL));
+ uri_encode(filename:join(lists:nthtail(length(PwdL),TargetL)));
false ->
- "file:" ++ Target
+ "file:" ++ uri_encode(Target)
end,
- H = io_lib:format("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<html>\n"
- "<head><title>~s</title></head>\n"
- "<body bgcolor=\"white\" text=\"black\""
- " link=\"blue\" vlink=\"purple\" alink=\"red\">\n"
- "<h1>Last test</h1>\n"
- "<a href=\"~s\">~s</a>~n"
- "</body>\n</html>\n",
- [Explanation,Href,Explanation]),
- ok = file:write_file(LinkName, H).
+ H = [html_header(Explanation),
+ "<h1>Last test</h1>\n"
+ "<a href=\"",Href,"\">",Explanation,"</a>\n"
+ "</body>\n</html>\n"],
+ ok = write_html_file(LinkName, H).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% start_minor_log_file(Mod, Func) -> AbsName
@@ -1959,46 +1748,40 @@ 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])),
+ Name0 = lists:flatten(io_lib:format("~w.~w~ts", [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 =
- lists:flatten(io_lib:format("~s.~s.~w.~w~s", [Mod,Func,S,
- trunc(Us/1000),
- ?html_ext])),
+ lists:flatten(io_lib:format("~w.~w.~w.~w~ts", [Mod,Func,S,
+ trunc(Us/1000),
+ ?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) ->
- {ok,Fd} = file:open(AbsName, [write]),
+start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA) ->
+ {ok,Fd} = open_html_file(AbsName),
Lev = get(test_server_minor_level)+1000, %% far down in the minor levels
put(test_server_minor_fd, Fd),
-
- TestDescr = io_lib:format("Test ~p:~p result", [Mod,Func]),
+ test_server_gl:set_minor_fd(group_leader(), Fd, MFA),
+
+ TestDescr = io_lib:format("Test ~w:~w result", [Mod,Func]),
{Header,Footer} =
- case test_server_sup:framework_call(get_html_wrapper,
+ case test_server_sup:framework_call(get_html_wrapper,
[TestDescr,false,
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",
- "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n",
- "<html>\n",
- "<head><title>", TestDescr, "</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"],
- "\n</body>\n</html>\n"};
+ {html_header(TestDescr), "\n</body>\n</html>\n"};
{basic_html,Html0,Html1} ->
put(basic_html, true),
{Html0,Html1};
@@ -2007,32 +1790,30 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) ->
{Html0,Html1}
end,
put(test_server_minor_footer, Footer),
- io:fwrite(Fd, Header, []),
+ io:put_chars(Fd, Header),
- SrcListing = downcase(cast_to_list(Mod)) ++ ?src_listing_ext,
+ SrcListing = downcase(atom_to_list(Mod)) ++ ?src_listing_ext,
case {filelib:is_file(filename:join(LogDir, SrcListing)),
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]);
- _ -> ok
+ print(Lev, "<a href=\"~ts#~ts\">source code for ~w:~w/1</a>\n",
+ [uri_encode(SrcListing),
+ uri_encode(atom_to_list(Func)++"-1",utf8),
+ Mod,Func]);
+ _ ->
+ ok
end,
- io:fwrite(Fd, "<pre>\n", []),
-
-% Stupid BUG!
-% case catch apply(Mod, Func, [doc]) of
-% {'EXIT', _Why} -> ok;
-% Comment -> print(Lev, "Comment: ~s~n<br>", [Comment])
-% end,
+ io:put_chars(Fd, "<pre>\n"),
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, []),
- file:close(Fd),
+ io:put_chars(Fd, "</pre>\n" ++ Footer),
+ ok = file:close(Fd),
put(test_server_minor_fd, undefined).
downcase(S) -> downcase(S, []).
@@ -2114,25 +1895,15 @@ html_possibly_convert(Src, SrcInfo, Dest) ->
ok; % dest file up to date
_ ->
OutDir = get(test_server_log_dir_base),
- Header =
- case test_server_sup:framework_call(get_html_wrapper,
- ["Module "++Src,false,
- OutDir,undefined], "") of
- Empty when (Empty == "") ; (element(2,Empty) == "") ->
- ["<!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"];
- {_,Html,_} ->
- Html
- end,
- erl2html2:convert(Src, Dest, Header)
+ case test_server_sup:framework_call(get_html_wrapper,
+ ["Module "++Src,false,
+ OutDir,undefined,
+ encoding(Src)], "") of
+ Empty when (Empty == "") ; (element(2,Empty) == "") ->
+ erl2html2:convert(Src, Dest);
+ {_,Header,_} ->
+ erl2html2:convert(Src, Dest, Header)
+ end
end.
%% Copy all HTML files in InDir to OutDir.
@@ -2144,9 +1915,9 @@ copy_html_file(Src, DestDir) ->
Dest = filename:join(DestDir, filename:basename(Src)),
case file:read_file(Src) of
{ok,Bin} ->
- ok = file:write_file(Dest, Bin);
+ ok = write_binary_file(Dest, Bin);
{error,_Reason} ->
- io:format("File ~p: read failed\n", [Src])
+ io:format("File ~ts: read failed\n", [Src])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2304,9 +2075,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;
@@ -2316,8 +2085,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,""};
@@ -2325,52 +2092,17 @@ run_test_cases(TestSpec, Config, TimetrapData) ->
end,
OkN = get(test_server_ok),
FailedN = get(test_server_failed),
- print(1, "TEST COMPLETE, ~w ok, ~w failed~s of ~w test cases\n",
+ print(1, "TEST COMPLETE, ~w ok, ~w failed~ts of ~w test cases\n",
[OkN,FailedN,SkipStr,OkN+FailedN+AllSkippedN]),
test_server_sup:framework_call(report, [tests_done,
{OkN,FailedN,{UserSkipN,AutoSkipN}}]),
print(major, "=finished ~s", [lists:flatten(timestamp_get(""))]),
- print(major, "=failed ~p", [FailedN]),
- print(major, "=successful ~p", [OkN]),
- print(major, "=user_skipped ~p", [UserSkipN]),
- print(major, "=auto_skipped ~p", [AutoSkipN]),
+ print(major, "=failed ~w", [FailedN]),
+ print(major, "=successful ~w", [OkN]),
+ print(major, "=user_skipped ~w", [UserSkipN]),
+ print(major, "=auto_skipped ~w", [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
@@ -2449,27 +2181,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.
@@ -2522,7 +2265,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));
_ ->
@@ -2531,7 +2274,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
@@ -2548,7 +2291,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
@@ -2579,7 +2322,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
@@ -2595,7 +2338,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
@@ -2606,22 +2349,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 ->
@@ -2631,15 +2373,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));
@@ -2875,7 +2617,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
@@ -2889,7 +2631,8 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
run_test_cases_loop(Cases, [NewCfg|Config],
TimetrapData, Mode, Status2);
Bad ->
- print(minor, "~n*** ~p returned bad elements in Config: ~p.~n",
+ print(minor,
+ "~n*** ~w returned bad elements in Config: ~p.~n",
[Func,Bad]),
Reason = {failed,{Mod,init_per_suite,bad_return}},
Cases2 = skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
@@ -2904,16 +2647,18 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
stop_minor_log_file(),
run_test_cases_loop(Cases, [NewCfg|Config], TimetrapData, Mode, Status2);
{_,{framework_error,{FwMod,FwFunc},Reason},_} ->
- print(minor, "~n*** ~p failed in ~p. Reason: ~p~n", [FwMod,FwFunc,Reason]),
- print(1, "~p failed in ~p. Reason: ~p~n", [FwMod,FwFunc,Reason]),
+ print(minor, "~n*** ~w failed in ~w. Reason: ~p~n",
+ [FwMod,FwFunc,Reason]),
+ print(1, "~w failed in ~w. Reason: ~p~n", [FwMod,FwFunc,Reason]),
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 ->
ReportAbortRepeat(failed),
- print(minor, "~n*** ~p failed.~n"
+ print(minor, "~n*** ~w failed.~n"
" Skipping all cases.", [Func]),
Reason = {failed,{Mod,Func,Fail}},
{skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
@@ -2928,17 +2673,9 @@ 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"
+ print(minor, "~n*** ~w skipped.~n"
" Skipping all cases.", [Func]),
set_io_buffering(IOHandler),
stop_minor_log_file(),
@@ -2947,7 +2684,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
delete_status(Ref, Status2));
{_,{skip_and_save,Reason,_SavedConfig},_} when StartConf ->
ReportAbortRepeat(skipped),
- print(minor, "~n*** ~p skipped.~n"
+ print(minor, "~n*** ~w skipped.~n"
" Skipping all cases.", [Func]),
set_io_buffering(IOHandler),
stop_minor_log_file(),
@@ -3006,9 +2743,9 @@ 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"
+ print(minor, "~n*** ~w failed.~n"
" Skipping all cases.", [Func]),
Reason = {failed,{Mod,Func,Why}},
Cases = skip_cases_upto(Ref, Cases0, Reason, conf, Mode),
@@ -3037,27 +2774,26 @@ 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]),
- print(1, "~p failed in ~p. Reason: ~p~n", [FwMod,FwFunc,Reason]),
+ print(minor, "~n*** ~w failed in ~w. Reason: ~p~n",
+ [FwMod,FwFunc,Reason]),
+ print(1, "~w failed in ~w. Reason: ~p~n", [FwMod,FwFunc,Reason]),
stop_minor_log_file(),
exit(framework_error);
%% sequential execution of test case finished
@@ -3089,8 +2825,9 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status)
stop_minor_log_file(),
run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status1);
true -> % skip rest of cases in sequence
- print(minor, "~n*** ~p failed.~n"
- " Skipping all other cases in sequence.", [Func]),
+ print(minor, "~n*** ~w failed.~n"
+ " Skipping all other cases in sequence.",
+ [Func]),
Reason = {failed,{Mod,Func}},
Cases2 = skip_cases_upto(Ref, Cases, Reason, tc, Mode),
stop_minor_log_file(),
@@ -3100,8 +2837,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;
@@ -3206,13 +2943,19 @@ get_data_dir(Mod, Suite) ->
end,
case code:which(UseMod) of
non_existing ->
- print(12, "The module ~p is not loaded", [Mod]),
+ print(12, "The module ~w 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) ++ "/" ++ atom_to_list(Mod) ++ ?data_dir_suffix.
+
print_conf_time(0) ->
ok;
print_conf_time(ConfTime) ->
@@ -3356,7 +3099,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)
@@ -3371,24 +3116,24 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
Comment1 = reason_to_string(Comment),
- print(major, "~n=case ~p:~p", [Mod,Func]),
+ print(major, "~n=case ~w:~w", [Mod,Func]),
print(major, "=started ~s", [lists:flatten(timestamp_get(""))]),
- print(major, "=result skipped: ~s", [Comment1]),
- print(2,"*** Skipping test case #~w ~p ***", [CaseNum,{Mod,Func}]),
+ print(major, "=result skipped: ~ts", [Comment1]),
+ print(2,"*** Skipping test case #~w ~w ***", [CaseNum,{Mod,Func}]),
TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]),
GroupName = case get_name(Mode) of
undefined -> "";
Name -> cast_to_list(Name)
end,
print(html,
- TR ++ "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
- "<td>" ++ Col0 ++ "~p" ++ Col1 ++ "</td>"
- "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
- "<td>" ++ Col0 ++ "~p" ++ Col1 ++ "</td>"
+ TR ++ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "< >" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "0.000s" ++ Col1 ++ "</td>"
- "<td><font color=\"~s\">SKIPPED</font></td>"
- "<td>~s</td></tr>\n",
+ "<td><font color=\"~ts\">SKIPPED</font></td>"
+ "<td>~ts</td></tr>\n",
[num2str(CaseNum),fw_name(Mod),GroupName,Func,ResultCol,Comment1]),
if CaseNum > 0 ->
{US,AS} = get(test_server_skipped),
@@ -3497,13 +3242,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
@@ -3550,7 +3302,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} =
@@ -3563,7 +3315,7 @@ wait_and_resend(Ref, [{_,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
{'EXIT',CurrPid,Reason} when Reason /= normal ->
%% unexpected termination of test case process
{value,{_,_,CaseNum,Mod,Func}} = lists:keysearch(CurrPid, 2, Cases),
- print(1, "Error! Process for test case #~p (~p:~p) died! Reason: ~p",
+ print(1, "Error! Process for test case #~w (~w:~w) died! Reason: ~p",
[CaseNum, Mod, Func, Reason]),
exit({unexpected_termination,{CaseNum,Mod,Func},{CurrPid,Reason}})
end;
@@ -3581,16 +3333,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)
@@ -3599,13 +3353,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)!
@@ -3632,7 +3386,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} =
@@ -3672,11 +3426,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);
@@ -3689,15 +3450,11 @@ 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",
+ print(1, "Error! Process for test case #~w (~w:~w) died! Reason: ~p",
[Num, M, F, Reason]),
exit({unexpected_termination,{Num,M,F},{TCPid,Reason}})
end.
@@ -3727,64 +3484,57 @@ 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]),
+
+ print(major, "=case ~w:~w", [Mod, Func]),
MinorName = start_minor_log_file(Mod, Func),
print(minor, "<a name=\"top\"></a>", [], internal_raw),
MinorBase = filename:basename(MinorName),
- print(major, "=logfile ~s", [filename:basename(MinorName)]),
+ print(major, "=logfile ~ts", [filename:basename(MinorName)]),
UpdatedArgs =
%% maybe create unique private directory for test case or config func
@@ -3792,14 +3542,13 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
auto_per_run ->
update_config(hd(Args), [{tc_logfile,MinorName}]);
PrivDirMode ->
+ %% create unique private directory for test case
RunDir = filename:dirname(MinorName),
Ext =
if Num == 0 ->
{_,S,Us} = now(),
lists:flatten(io_lib:format(".~w.~w", [S,Us]));
true ->
- %% create unique private directory for test case
- RunDir = filename:dirname(MinorName),
lists:flatten(io_lib:format(".~w", [Num]))
end,
PrivDir = filename:join(RunDir, ?priv_dir) ++ Ext,
@@ -3813,7 +3562,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
@@ -3823,21 +3572,21 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
print(major, "=started ~s", [lists:flatten(timestamp_get(""))]),
{{Col0,Col1},Style} = get_font_style((RunInit==run_init), Mode),
TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]),
- print(html, TR ++ "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
- "<td>" ++ Col0 ++ "~p" ++ Col1 ++ "</td>"
- "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
- "<td><a href=\"~s\">~p</a></td>"
- "<td><a href=\"~s#top\"><</a> <a href=\"~s#end\">></a></td>",
- [num2str(Num),fw_name(Mod),GroupName,MinorBase,Func,
- MinorBase,MinorBase]),
+ EncMinorBase = uri_encode(MinorBase),
+ print(html, TR ++ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>"
+ "<td><a href=\"~ts\">~w</a></td>"
+ "<td><a href=\"~ts#top\"><</a> <a href=\"~ts#end\">></a></td>",
+ [num2str(Num),fw_name(Mod),GroupName,EncMinorBase,Func,
+ EncMinorBase,EncMinorBase]),
- do_if_parallel(Main, ok, fun erlang:yield/0),
+ do_unless_parallel(Main, fun erlang:yield/0),
- RejectIoReqs = get(test_server_reject_io_reqs),
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode),
- RunInit, Where, TimetrapData, RejectIoReqs),
+ RunInit, TimetrapData),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal;
@@ -3849,7 +3598,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 =
@@ -3954,14 +3703,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}.
@@ -3969,126 +3721,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
@@ -4104,9 +3746,9 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
fun() -> {?auto_skip_color,auto_skip} end,
fun() -> {?user_skip_color,skip} end),
print(major, "=result skipped", []),
- print(1, "*** SKIPPED *** ~s",
+ print(1, "*** SKIPPED *** ~ts",
[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) ||
@@ -4123,24 +3765,24 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
end,
print(html,
"<td>" ++ St0 ++ "~.3fs" ++ St1 ++ "</td>"
- "<td><font color=\"~s\">SKIPPED</font></td>"
- "<td>~s~s</td></tr>\n",
+ "<td><font color=\"~ts\">SKIPPED</font></td>"
+ "<td>~ts~ts</td></tr>\n",
[Time,Color,ReasonStr2,Comment1]),
FormatLoc = test_server_sup:format_loc(Loc),
- print(minor, "=== location ~s", [FormatLoc]),
- print(minor, "=== reason = ~s", [ReasonStr1]),
+ print(minor, "=== location ~ts", [FormatLoc]),
+ print(minor, "=== reason = ~ts", [ReasonStr1]),
Ret;
progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
Comment0, {St0,St1}) ->
print(major, "=result failed: timeout, ~p", [Loc]),
- print(1, "*** FAILED *** ~s",
+ print(1, "*** FAILED *** ~ts",
[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]),
+ ErrorReason = io_lib:format("{timetrap_timeout,~ts}", [FormatLastLoc]),
Comment =
case Comment0 of
"" -> "<font color=\"red\">" ++ ErrorReason ++ "</font>";
@@ -4150,23 +3792,23 @@ progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
print(html,
"<td>" ++ St0 ++ "~.3fs" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td>~s</td></tr>\n",
+ "<td>~ts</td></tr>\n",
[T/1000,Comment]),
FormatLoc = test_server_sup:format_loc(Loc),
- print(minor, "=== location ~s", [FormatLoc]),
+ print(minor, "=== location ~ts", [FormatLoc]),
print(minor, "=== reason = timetrap timeout", []),
failed;
progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
Comment0, {St0,St1}) ->
print(major, "=result failed: testcase_aborted, ~p", [Loc]),
- print(1, "*** FAILED *** ~s",
+ print(1, "*** FAILED *** ~ts",
[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]),
+ ErrorReason = io_lib:format("{testcase_aborted,~ts}", [FormatLastLoc]),
Comment =
case Comment0 of
"" -> "<font color=\"red\">" ++ ErrorReason ++ "</font>";
@@ -4176,19 +3818,19 @@ progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
print(html,
"<td>" ++ St0 ++ "died" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td>~s</td></tr>\n",
+ "<td>~ts</td></tr>\n",
[Comment]),
FormatLoc = test_server_sup:format_loc(Loc),
- print(minor, "=== location ~s", [FormatLoc]),
+ print(minor, "=== location ~ts", [FormatLoc]),
print(minor, "=== reason = {testcase_aborted,~p}", [Reason]),
failed;
progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
Comment0, {St0,St1}) ->
- print(major, "=result failed: ~p, ~p", [Reason,unknown]),
- print(1, "*** FAILED *** ~s",
+ print(major, "=result failed: ~p, ~w", [Reason,unknown]),
+ print(1, "*** FAILED *** ~ts",
[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"
@@ -4210,11 +3852,11 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
to_string(Comment0)
end,
print(html,
- "<td>" ++ St0 ++ "~s" ++ St1 ++ "</td>"
+ "<td>" ++ St0 ++ "~ts" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td>~s</td></tr>\n",
+ "<td>~ts</td></tr>\n",
[TimeStr,Comment]),
- print(minor, "=== location ~s", [unknown]),
+ print(minor, "=== location ~w", [unknown]),
{FStr,FormattedReason} = format_exception(Reason),
print(minor, "=== reason = " ++ FStr, [FormattedReason]),
failed;
@@ -4222,9 +3864,9 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~p", [Reason,Loc]),
- print(1, "*** FAILED *** ~s",
+ print(1, "*** FAILED *** ~ts",
[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"
@@ -4236,12 +3878,12 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
end,
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
print(html,
- "<td>" ++ St0 ++ "~s" ++ St1 ++ "</td>"
+ "<td>" ++ St0 ++ "~ts" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td><font color=\"red\">~s</font>~s</td></tr>\n",
+ "<td><font color=\"red\">~ts</font>~ts</td></tr>\n",
[TimeStr,FormatLastLoc,Comment]),
FormatLoc = test_server_sup:format_loc(Loc),
- print(minor, "=== location ~s", [FormatLoc]),
+ print(minor, "=== location ~ts", [FormatLoc]),
{FStr,FormattedReason} = format_exception(Reason),
print(minor, "=== reason = " ++ FStr, [FormattedReason]),
failed;
@@ -4249,7 +3891,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} ->
@@ -4257,7 +3899,7 @@ progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time,
HtmlCmt = test_server_sup:framework_call(format_comment,
[String],
String),
- print(major, "=result ok: ~s", [String]),
+ print(major, "=result ok: ~ts", [String]),
"<td>" ++ HtmlCmt ++ "</td>";
_ ->
print(major, "=result ok", []),
@@ -4270,7 +3912,7 @@ progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time,
print(html,
"<td>" ++ St0 ++ "~.3fs" ++ St1 ++ "</td>"
"<td><font color=\"green\">Ok</font></td>"
- "~s</tr>\n",
+ "~ts</tr>\n",
[Time,Comment]),
print(minor, "=== returned value = ~p", [RetVal]),
ok.
@@ -4348,8 +3990,8 @@ print_if_known(Known, {SK,AK}, {SU,AU}) ->
io_lib:format(S, A).
to_string(Term) when is_list(Term) ->
- case (catch io_lib:format("~s", [Term])) of
- {'EXIT',_} -> io_lib:format("~p", [Term]);
+ case (catch io_lib:format("~ts", [Term])) of
+ {'EXIT',_} -> lists:flatten(io_lib:format("~p", [Term]));
String -> lists:flatten(String)
end;
to_string(Term) ->
@@ -4450,17 +4092,16 @@ do_format_exception(Reason={Error,Stack}) ->
{"~p",Reason};
Formatted ->
Formatted1 = re:replace(Formatted, "exception error: ", "", [{return,list}]),
- {"~s",lists:flatten(Formatted1)}
+ {"~ts",lists:flatten(Formatted1)}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
-%% Where, TimetrapData, RejectIoReqs) ->
+%% 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()
@@ -4475,23 +4116,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, RejectIoReqs) ->
+run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
+ TimetrapData) ->
test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData,RejectIoReqs});
-run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target,
- TimetrapData, RejectIoReqs) ->
- case get(test_server_ctrl_job_sock) of
- undefined ->
- %% local target
- test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData,RejectIoReqs});
- JobSock ->
- %% remote target
- request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit,
- TimetrapData,RejectIoReqs}}),
- read_job_sock_loop(JobSock)
- end.
+ TimetrapData}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% print(Detail, Format, Args) -> ok
@@ -4501,16 +4129,6 @@ run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target,
%%
%% 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, []).
@@ -4523,19 +4141,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
@@ -4557,7 +4163,7 @@ print_who(Host, User) ->
"" -> "";
_ -> " by " ++ User
end,
- print(html, "Run~s on ~s", [UserStr,Host]).
+ print(html, "Run~ts on ~ts", [UserStr,Host]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% format(Format) -> IoLibReturn
@@ -4599,112 +4205,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) ->
@@ -4734,7 +4234,7 @@ odd_or_even() ->
%% date and time. The resulting string is suitable as a filename.
timestamp_filename_get(Leader) ->
timestamp_get_internal(Leader,
- "~s~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w").
+ "~ts~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% timestamp_get(Leader) -> string()
@@ -4744,7 +4244,7 @@ timestamp_filename_get(Leader) ->
%% date and time. The resulting string is suitable for display.
timestamp_get(Leader) ->
timestamp_get_internal(Leader,
- "~s~w-~2.2.0w-~2.2.0w ~2.2.0w:~2.2.0w:~2.2.0w").
+ "~ts~w-~2.2.0w-~2.2.0w ~2.2.0w:~2.2.0w:~2.2.0w").
timestamp_get_internal(Leader, Format) ->
{YY,MM,DD,H,M,S} = time_get(),
@@ -5038,7 +4538,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.
@@ -5085,7 +4585,11 @@ collect_files(Dir, Pattern, St) ->
collect_cases(Mods, St)
end.
-path_to_module(Path) ->
+path_to_module(Path) when is_list(Path) ->
+ %% If this is called with a binary, then we are probably in +fnu
+ %% mode and have found a beam file with name encoded as latin1. We
+ %% will let this crash since it can not work to load such a module
+ %% anyway. It should be removed or renamed!
list_to_atom(filename:rootname(filename:basename(Path))).
collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St) ->
@@ -5216,15 +4720,15 @@ 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
{{ok,Nodename}, Host, Cmd, Info, Warning} ->
format(minor,
- "Successfully started node ~p on ~p with command: ~p",
+ "Successfully started node ~w on ~tp with command: ~tp",
[Nodename, Host, Cmd]),
- format(major, "=node_start ~p", [Nodename]),
+ format(major, "=node_start ~w", [Nodename]),
case Info of
[] -> ok;
_ -> format(minor, Info)
@@ -5238,16 +4742,16 @@ start_node(Name, Type, Options) ->
{ok, Nodename};
{fail,{Ret, Host, Cmd}} ->
format(minor,
- "Failed to start node ~p on ~p with command: ~p~n"
+ "Failed to start node ~tp on ~tp with command: ~tp~n"
"Reason: ~p",
[Name, Host, Cmd, Ret]),
{fail,Ret};
{Ret, undefined, undefined} ->
- format(minor, "Failed to start node ~p: ~p", [Name,Ret]),
+ format(minor, "Failed to start node ~tp: ~p", [Name,Ret]),
Ret;
{Ret, Host, Cmd} ->
format(minor,
- "Failed to start node ~p on ~p with command: ~p~n"
+ "Failed to start node ~tp on ~tp with command: ~tp~n"
"Reason: ~p",
[Name, Host, Cmd, Ret]),
Ret
@@ -5261,7 +4765,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.
@@ -5285,60 +4790,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 %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -5443,33 +4894,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
@@ -5478,62 +4948,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",
+ io:fwrite("Can't read CoverFile ~ts\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
@@ -5544,38 +5020,42 @@ 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]),
+ {ok,CoverLog} = open_html_file(filename:join(TestDir, ?coverlog_name)),
write_coverlog_header(CoverLog),
io:fwrite(CoverLog, "<h1>Coverage for application '~w'</h1>\n", [App]),
io:fwrite(CoverLog,
- "<p><a href=\"~s\">Coverdata collected over all tests</a></p>",
+ "<p><a href=\"~ts\">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]),
+ io:fwrite(CoverLog, "<p>CoverFile: <code>~tp</code>\n", [CoverFile]),
+ write_cross_cover_info(TestDir,Cross),
case length(cover:imported_modules()) of
Imps when Imps > 0 ->
- io:fwrite(CoverLog, "<p>Analysis includes data from ~w imported module(s).\n",
+ io:fwrite(CoverLog,
+ "<p>Analysis includes data from ~w imported module(s).\n",
[Imps]);
_ ->
ok
end,
- io:fwrite(CoverLog, "<p>Excluded module(s): <code>~p</code>\n", [Excluded]),
+ io:fwrite(CoverLog, "<p>Excluded module(s): <code>~tp</code>\n", [Excluded]),
- Coverage = cover_analyse(Analyse, AnalyseMods),
+ Coverage = cover_analyse(Analyse, AnalyseMods, Stop),
+ write_binary_file(filename:join(TestDir,?raw_coverlog_name),
+ term_to_binary(Coverage)),
case lists:filter(fun({_M,{_,_,_}}) -> false;
(_) -> true
@@ -5589,35 +5069,30 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
end,
TotPercent = write_cover_result_table(CoverLog, Coverage),
- file:write_file(filename:join(TestDir, ?cover_total),
- term_to_binary(TotPercent)).
+ write_binary_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 ->
@@ -5625,100 +5100,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) ->
+ write_binary_file(filename:join(Dir,?cross_cover_info),
+ term_to_binary(Cross)).
+
+%% 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=/=[] ->
+ write_binary_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]),
+ {ok,CoverLog} = open_html_file(CoverLogName),
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 ~tp\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:read_file(filename:join(Dir,?cross_cover_info)) of
+ {ok,Bin} ->
+ TagMods = binary_to_term(Bin),
+ 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
@@ -5731,38 +5217,9 @@ 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
- io:fwrite(CoverLog,
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '~w'. -->\n"
- "<html>\n"
- "<head><title>Coverage results</title></head>\n"
- "<body bgcolor=\"white\" text=\"black\" "
- "link=\"blue\" vlink=\"purple\" alink=\"red\">",
- [?MODULE]) of
+ case catch io:put_chars(CoverLog,html_header("Coverage results")) of
{'EXIT',Reason} ->
io:format("\n\nERROR: Could not write normal heading in coverlog.\n"
"CoverLog: ~w\n"
@@ -5781,20 +5238,22 @@ format_analyse(M,Cov,NotCov,undefined) ->
"<td align=right>~w</td></tr>\n",
[M,pc(Cov,NotCov),Cov,NotCov]);
format_analyse(M,Cov,NotCov,{file,File}) ->
- io_lib:fwrite("<tr><td><a href=\"~s\">~w</a></td>"
+ io_lib:fwrite("<tr><td><a href=\"~ts\">~w</a></td>"
"<td align=right>~w %</td>"
"<td align=right>~w</td>"
"<td align=right>~w</td></tr>\n",
- [filename:basename(File),M,pc(Cov,NotCov),Cov,NotCov]);
+ [uri_encode(filename:basename(File)),
+ M,pc(Cov,NotCov),Cov,NotCov]);
format_analyse(M,Cov,NotCov,{lines,Lines}) ->
CoverOutName = atom_to_list(M)++".COVER.html",
- {ok,CoverOut} = file:open(CoverOutName, [write]),
+ {ok,CoverOut} = open_html_file(CoverOutName),
write_not_covered(CoverOut,M,Lines),
- io_lib:fwrite("<tr><td><a href=\"~s\">~w</a></td>"
+ ok = file:close(CoverOut),
+ io_lib:fwrite("<tr><td><a href=\"~ts\">~w</a></td>"
"<td align=right>~w %</td>"
"<td align=right>~w</td>"
"<td align=right>~w</td></tr>\n",
- [CoverOutName,M,pc(Cov,NotCov),Cov,NotCov]);
+ [uri_encode(CoverOutName),M,pc(Cov,NotCov),Cov,NotCov]);
format_analyse(M,Cov,NotCov,{error,_}) ->
io_lib:fwrite("<tr><td>~w</td>"
"<td align=right>~w %</td>"
@@ -5810,8 +5269,8 @@ pc(Cov,NotCov) ->
write_not_covered(CoverOut,M,Lines) ->
+ io:put_chars(CoverOut,html_header("Coverage results for "++atom_to_list(M))),
io:fwrite(CoverOut,
- "<html>\n"
"The following lines in module ~w are not covered:\n"
"<table border=3 cellpadding=5>\n"
"<th>Line Number</th>\n",
@@ -5822,25 +5281,25 @@ write_not_covered(CoverOut,M,Lines) ->
ok
end,
Lines),
- io:fwrite(CoverOut,"</table>\n</html>\n", []).
+ io:put_chars(CoverOut,"</table>\n</body>\n</html>\n").
write_default_coverlog(TestDir) ->
- {ok,CoverLog} = file:open(filename:join(TestDir,?coverlog_name), [write]),
+ {ok,CoverLog} = open_html_file(filename:join(TestDir,?coverlog_name)),
write_coverlog_header(CoverLog),
- io:fwrite(CoverLog,"Cover tool is not used\n</body></html>\n", []),
- file:close(CoverLog).
+ io:put_chars(CoverLog,"Cover tool is not used\n</body></html>\n"),
+ ok = file:close(CoverLog).
write_default_cross_coverlog(TestDir) ->
{ok,CrossCoverLog} =
- file:open(filename:join(TestDir,?cross_coverlog_name), [write]),
+ open_html_file(filename:join(TestDir,?cross_coverlog_name)),
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"], []),
- file:close(CrossCoverLog).
+ 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"]),
+ ok = file:close(CrossCoverLog).
write_cover_result_table(CoverLog,Coverage) ->
io:fwrite(CoverLog,
@@ -5851,7 +5310,7 @@ write_cover_result_table(CoverLog,Coverage) ->
{TotCov,TotNotCov} =
lists:foldl(fun({M,{Cov,NotCov,Details}},{AccCov,AccNotCov}) ->
Str = format_analyse(M,Cov,NotCov,Details),
- io:fwrite(CoverLog,"~s", [Str]),
+ io:fwrite(CoverLog,"~ts", [Str]),
{AccCov+Cov,AccNotCov+NotCov};
({_M,{error,_Reason}},{AccCov,AccNotCov}) ->
{AccCov,AccNotCov}
@@ -5866,5 +5325,88 @@ write_cover_result_table(CoverLog,Coverage) ->
"</body>\n"
"</html>\n",
[TotPercent,TotCov,TotNotCov]),
- file:close(CoverLog),
+ ok = file:close(CoverLog),
TotPercent.
+
+
+%%%-----------------------------------------------------------------
+%%% Support functions for writing files
+
+%% HTML files are always written with utf8 encoding
+html_header(Title) ->
+ ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
+ "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>", Title, "</title>\n"
+ "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n"
+ "</head>\n"
+ "<body bgcolor=\"white\" text=\"black\" "
+ "link=\"blue\" vlink=\"purple\" alink=\"red\">\n"].
+
+open_html_file(File) ->
+ open_utf8_file(File).
+
+write_html_file(File,Content) ->
+ write_file(File,Content,utf8).
+
+%% The 'major' log file, which is a pure text file is also written
+%% with utf8 encoding
+open_utf8_file(File) ->
+ file:open(File,[write,{encoding,utf8}]).
+
+%% Write a file with specified encoding
+write_file(File,Content,latin1) ->
+ file:write_file(File,Content);
+write_file(File,Content,utf8) ->
+ write_binary_file(File,unicode:characters_to_binary(Content)).
+
+%% Write a file with only binary data
+write_binary_file(File,Content) ->
+ file:write_file(File,Content).
+
+%% Encoding of hyperlinks in HTML files
+uri_encode(File) ->
+ Encoding = file:native_name_encoding(),
+ uri_encode(File,Encoding).
+
+uri_encode(File,Encoding) ->
+ Components = filename:split(File),
+ filename:join([uri_encode_comp(C,Encoding) || C <- Components]).
+
+uri_encode_comp("/",_) ->
+ "/";
+uri_encode_comp(Chars,utf8) ->
+ http_uri:encode(Chars);
+uri_encode_comp(Chars,latin1) ->
+ do_uri_encode(Chars).
+
+%% Encode a file reference to a latin1 filename so it can be inserted
+%% in a utf8 encoded HTML file.
+%% This does the same as http_uri:encode/1, except it also encodes all
+%% characters >127 - i.e. latin1 but not ASCII.
+do_uri_encode([Char|Chars]) ->
+ case Char>127 orelse sets:is_element(Char, reserved()) of
+ true ->
+ [ $% | http_util:integer_to_hexlist(Char)] ++ do_uri_encode(Chars);
+ false ->
+ [Char | do_uri_encode(Chars)]
+ end;
+do_uri_encode([]) ->
+ [].
+
+%% Copied from http_uri.erl, but slightly modified
+%% (not converting @ and :)
+reserved() ->
+ sets:from_list([$;, $&, $=, $+, $,, $/, $?,
+ $#, $[, $], $<, $>, $\", ${, $}, $|,
+ $\\, $', $^, $%, $ ]).
+
+encoding(File) ->
+ case epp:read_encoding(File) of
+ none ->
+ epp:default_encoding();
+ E ->
+ E
+ end.
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..766a4537a2
--- /dev/null
+++ b/lib/test_server/src/test_server_gl.erl
@@ -0,0 +1,300 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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,_,Reason}=D, #st{minor_monitor=Ref}=St) ->
+ case Reason of
+ normal -> ok;
+ _ ->
+ Data = io_lib:format("=== WARNING === TC: ~w\n"
+ "Got down from minor Fd ~w: ~w\n\n",
+ [St#st.tc,St#st.minor,D]),
+ test_server_io:print(xxxFrom, unexpected_io, Data)
+ end,
+ {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 ->
+ unicode:characters_to_list(Bytes, Enc);
+io_req({put_chars,Encoding,Mod,Func,[Format,Args]}, _, _) ->
+ Str = Mod:Func(Format, Args),
+ unicode:characters_to_list(Str, Encoding);
+io_req(_, _, _) -> passthrough.
+
+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("=== ~w:~w/~w\n", [M,F,A]),Data0],
+ test_server_io:print(From, unexpected_io, Data),
+ ok;
+output_to_file(minor, Data, From, #st{tc=TC,minor=Fd}) ->
+ try
+ io:put_chars(Fd, Data)
+ catch
+ Type:Reason ->
+ Data1 =
+ [io_lib:format("=== ERROR === TC: ~w\n"
+ "Failed to write to minor Fd: ~w\n"
+ "Type: ~w\n"
+ "Reason: ~w\n",
+ [TC,Fd,Type,Reason]),
+ Data,"\n"],
+ test_server_io:print(From, unexpected_io, Data1)
+ 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 78daba855d..24063ddb10 100644
--- a/lib/test_server/src/test_server_h.erl
+++ b/lib/test_server/src/test_server_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -140,7 +140,7 @@ report_receiver(info_report, _) -> kernel;
report_receiver(_, _) -> none.
tag({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) ->
- io:format(user, "~n=TESTCASE: ~p:~p/~p", [M,F,A]);
+ io:format(user, "~n=TESTCASE: ~w:~w/~w", [M,F,A]);
tag(Testcase) ->
io:format(user, "~n=TESTCASE: ~p", [Testcase]).
diff --git a/lib/test_server/src/test_server_internal.hrl b/lib/test_server/src/test_server_internal.hrl
index c9c52854e3..9a11182725 100644
--- a/lib/test_server/src/test_server_internal.hrl
+++ b/lib/test_server/src/test_server_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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,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..242c08f765
--- /dev/null
+++ b/lib/test_server/src/test_server_io.erl
@@ -0,0 +1,319 @@
+%%
+%% %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/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, 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 ~ts: ~ts\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: ~w, line ~w: 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: ~w, line ~w: 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..54a49b31ca 100644
--- a/lib/test_server/src/test_server_node.erl
+++ b/lib/test_server/src/test_server_node.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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,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..377aa21018 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2012. 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
@@ -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, _} ->
@@ -78,7 +72,7 @@ timetrap(Timeout0, ReportTVal, Scale, Pid) ->
after 10000 ->
%% Pid is probably trapping exits, hit it harder...
catch error_logger:warning_msg(
- "Testcase process ~p not "
+ "Testcase process ~w not "
"responding to timetrap "
"timeout:~n"
" ~p.~n"
@@ -318,7 +312,7 @@ check_dict(Dict, Reason) ->
[] ->
1; % All ok.
List ->
- io:format("** ~s (~s) ->~n~p~n",[Reason, Dict, List]),
+ io:format("** ~ts (~ts) ->~n~p~n",[Reason, Dict, List]),
0
end.
@@ -327,7 +321,7 @@ check_dict_tolerant(Dict, Reason, Mode) ->
[] ->
1; % All ok.
List ->
- io:format("** ~s (~s) ->~n~p~n",[Reason, Dict, List]),
+ io:format("** ~ts (~ts) ->~n~p~n",[Reason, Dict, List]),
case Mode of
pedantic ->
0;
@@ -383,7 +377,7 @@ check_new_crash_dumps() ->
ok;
Num ->
test_server_ctrl:format(minor,
- "Found ~p crash dumps:~n", [Num]),
+ "Found ~w crash dumps:~n", [Num]),
append_files_to_logfile(Dumps),
delete_files(Dumps)
end.
@@ -391,7 +385,7 @@ check_new_crash_dumps() ->
append_files_to_logfile([]) -> ok;
append_files_to_logfile([File|Files]) ->
NodeName=from($., File),
- test_server_ctrl:format(minor, "Crash dump from node ~p:~n",[NodeName]),
+ test_server_ctrl:format(minor, "Crash dump from node ~tp:~n",[NodeName]),
Fd=get(test_server_minor_fd),
case file:read_file(File) of
{ok, Bin} ->
@@ -406,19 +400,19 @@ append_files_to_logfile([File|Files]) ->
"to this file: ~p~n", [file:format_error(Error)])
end;
_Error ->
- io:format(Fd, "Failed to read: ~s\n", [File])
+ io:format(Fd, "Failed to read: ~ts\n", [File])
end,
append_files_to_logfile(Files).
delete_files([]) -> ok;
delete_files([File|Files]) ->
- io:format("Deleting file: ~s~n", [File]),
+ io:format("Deleting file: ~ts~n", [File]),
case file:delete(File) of
{error, _} ->
case file:rename(File, File++".old") of
{error, Error} ->
io:format("Could neither delete nor rename file "
- "~s: ~s.~n", [File, Error]);
+ "~ts: ~ts.~n", [File, Error]);
_ ->
ok
end;
@@ -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,43 +553,27 @@ 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]).
+ io_lib:format("~p",[Loc]).
format_loc1([{Mod,Func,Line}]) ->
[" ",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=\"~ts~ts#~w\">~w</a>}",
+ [Mod,Func,
+ test_server_ctrl:uri_encode(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 +582,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 5fbc0ee017..4e5dc1b759 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. 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
@@ -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)
@@ -520,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
@@ -539,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.
@@ -581,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_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_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 43e56e1098..73abe86e11 100644
--- a/lib/test_server/src/ts_erl_config.erl
+++ b/lib/test_server/src/ts_erl_config.erl
@@ -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 caf00759e5..e9e559df5d 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-2012. 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
@@ -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,14 +88,12 @@ 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).
autoconf2({ok, Bin}) ->
- get_vars(binary_to_list(Bin), name, [], []);
+ get_vars(ts_lib:b2s(Bin), name, [], []);
autoconf2(Error) ->
Error.
@@ -174,12 +170,12 @@ parse_xcomp_file(Filepath) ->
parse_xcomp_file([<<A:8,_/binary>> = Line|R],Envs,Flags)
when $A =< A, A =< $Z ->
[Var,Value] = binary:split(Line,<<"=">>),
- parse_xcomp_file(R,[{binary_to_list(Var),
- binary_to_list(Value)}|Envs],Flags);
+ parse_xcomp_file(R,[{ts_lib:b2s(Var),
+ ts_lib:b2s(Value)}|Envs],Flags);
parse_xcomp_file([<<"erl_xcomp_",Line/binary>>|R],Envs,Flags) ->
[Var,Value] = binary:split(Line,<<"=">>),
- parse_xcomp_file(R,Envs,[{binary_to_list(Var),
- binary_to_list(Value)}|Flags]);
+ parse_xcomp_file(R,Envs,[{ts_lib:b2s(Var),
+ ts_lib:b2s(Value)}|Flags]);
parse_xcomp_file([_|R],Envs,Flags) ->
parse_xcomp_file(R,Envs,Flags);
parse_xcomp_file([],Envs,Flags) ->
@@ -411,4 +407,3 @@ extra_platform_label() ->
[_|_]=Label -> "/" ++ Label;
false -> ""
end.
-
diff --git a/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl
index ea97361bd3..a00f607fc1 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-2012. 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
@@ -25,12 +25,12 @@
-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
+ maybe_atom_to_list/1, progress/4,
+ b2s/1
]).
error(Reason) ->
@@ -91,25 +91,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 +144,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}
@@ -200,7 +156,7 @@ force_rename(From, To, Number) ->
subst_file(In, Out, Vars) ->
case file:read_file(In) of
{ok, Bin} ->
- Subst = subst(binary_to_list(Bin), Vars, []),
+ Subst = subst(b2s(Bin), Vars, []),
case file:write_file(Out, Subst) of
ok ->
ok;
@@ -379,3 +335,11 @@ make_non_erlang_do(DataDir, Variables) ->
after
timer:sleep(100) %% maybe unnecessary now when we don't do set_cwd anymore
end.
+
+b2s(Bin) ->
+ unicode:characters_to_list(Bin,default_encoding()).
+
+default_encoding() ->
+ try epp:default_encoding()
+ catch error:undef -> latin1
+ end.
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 95e3c08d5b..60c9a7a4b7 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-2012. 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
@@ -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],
@@ -245,13 +261,17 @@ run_batch(Vars, _Spec, State) ->
ts_lib:progress(Vars, 1, "Command: ~s~n", [Command]),
io:format(user, "Command: ~s~n",[Command]),
Port = open_port({spawn, Command}, [stream, in, eof]),
- tricky_print_data(Port).
+ Timeout = 30000 * case os:getenv("TS_RUN_VALGRIND") of
+ false -> 1;
+ _ -> 100
+ end,
+ tricky_print_data(Port, Timeout).
-tricky_print_data(Port) ->
+tricky_print_data(Port, Timeout) ->
receive
{Port, {data, Bytes}} ->
io:put_chars(Bytes),
- tricky_print_data(Port);
+ tricky_print_data(Port, Timeout);
{Port, eof} ->
Port ! {self(), close},
receive
@@ -264,7 +284,7 @@ tricky_print_data(Port) ->
after 1 -> % force context switch
ok
end
- after 30000 ->
+ after Timeout ->
case erl_epmd:names() of
{ok,Names} ->
case is_testnode_dead(Names) of
@@ -272,10 +292,10 @@ tricky_print_data(Port) ->
io:put_chars("WARNING: No EOF, but "
"test_server node is down!\n");
false ->
- tricky_print_data(Port)
+ tricky_print_data(Port, Timeout)
end;
_ ->
- tricky_print_data(Port)
+ tricky_print_data(Port, Timeout)
end
end.
@@ -329,8 +349,7 @@ start_xterm(Command) ->
path_separator() ->
case os:type() of
{win32, _} -> ";";
- {unix, _} -> ":";
- vxworks -> ":"
+ {unix, _} -> ":"
end.
@@ -353,7 +372,7 @@ make_common_test_args(Args0, Options0, _Vars) ->
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,
@@ -365,13 +384,7 @@ make_common_test_args(Args0, Options0, _Vars) ->
[{logdir,"../test_server"}]
end,
- TimeTrap = case test_server:timetrap_scale_factor() of
- 1 ->
- [];
- Scale ->
- [{multiply_timetraps, Scale},
- {scale_timetraps, true}]
- end,
+ TimeTrap = [{scale_timetraps, true}],
{ConfigPath,
Options} = case {os:getenv("TEST_CONFIG_PATH"),
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 afe5aff196..afccc28662 100644
--- a/lib/test_server/test/Makefile
+++ b/lib/test_server/test/Makefile
@@ -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..37c2b74d8e
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE.erl
@@ -0,0 +1,264 @@
+%%
+%% %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(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/dialyzer/test/small_SUITE_data/results/pubsub b/lib/test_server/test/erl2html2_SUITE_data/include/header2.hrl
index e69de29bb2..e69de29bb2 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/pubsub
+++ 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..bea2c0dc49 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-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,6 +31,7 @@
-include_lib("common_test/include/ct.hrl").
-include("test_server_test_lib.hrl").
+-include_lib("kernel/include/file.hrl").
%%--------------------------------------------------------------------
%% COMMON TEST CALLBACK FUNCTIONS
@@ -68,6 +69,13 @@ init_per_testcase(_TestCase, Config) ->
%% @spec end_per_testcase(TestCase, Config0) ->
%% void() | {save_config,Config1} | {fail,Reason}
+end_per_testcase(test_server_unicode, _Config) ->
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+ N1 = list_to_atom("test_server_tester_latin1" ++ "@" ++ Host),
+ N2 = list_to_atom("test_server_tester_utf8" ++ "@" ++ Host),
+ test_server:stop_node(N1),
+ test_server:stop_node(N2),
+ ok;
end_per_testcase(_TestCase, _Config) ->
ok.
@@ -79,7 +87,9 @@ 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,
+ test_server_unicode].
%%--------------------------------------------------------------------
@@ -92,45 +102,131 @@ 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.
+
+test_server_unicode(Config) ->
+ run_test_server_tests("test_server_unicode_SUITE", [],
+ 5, 0, 3, 3, 0, 0, 0, 0, 5, Config),
+
+ %% Create and run two test suites - one with filename and content
+ %% in latin1 (if the default filename mode is latin1) and one with
+ %% filename and content in utf8. Both have name and content
+ %% including letters ���. Check that all logs are generated with
+ %% utf8 encoded filenames.
+ case file:native_name_encoding() of
+ utf8 ->
+ ok;
+ latin1 ->
+ generate_and_run_unicode_test(Config,latin1)
+ end,
+ generate_and_run_unicode_test(Config,utf8).
+
+%%%-----------------------------------------------------------------
+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) ->
Node = proplists:get_value(node, Config),
+ Encoding = rpc:call(Node,file,native_name_encoding,[]),
+ WorkDir = proplists:get_value(work_dir, Config),
+ LogDir = filename:join(WorkDir, SuiteName++".logs"),
+ LogDirUri = test_server_ctrl:uri_encode(LogDir, Encoding),
+ ct:log("<a href=\"file://~s\">Test case log files</a>\n", [LogDirUri]),
+
{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 +234,16 @@ 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"])))),
+ LogDir1 = translate_filename(LogDir,Encoding),
+ LastRunDir = get_latest_run_dir(LogDir1),
+ LastSuiteLog = filename:join(LastRunDir,"suite.log"),
+ {ok,Data} = test_server_test_lib:parse_suite(LastSuiteLog),
+ 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 +251,59 @@ 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.
+translate_filename(Filename,EncodingOnTestNode) ->
+ case {file:native_name_encoding(),EncodingOnTestNode} of
+ {X,X} -> Filename;
+ {utf8,latin1} -> list_to_binary(Filename);
+ {latin1,utf8} -> unicode:characters_to_binary(Filename)
+ end.
+
+get_latest_run_dir(Dir) ->
+ %% For the time being, filelib:wildcard can not take a binary
+ %% argument, so we avoid using this here.
+ case file:list_dir(Dir) of
+ {ok,Files} ->
+ {ok,RE} = re:compile(<<"^run.[1-2][-_\.0-9]*$">>),
+ RunDirs = lists:filter(
+ fun(F) ->
+ L = l(F),
+ case re:run(F,RE) of
+ {match,[{0,L}]} -> true;
+ _ -> false
+ end
+ end, Files),
+ case RunDirs of
+ [] ->
+ Dir;
+ [H|T] ->
+ filename:join(Dir,get_latest_dir(T,H))
+ end;
+ _ ->
+ Dir
+ end.
+
+l(X) when is_binary(X) -> size(X);
+l(X) when is_list(X) -> length(X).
+
+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.
+
+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 ->
@@ -167,4 +312,139 @@ until(Fun) ->
timer:sleep(100),
until(Fun)
end.
-
+
+generate_and_run_unicode_test(Config0,Encoding) ->
+ DataDir = ?config(data_dir,Config0),
+ Suite = create_unicode_test_suite(DataDir,Encoding),
+
+ %% We can not run this test on default node since it must be
+ %% started with correct file name mode (+fnu/+fnl).
+ %% OBS: the node are stopped by end_per_testcase/2
+ Config1 = lists:keydelete(node,1,Config0),
+ Config2 = lists:keydelete(work_dir,1,Config1),
+ NodeName = list_to_atom("test_server_tester_" ++ atom_to_list(Encoding)),
+ ErtsSwitch = case Encoding of
+ latin1 -> "+fnl";
+ utf8 -> "+fnu"
+ end,
+ Config = start_node(Config2,NodeName,ErtsSwitch),
+
+ %% Compile the suite
+ Node = proplists:get_value(node,Config),
+ {ok,Mod} = rpc:call(Node,compile,file,[Suite,[{outdir,DataDir}]]),
+ ModStr = atom_to_list(Mod),
+
+ %% Clean logdir
+ LogDir0 = filename:join(DataDir,ModStr++".logs"),
+ LogDir = translate_filename(LogDir0,Encoding),
+ rm_dir(LogDir),
+
+ %% Run the test
+ run_test_server_tests(ModStr, [], 3, 0, 1, 1, 0, 0, 0, 0, 3, Config),
+
+ %% Check that all logs are created with utf8 encoded filenames
+ true = filelib:is_dir(LogDir),
+
+ RunDir = get_latest_run_dir(LogDir),
+ true = filelib:is_dir(RunDir),
+
+ LowerModStr = string:to_lower(ModStr),
+ SuiteHtml = translate_filename(LowerModStr++".src.html",Encoding),
+ true = filelib:is_regular(filename:join(RunDir,SuiteHtml)),
+
+ TCLog = translate_filename(LowerModStr++".tc_���.html",Encoding),
+ true = filelib:is_regular(filename:join(RunDir,TCLog)),
+ ok.
+
+%% Same as test_server_test_lib:start_slave, but starts a peer with
+%% additional arguments.
+%% The reason for this is that we need to start nodes with +fnu/+fnl,
+%% and that will not work well with a slave node since slave nodes run
+%% remote file system on master - i.e. they will use same file name
+%% mode as the master.
+start_node(Config,Name,Args) ->
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+ ct:log("Trying to start ~w@~s~n",[Name,Host]),
+ case test_server:start_node(Name, peer, [{args,Args}]) of
+ {error,Reason} ->
+ test_server:fail(Reason);
+ {ok,Node} ->
+ ct:log("Node ~p started~n", [Node]),
+ test_server_test_lib:prepare_tester_node(Node,Config)
+ end.
+
+create_unicode_test_suite(Dir,Encoding) ->
+ ModStr = "test_server_"++atom_to_list(Encoding)++"_���_SUITE",
+ File = filename:join(Dir,ModStr++".erl"),
+ Suite =
+ ["%% -*- ",epp:encoding_to_string(Encoding)," -*-\n",
+ "-module(",ModStr,").\n"
+ "\n"
+ "-export([all/1, init_per_suite/1, end_per_suite/1]).\n"
+ "-export([init_per_testcase/2, end_per_testcase/2]).\n"
+ "-export([tc_���/1]).\n"
+ "\n"
+ "-include_lib(\"test_server/include/test_server.hrl\").\n"
+ "\n"
+ "all(suite) ->\n"
+ " [tc_���].\n"
+ "\n"
+ "init_per_suite(Config) ->\n"
+ " Config.\n"
+ "\n"
+ "end_per_suite(_Config) ->\n"
+ " ok.\n"
+ "\n"
+ "init_per_testcase(_Case,Config) ->\n"
+ " init_timetrap(500,Config).\n"
+ "\n"
+ "init_timetrap(T,Config) ->\n"
+ " Dog = ?t:timetrap(T),\n"
+ " [{watchdog, Dog}|Config].\n"
+ "\n"
+ "end_per_testcase(_Case,Config) ->\n"
+ " cancel_timetrap(Config).\n"
+ "\n"
+ "cancel_timetrap(Config) ->\n"
+ " Dog=?config(watchdog, Config),\n"
+ " ?t:timetrap_cancel(Dog),\n"
+ " ok.\n"
+ "\n"
+ "tc_���(Config) when is_list(Config) ->\n"
+ " true = filelib:is_dir(?config(priv_dir,Config)),\n"
+ " ok.\n"],
+ {ok,Fd} = file:open(raw_filename(File,Encoding),[write,{encoding,Encoding}]),
+ io:put_chars(Fd,Suite),
+ ok = file:close(Fd),
+ File.
+
+raw_filename(Name,latin1) -> list_to_binary(Name);
+raw_filename(Name,utf8) -> unicode:characters_to_binary(Name).
+
+rm_dir(Dir) ->
+ case file:list_dir(Dir) of
+ {error,enoent} ->
+ ok;
+ {ok,Files} ->
+ rm_files([filename:join(Dir, F) || F <- Files]),
+ file:del_dir(Dir)
+ end.
+
+rm_files([F | Fs]) ->
+ case file:read_file_info(F) of
+ {ok,#file_info{type=directory}} ->
+ rm_dir(F),
+ rm_files(Fs);
+ {ok,_Regular} ->
+ case file:delete(F) of
+ ok ->
+ rm_files(Fs);
+ {error,Errno} ->
+ exit({del_failed,F,Errno})
+ end
+ end;
+rm_files([]) ->
+ ok.
+
+erts_switch(latin1) -> "+fnl";
+erts_switch(utf8) -> "+fnu".
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..5aeb035572 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,8 @@ 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
+ erlc test_server_unicode_SUITE.erl
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_SUITE_data/test_server_unicode_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_unicode_SUITE.erl
new file mode 100644
index 0000000000..662adedd4c
--- /dev/null
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_unicode_SUITE.erl
@@ -0,0 +1,82 @@
+%% -*- coding: utf-8 -*-
+%%
+%% %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(test_server_unicode_SUITE).
+
+-export([all/1, init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+-export([':#"|@\\ difficult_case_name_äöå'/1,
+ print_and_log_unicode/1,
+ print_and_log_latin1/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+all(suite) ->
+ [':#"|@\\ difficult_case_name_äöå',
+ print_and_log_unicode,
+ print_and_log_latin1].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+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) ->
+ cancel_timetrap(Config).
+
+cancel_timetrap(Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+
+':#"|@\\ difficult_case_name_äöå'(Config) when is_list(Config) ->
+ ok.
+
+print_and_log_unicode(Config) when is_list(Config) ->
+ String = "שלום-שלום+של 日本語",
+ test_server:comment(String),
+ test_server:capture_start(),
+ io:format("String with ts: ~ts",[String]),
+ test_server:capture_stop(),
+ "String with ts: "++String = lists:flatten(test_server:capture_get()),
+ ok.
+
+print_and_log_latin1(Config) when is_list(Config) ->
+ String = "æøå",
+ test_server:comment(String),
+ test_server:capture_start(),
+ io:format("String with s: ~s",[String]),
+ io:format("String with ts: ~ts",[String]),
+ test_server:capture_stop(),
+ ["String with s: "++String,
+ "String with ts: "++String] =
+ [lists:flatten(L) || L<- test_server:capture_get()],
+ 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..cd6804f7ad 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-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,6 +20,9 @@
-export([parse_suite/1]).
-export([init/2, pre_init_per_testcase/3, post_end_per_testcase/4]).
+%% for test_server_SUITE when node can not be started as slave
+-export([prepare_tester_node/2]).
+
-include("test_server_test_lib.hrl").
%% The CTH hooks all tests
@@ -49,39 +52,58 @@ start_slave(Config,_Level) ->
ct:log("Node ~p started~n", [Node]),
IsCover = test_server:is_cover(),
if IsCover ->
- cover:start(Node);
- true->
- ok
+ cover:start(Node);
+ true->
+ ok
end,
- DataDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
-
- %% PrivDir as well as directory of Test Server suites
- %% have to be in code path on Test Server node.
- [_ | Parts] = lists:reverse(filename:split(DataDir)),
- TSDir = filename:join(lists:reverse(Parts)),
- AddPathDirs = case proplists:get_value(path_dirs, Config) of
- undefined -> [];
- Ds -> Ds
- end,
- PathDirs = [PrivDir,TSDir | AddPathDirs],
- [true = rpc:call(Node, code, add_patha, [D]) || D <- PathDirs],
- io:format("Dirs added to code path (on ~w):~n",
- [Node]),
- [io:format("~s~n", [D]) || D <- PathDirs],
-
- true = rpc:call(Node, os, putenv,
- ["TEST_SERVER_FRAMEWORK", "undefined"]),
-
- ok = rpc:call(Node, file, set_cwd, [PrivDir]),
- [{node,Node} | Config]
+ prepare_tester_node(Node,Config)
end.
+prepare_tester_node(Node,Config) ->
+ DataDir = proplists:get_value(data_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,
+
+ %% 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)),
+ AddPathDirs = case proplists:get_value(path_dirs, Config) of
+ undefined -> [];
+ Ds -> Ds
+ end,
+ 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]),
+ [io:format("~s~n", [D]) || D <- PathDirs],
+
+ true = rpc:call(Node, os, putenv,
+ ["TEST_SERVER_FRAMEWORK", "undefined"]),
+
+ ok = rpc:call(Node, file, set_cwd, [WorkDir]),
+ [{node,Node}, {work_dir,WorkDir} | Config].
+
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 b956ebb2b3..0a5c4246f1 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1 +1 @@
-TEST_SERVER_VSN = 3.5.3
+TEST_SERVER_VSN = 3.6.1
diff --git a/lib/toolbar/doc/src/notes.xml b/lib/toolbar/doc/src/notes.xml
index f3ed41bd82..0ae8e261d6 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,21 @@
<p>This document describes the changes made to the Toolbar
application.</p>
+<section><title>Toolbar 1.4.2.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Toolbar 1.4.2.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/toolbar/src/Makefile b/lib/toolbar/src/Makefile
index 102970a59a..a24846976b 100644
--- a/lib/toolbar/src/Makefile
+++ b/lib/toolbar/src/Makefile
@@ -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/toolbar/vsn.mk b/lib/toolbar/vsn.mk
index 9fbfec871f..e84fb4e37b 100644
--- a/lib/toolbar/vsn.mk
+++ b/lib/toolbar/vsn.mk
@@ -1,4 +1,4 @@
-TOOLBAR_VSN = 1.4.2.2
+TOOLBAR_VSN = 1.4.2.3
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/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/notes.xml b/lib/tools/doc/src/notes.xml
index 5d7bd464ce..20dc9679f1 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,114 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.6.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix a bug in cover when used with no_auto_import. Thanks
+ to Jos� Valim.</p>
+ <p>
+ Own Id: OTP-10778</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.6.9</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>
+ <item>
+ <p>
+ Make erlang-mode more compatible with package.el (Thanks
+ to Gleb Peregud)</p>
+ <p>
+ Own Id: OTP-10465</p>
+ </item>
+ <item>
+ <p>
+ Fix various typos (thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-10611</p>
+ </item>
+ <item>
+ <p>
+ Add separate face for exported functions (Thanks to
+ Thomas J�rvstrand)</p>
+ <p>
+ Own Id: OTP-10637</p>
+ </item>
+ <item>
+ <p>
+ The BIF highlighting in the emacs mode has been updated
+ to correspond with the correct BIFs.</p>
+ <p>
+ Own Id: OTP-10774</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for Unicode has been implemented. </p>
+ <p>
+ Own Id: OTP-10302</p>
+ </item>
+ <item>
+ <p>
+ A new function, cover:flush(Nodes), is added which will
+ fetch data from remote nodes without stopping cover on
+ those nodes. This is used by test_server and common_test
+ when it is safe to assume that the node will be
+ terminated after the test anyway. The purpose is to avoid
+ processes crashing when re-loading the original beam if
+ the processes is still running old code.</p>
+ <p>
+ Remote nodes will now continue to count code coverage if
+ the connection to the main node is broken. Earlier, a
+ broken connection would cause the cover_server on the
+ remote node to die and thus any still cover compiled
+ modules would cause process crash when trying to insert
+ cover data in ets tables that used to exist on the
+ cover_server. The new functionality also involves
+ synchronization with the main node if the nodes are
+ reconnected.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-10523 Aux Id: OTP-10427 </p>
+ </item>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p>
+ Fix syntax highlighting of $\' in Emacs mode. Thanks to
+ Magnus Henoch.</p>
+ <p>
+ Own Id: OTP-10766</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.6.8</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..891a81639a 100644
--- a/lib/tools/doc/src/xref.xml
+++ b/lib/tools/doc/src/xref.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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/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 b5da9e79d8..ddd22707dd 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-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
@@ -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.
@@ -724,31 +723,32 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defvar erlang-int-bifs
'("abs"
- "adler32"
- "adler32_combine"
- "alive"
"apply"
"atom_to_binary"
"atom_to_list"
"binary_to_atom"
"binary_to_existing_atom"
+ "binary_to_float"
+ "binary_to_integer"
"binary_to_list"
"binary_to_term"
+ "binary_part"
"bit_size"
+ "bitsize"
"bitstring_to_list"
"byte_size"
+ "check_old_code"
"check_process_code"
- "contact_binary"
- "crc32"
- "crc32_combine"
"date"
- "decode_packet"
"delete_module"
+ "demonitor"
"disconnect_node"
"element"
"erase"
+ "error"
"exit"
"float"
+ "float_to_binary"
"float_to_list"
"garbage_collect"
"get"
@@ -757,7 +757,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"halt"
"hd"
"integer_to_list"
- "internal_bif"
+ "integer_to_binary"
"iolist_size"
"iolist_to_binary"
"is_alive"
@@ -788,13 +788,13 @@ resulting regexp is surrounded by \\_< and \\_>."
"list_to_tuple"
"load_module"
"make_ref"
+ "max"
+ "min"
"module_loaded"
+ "monitor"
"monitor_node"
"node"
- "node_link"
- "node_unlink"
"nodes"
- "notalive"
"now"
"open_port"
"pid_to_list"
@@ -838,48 +838,102 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defvar erlang-ext-bifs
- '("append_element"
+ '("adler32"
+ "adler32_combine"
+ "alloc_info"
+ "alloc_sizes"
+ "append"
+ "append_element"
+ "await_proc_exit"
+ "await_sched_wall_time_modifications"
+ "bitstr_to_list"
"bump_reductions"
+ "call_on_load_function"
"cancel_timer"
- "demonitor"
+ "crasher"
+ "crc32"
+ "crc32_combine"
+ "decode_packet"
+ "delay_trap"
+ "delete_element"
+ "dexit"
+ "dgroup_leader"
"display"
+ "display_nl"
+ "display_string"
+ "dist_exit"
+ "dlink"
+ "dmonitor_node"
+ "dmonitor_p"
+ "dsend"
+ "dt_append_vm_tag_data"
+ "dt_get_tag"
+ "dt_get_tag_data"
+ "dt_prepend_vm_tag_data"
+ "dt_put_tag"
+ "dt_restore_tag"
+ "dt_spread_tag"
+ "dunlink"
+ "external_size"
+ "finish_after_on_load"
+ "finish_loading"
+ "flush_monitor_message"
+ "format_cpu_topology"
"fun_info"
"fun_to_list"
"function_exported"
+ "garbage_collect_message_area"
+ "gather_sched_wall_time_result"
"get_cookie"
+ "get_module_info"
"get_stacktrace"
"hash"
- "integer_to_list"
+ "hibernate"
+ "insert_element"
"is_builtin"
- "list_to_integer"
+ "list_to_bitstr"
+ "load_nif"
"loaded"
"localtime"
"localtime_to_universaltime"
+ "make_fun"
"make_tuple"
- "max"
+ "match_spec_test"
"md5"
"md5_final"
"md5_init"
"md5_update"
"memory"
- "min"
- "monitor"
+ "module_info"
"monitor_node"
+ "nif_error"
"phash"
"phash2"
"port_call"
+ "port_get_data"
"port_info"
+ "port_set_data"
"port_to_list"
"ports"
+ "posixtime_to_universaltime"
+ "prepare_loading"
"process_display"
+ "raise"
"read_timer"
"ref_to_list"
"resume_process"
"send"
"send_after"
"send_nosuspend"
+ "seq_trace"
+ "seq_trace_info"
+ "seq_trace_print"
"set_cookie"
+ "set_cpu_topology"
+ "setnode"
+ "spawn_opt"
"start_timer"
+ "subtract"
"suspend_process"
"system_flag"
"system_info"
@@ -891,6 +945,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"trace_pattern"
"universaltime"
"universaltime_to_localtime"
+ "universaltime_to_posixtime"
"yield")
"Erlang built-in functions (BIFs) that needs erlang: prefix"))
@@ -998,6 +1053,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 +1204,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 +1247,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 +1400,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 +1515,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."
@@ -1527,9 +1574,9 @@ Other commands:
. (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w")
;; Likewise for atoms
("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w")
- ;; And the dollar sign in $\" escapes two characters, not
- ;; just one.
- ("\\(\\$\\)\\\\\\\"" 1 "'"))))))
+ ;; And the dollar sign in $\" or $\' escapes two
+ ;; characters, not just one.
+ ("\\(\\$\\)\\\\[\"']" 1 "'"))))))
@@ -3689,6 +3736,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
@@ -4896,7 +4975,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/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 abe1389771..e606b97a48 100644
--- a/lib/tools/src/Makefile
+++ b/lib/tools/src/Makefile
@@ -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
@@ -75,7 +75,7 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS +=
+ERL_COMPILE_FLAGS += -Werror
# ----------------------------------------------------
# Targets
@@ -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..2579711dc7 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-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,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,16 +792,39 @@ 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]),
+ io:format("~tp~n",[State]),
main_process_loop(State)
end.
@@ -850,10 +877,19 @@ 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]),
+ io:format("~tp~n",[State]),
remote_process_loop(State);
M ->
@@ -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: ~tp\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--------------------------------------
@@ -1134,7 +1223,7 @@ do_get_all_importfiles([],Acc) ->
imported_info(Text,Module,Imported) ->
case lists:keysearch(Module,1,Imported) of
{value,{Module,_File,ImportFiles}} ->
- io:format("~s includes data from imported files\n~p\n",
+ io:format("~ts includes data from imported files\n~tp\n",
[Text,ImportFiles]);
false ->
ok
@@ -1148,7 +1237,7 @@ add_imported(Module, File, ImportFile, Imported) ->
add_imported(M, F1, ImportFile, [{M,_F2,ImportFiles}|Imported], Acc) ->
case lists:member(ImportFile,ImportFiles) of
true ->
- io:fwrite("WARNING: Module ~w already imported from ~p~n"
+ io:fwrite("WARNING: Module ~w already imported from ~tp~n"
"Not importing again!~n",[M,ImportFile]),
dont_import;
false ->
@@ -1166,7 +1255,7 @@ remove_imported(Module,Imported) ->
case lists:keysearch(Module,1,Imported) of
{value,{Module,_,ImportFiles}} ->
io:fwrite("WARNING: Deleting data for module ~w imported from~n"
- "~p~n",[Module,ImportFiles]),
+ "~tp~n",[Module,ImportFiles]),
lists:keydelete(Module,1,Imported);
false ->
Imported
@@ -1283,10 +1372,15 @@ do_compile_beam(Module,Beam,UserOptions) ->
Forms0 = epp:interpret_file_attribute(Code),
{Forms,Vars} = transform(Vsn, Forms0, Module, Beam),
+ %% We need to recover the source from the compilation
+ %% info otherwise the newly compiled module will have
+ %% source pointing to the current directory
+ SourceInfo = get_source_info(Module, Beam),
+
%% Compile and load the result
%% It's necessary to check the result of loading since it may
%% fail, for example if Module resides in a sticky directory
- {ok, Module, Binary} = compile:forms(Forms, UserOptions),
+ {ok, Module, Binary} = compile:forms(Forms, SourceInfo ++ UserOptions),
case code:load_binary(Module, ?TAG, Binary) of
{module, Module} ->
@@ -1314,6 +1408,17 @@ get_abstract_code(Module, Beam) ->
Error -> Error
end.
+get_source_info(Module, Beam) ->
+ case beam_lib:chunks(Beam, [compile_info]) of
+ {ok, {Module, [{compile_info, Compile}]}} ->
+ case lists:keyfind(source, 1, Compile) of
+ { source, _ } = Tuple -> [Tuple];
+ false -> []
+ end;
+ _ ->
+ []
+ end.
+
transform(Vsn, Code, Module, Beam) when Vsn=:=abstract_v1; Vsn=:=abstract_v2 ->
Vars0 = #vars{module=Module, vsn=Vsn},
MainFile=find_main_filename(Code),
@@ -1430,12 +1535,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,
@@ -1700,17 +1799,11 @@ munge_expr({'catch',Line,Expr}, Vars) ->
{MungedExpr, Vars2} = munge_expr(Expr, Vars),
{{'catch',Line,MungedExpr}, Vars2};
munge_expr({call,Line1,{remote,Line2,ExprM,ExprF},Exprs},
- Vars) when Vars#vars.is_guard=:=false->
+ Vars) ->
{MungedExprM, Vars2} = munge_expr(ExprM, Vars),
{MungedExprF, Vars3} = munge_expr(ExprF, Vars2),
{MungedExprs, Vars4} = munge_exprs(Exprs, Vars3, []),
{{call,Line1,{remote,Line2,MungedExprM,MungedExprF},MungedExprs}, Vars4};
-munge_expr({call,Line1,{remote,_Line2,_ExprM,ExprF},Exprs},
- Vars) when Vars#vars.is_guard=:=true ->
- %% Difference in abstract format after preprocessing: BIF calls in guards
- %% are translated to {remote,...} (which is not allowed as source form)
- %% NOT NECESSARY FOR Vsn=raw_abstract_v1
- munge_expr({call,Line1,ExprF,Exprs}, Vars);
munge_expr({call,Line,Expr,Exprs}, Vars) ->
{MungedExpr, Vars2} = munge_expr(Expr, Vars),
{MungedExprs, Vars3} = munge_exprs(Exprs, Vars2, []),
@@ -1862,7 +1955,7 @@ move_clauses([]) ->
%% Given a .beam file, find the .erl file. Look first in same directory as
%% the .beam file, then in <beamdir>/../src
-find_source(File0) ->
+find_source(Module, File0) ->
case filename:rootname(File0,".beam") of
File0 ->
File0;
@@ -1879,11 +1972,27 @@ find_source(File0) ->
true ->
InDotDotSrc;
false ->
- {beam,File0}
+ find_source_from_module(Module, File0)
end
end
end.
+%% In case we can't find the file from the given .beam,
+%% we try to get the information directly from the module source
+find_source_from_module(Module, File) ->
+ Compile = Module:module_info(compile),
+ case lists:keyfind(source, 1, Compile) of
+ {source, Path} ->
+ case filelib:is_file(Path) of
+ true ->
+ Path;
+ false ->
+ {beam, File}
+ end;
+ false ->
+ {beam, File}
+ end.
+
do_parallel_analysis(Module, Analysis, Level, Loaded, From, State) ->
analyse_info(Module,State#main_state.imported),
C = case Loaded of
@@ -1987,7 +2096,7 @@ do_parallel_analysis_to_file(Module, OutFile, Opts, Loaded, From, State) ->
{imported, File0, _} ->
File0
end,
- case find_source(File) of
+ case find_source(Module, File) of
{beam,_BeamFile} ->
reply(From, {error,no_source_code_found});
ErlFile ->
@@ -2007,30 +2116,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 +2373,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 +2432,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 +2441,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..f13a297ecf 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-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
@@ -271,7 +272,7 @@ handle_call({locations, InOpts}, _From, #state{ locks = Locks } = State) when is
Opts = options(InOpts, Default),
Printables = filter_print([#print{
name = string_names(Names),
- entry = term2string("~p:~p", [Stats#stats.file, Stats#stats.line]),
+ entry = term2string("~tp:~p", [Stats#stats.file, Stats#stats.line]),
colls = Stats#stats.colls,
tries = Stats#stats.tries,
cr = percent(Stats#stats.colls, Stats#stats.tries),
@@ -566,7 +567,7 @@ stats2print(Stats, Duration) ->
lists:map(fun
(S) ->
#print{
- entry = term2string("~p:~p", [S#stats.file, S#stats.line]),
+ entry = term2string("~tp:~p", [S#stats.file, S#stats.line]),
colls = S#stats.colls,
tries = S#stats.tries,
cr = percent(S#stats.colls, S#stats.tries),
@@ -797,20 +798,20 @@ options1([{Key, Value}|Opts], Defaults) ->
%%% AUX STRING FORMATTING
-print(String) -> io:format("~s~n", [String]).
+print(String) -> io:format("~ts~n", [String]).
kv(Key, Value) -> kv(Key, Value, 20).
kv(Key, Value, Offset) -> term2string(term2string("~~~ps : ~~s", [Offset]),[Key, Value]).
s(T) when is_float(T) -> term2string("~.4f", [T]);
-s(T) when is_list(T) -> term2string("~s", [T]);
+s(T) when is_list(T) -> term2string("~ts", [T]);
s(T) -> term2string(T).
strings(Strings) -> strings(Strings, []).
strings([], Out) -> Out;
strings([{space, N, S} | Ss], Out) -> strings(Ss, Out ++ term2string(term2string("~~~ps", [N]), [S]));
strings([{format, Format, S} | Ss], Out) -> strings(Ss, Out ++ term2string(Format, [S]));
-strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~s", [S])).
+strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~ts", [S])).
term2string({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> term2string("~p:~p/~p", [M,F,A]);
diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl
index 5cc8d47faa..c8ef0a04a5 100644
--- a/lib/tools/src/make.erl
+++ b/lib/tools/src/make.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
@@ -90,7 +90,7 @@ read_emakefile(Emakefile,Opts) ->
Mods = [filename:rootname(F) || F <- filelib:wildcard("*.erl")],
[{Mods, Opts}];
{error,Other} ->
- io:format("make: Trouble reading 'Emakefile':~n~p~n",[Other]),
+ io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]),
error
end.
@@ -145,7 +145,7 @@ get_opts_from_emakefile(Mods,Emakefile,Opts) ->
{error,enoent} ->
[{Mods, Opts}];
{error,Other} ->
- io:format("make: Trouble reading 'Emakefile':~n~p~n",[Other]),
+ io:format("make: Trouble reading 'Emakefile':~n~tp~n",[Other]),
error
end.
@@ -253,15 +253,15 @@ include_opt([]) ->
%% Where load can be netload | load | noload
recompile(File, true, _Load, _Opts) ->
- io:format("Out of date: ~s\n",[File]);
+ io:format("Out of date: ~ts\n",[File]);
recompile(File, false, noload, Opts) ->
- io:format("Recompile: ~s\n",[File]),
+ io:format("Recompile: ~ts\n",[File]),
compile:file(File, [report_errors, report_warnings, error_summary |Opts]);
recompile(File, false, load, Opts) ->
- io:format("Recompile: ~s\n",[File]),
+ io:format("Recompile: ~ts\n",[File]),
c:c(File, Opts);
recompile(File, false, netload, Opts) ->
- io:format("Recompile: ~s\n",[File]),
+ io:format("Recompile: ~ts\n",[File]),
c:nc(File, Opts).
exists(File) ->
diff --git a/lib/tools/src/tags.erl b/lib/tools/src/tags.erl
index e740d38c91..1c72ef8db5 100644
--- a/lib/tools/src/tags.erl
+++ b/lib/tools/src/tags.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. 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
@@ -157,7 +157,7 @@ files_loop([F | Fs], Os) ->
ok ->
ok;
error ->
- %% io:format("Could not open ~s~n", [F]),
+ %% io:format("Could not open ~ts~n", [F]),
error
end,
files_loop(Fs, Os).
@@ -315,11 +315,11 @@ close_out(Os) ->
pfnote(Str, {LineNo, CharNo}) ->
- io_lib:format("~s\177~w,~w~n", [flatrev(Str), LineNo, CharNo]).
+ io_lib:format("~ts\177~w,~w~n", [flatrev(Str), LineNo, CharNo]).
genout(Os, Name, Entries) ->
- io:format(Os, "\^l~n~s,~w~n", [Name, reclength(Entries)]),
+ io:format(Os, "\^l~n~ts,~w~n", [Name, reclength(Entries)]),
io:put_chars(Os, lists:reverse(Entries)).
diff --git a/lib/tools/src/xref.erl b/lib/tools/src/xref.erl
index 0693bec019..abc184c84d 100644
--- a/lib/tools/src/xref.erl
+++ b/lib/tools/src/xref.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -297,7 +297,7 @@ set_default(Name, Option, Value) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
%%%----------------------------------------------------------------------
%%%Callback functions from gen_server
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index 93f0e9c0c8..30c5f3d12d 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -480,43 +480,43 @@ set_default(State, Options) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({invalid_options, Options}) ->
- io_lib:format("Unknown option(s) or invalid option value(s): ~p~n",
+ io_lib:format("Unknown option(s) or invalid option value(s): ~tp~n",
[Options]);
format_error({invalid_filename, Term}) ->
- io_lib:format("A file name (a string) was expected: ~p~n", [Term]);
+ io_lib:format("A file name (a string) was expected: ~tp~n", [Term]);
format_error({no_debug_info, FileName}) ->
- io_lib:format("The BEAM file ~p has no debug info~n", [FileName]);
+ io_lib:format("The BEAM file ~tp has no debug info~n", [FileName]);
format_error({invalid_path, Term}) ->
- io_lib:format("A path (a list of strings) was expected: ~p~n", [Term]);
+ io_lib:format("A path (a list of strings) was expected: ~tp~n", [Term]);
format_error({invalid_query, Term}) ->
- io_lib:format("A query (a string or an atom) was expected: ~p~n", [Term]);
+ io_lib:format("A query (a string or an atom) was expected: ~tp~n", [Term]);
format_error({not_user_variable, Variable}) ->
- io_lib:format("~p is not a user variable~n", [Variable]);
+ io_lib:format("~tp is not a user variable~n", [Variable]);
format_error({unknown_analysis, Term}) ->
- io_lib:format("~p is not a predefined analysis~n", [Term]);
+ io_lib:format("~tp is not a predefined analysis~n", [Term]);
format_error({module_mismatch, Module, ReadModule}) ->
- io_lib:format("Name of read module ~p does not match analyzed module ~p~n",
+ io_lib:format("Name of read module ~tp does not match analyzed module ~tp~n",
[ReadModule, Module]);
format_error({release_clash, {Release, Dir, OldDir}}) ->
- io_lib:format("The release ~p read from ~p clashes with release "
- "already read from ~p~n", [Release, Dir, OldDir]);
+ io_lib:format("The release ~tp read from ~tp clashes with release "
+ "already read from ~tp~n", [Release, Dir, OldDir]);
format_error({application_clash, {Application, Dir, OldDir}}) ->
- io_lib:format("The application ~p read from ~p clashes with application "
- "already read from ~p~n", [Application, Dir, OldDir]);
+ io_lib:format("The application ~tp read from ~tp clashes with application "
+ "already read from ~tp~n", [Application, Dir, OldDir]);
format_error({module_clash, {Module, Dir, OldDir}}) ->
- io_lib:format("The module ~p read from ~p clashes with module "
- "already read from ~p~n", [Module, Dir, OldDir]);
+ io_lib:format("The module ~tp read from ~tp clashes with module "
+ "already read from ~tp~n", [Module, Dir, OldDir]);
format_error({no_such_release, Name}) ->
- io_lib:format("There is no analyzed release ~p~n", [Name]);
+ io_lib:format("There is no analyzed release ~tp~n", [Name]);
format_error({no_such_application, Name}) ->
- io_lib:format("There is no analyzed application ~p~n", [Name]);
+ io_lib:format("There is no analyzed application ~tp~n", [Name]);
format_error({no_such_module, Name}) ->
- io_lib:format("There is no analyzed module ~p~n", [Name]);
+ io_lib:format("There is no analyzed module ~tp~n", [Name]);
format_error({no_such_info, Term}) ->
- io_lib:format("~p is not one of 'modules', 'applications', "
+ io_lib:format("~tp is not one of 'modules', 'applications', "
"'releases' and 'libraries'~n", [Term]);
format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
%%
%% Local functions
@@ -1506,7 +1506,7 @@ do_variables(State) ->
_Else -> {[Name | P], U}
end;
({{tmp, V}, _}, A) ->
- io:format("Bug in ~p: temporary ~p~n", [?MODULE, V]), A;
+ io:format("Bug in ~tp: temporary ~tp~n", [?MODULE, V]), A;
(_V, A) -> A
end,
{U,P} = foldl(Fun, {[],[]}, dict:to_list(State#xref.variables)),
@@ -1766,23 +1766,23 @@ tpack(T, I, L) ->
message(true, What, Arg) ->
case What of
reading_beam ->
- io:format("~s... ", Arg);
+ io:format("~ts... ", Arg);
skipped_beam ->
io:format("skipped (no debug information)~n", Arg);
no_debug_info ->
- io:format("Skipping ~s (no debug information)~n", Arg);
+ io:format("Skipping ~ts (no debug information)~n", Arg);
unresolved_summary1 ->
- io:format("~p: 1 unresolved call~n", Arg);
+ io:format("~tp: 1 unresolved call~n", Arg);
unresolved_summary ->
- io:format("~p: ~p unresolved calls~n", Arg);
+ io:format("~tp: ~tp unresolved calls~n", Arg);
jam ->
- io:format("Skipping ~s (probably JAM file)~n", [Arg]);
+ io:format("Skipping ~ts (probably JAM file)~n", [Arg]);
unreadable ->
- io:format("Skipping ~s (unreadable)~n", [Arg]);
+ io:format("Skipping ~ts (unreadable)~n", [Arg]);
xref_attr ->
- io:format("~s: Skipping 'xref' attribute ~w~n", Arg);
+ io:format("~ts: Skipping 'xref' attribute ~w~n", Arg);
depr_attr ->
- io:format("~s: Skipping 'deprecated' attribute ~w~n", Arg);
+ io:format("~ts: Skipping 'deprecated' attribute ~w~n", Arg);
lib_search ->
io:format("Scanning library path for BEAM files... ", []);
lib_check ->
@@ -1794,7 +1794,7 @@ message(true, What, Arg) ->
error ->
io:format("error~n", Arg);
Else ->
- io:format("~p~n", [{Else,Arg}])
+ io:format("~tp~n", [{Else,Arg}])
end;
message(_, _, _) ->
true.
diff --git a/lib/tools/src/xref_compiler.erl b/lib/tools/src/xref_compiler.erl
index 22312c6754..f0fed502a5 100644
--- a/lib/tools/src/xref_compiler.erl
+++ b/lib/tools/src/xref_compiler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -83,19 +83,19 @@ format_error({error, Module, Error}) ->
format_error({parse_error, Line, Error}) ->
format_parse_error(Error, format_line(Line));
format_error({variable_reassigned, Expr}) ->
- io_lib:format("Variable assigned more than once: ~s~n", [Expr]);
+ io_lib:format("Variable assigned more than once: ~ts~n", [Expr]);
format_error({unknown_variable, Name}) ->
- io_lib:format("Variable ~p used before set~n", [Name]);
+ io_lib:format("Variable ~tp used before set~n", [Name]);
format_error({type_error, Expr}) ->
io_lib:format("Operator applied to argument(s) of different or "
- "invalid type(s): ~s~n", [Expr]);
+ "invalid type(s): ~ts~n", [Expr]);
format_error({type_mismatch, Expr1, Expr2}) ->
- io_lib:format("Constants of different types: ~s, ~s~n",
+ io_lib:format("Constants of different types: ~ts, ~ts~n",
[Expr1, Expr2]);
format_error({unknown_constant, Constant}) ->
- io_lib:format("Unknown constant ~s~n", [Constant]);
+ io_lib:format("Unknown constant ~ts~n", [Constant]);
format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
%%
%% Local functions
@@ -908,21 +908,21 @@ fetch_value(V, D) ->
Value.
format_parse_error(["invalid_regexp", String, Error], Line) ->
- io_lib:format("Invalid regular expression \"~s\"~s: ~s~n",
+ io_lib:format("Invalid regular expression \"~ts\"~s: ~ts~n",
[String, Line, lists:flatten(Error)]);
format_parse_error(["invalid_regexp_variable", Var], Line) ->
- io_lib:format("Invalid wildcard variable ~p~s "
+ io_lib:format("Invalid wildcard variable ~tp~s "
"(only '_' is allowed)~n", [Var, Line]);
format_parse_error(["missing_type", Expr], Line) ->
- io_lib:format("Missing type of regular expression ~s~s~n",
+ io_lib:format("Missing type of regular expression ~ts~s~n",
[Expr, Line]);
format_parse_error(["type_mismatch", Expr], Line) ->
- io_lib:format("Type does not match structure of constant~s: ~s~n",
+ io_lib:format("Type does not match structure of constant~s: ~ts~n",
[Line, Expr]);
format_parse_error(["invalid_operator", Op], Line) ->
- io_lib:format("Invalid operator ~p~s~n", [Op, Line]);
+ io_lib:format("Invalid operator ~tp~s~n", [Op, Line]);
format_parse_error(Error, Line) ->
- io_lib:format("Parse error~s: ~s~n", [Line, lists:flatten(Error)]).
+ io_lib:format("Parse error~s: ~ts~n", [Line, lists:flatten(Error)]).
format_line(-1) ->
" at end of string";
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index 92f0c45c7b..d3601c6ea0 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -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 680563e9df..7b72165e6f 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2000-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
@@ -517,16 +517,16 @@ subprocess(Fun, Opts) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({file_error, FileName, Reason}) ->
- io_lib:format("~s: ~p~n", [FileName, file:format_error(Reason)]);
+ io_lib:format("~ts: ~tp~n", [FileName, file:format_error(Reason)]);
format_error({unrecognized_file, FileName}) ->
- io_lib:format("~p is neither a regular file nor a directory~n",
+ io_lib:format("~tp is neither a regular file nor a directory~n",
[FileName]);
format_error({no_such_module, Module}) ->
- io_lib:format("Cannot find module ~p using the code path~n", [Module]);
+ io_lib:format("Cannot find module ~tp using the code path~n", [Module]);
format_error({interpreted, Module}) ->
- io_lib:format("Cannot use BEAM code of interpreted module ~p~n", [Module]);
+ io_lib:format("Cannot use BEAM code of interpreted module ~tp~n", [Module]);
format_error(E) ->
- io_lib:format("~p~n", [E]).
+ io_lib:format("~tp~n", [E]).
%%
%% Local functions
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index 86c81217b6..484cfdf53f 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -22,6 +22,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
cover_SUITE \
eprof_SUITE \
+ emacs_SUITE \
emem_SUITE \
fprof_SUITE \
cprof_SUITE \
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index c2c708d806..5abc5c41b1 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -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) -> [];
@@ -142,7 +149,9 @@ compile(Config) when is_list(Config) ->
ok = beam_lib:crypto_key_fun(simple_crypto_fun(Key)),
{ok,crypt} = cover:compile_beam("crypt.beam")
end,
+ Path = filename:join([?config(data_dir, Config), "compile_beam", "v.erl"]),
?line {ok,v} = cover:compile_beam(v),
+ {source,Path} = lists:keyfind(source, 1, v:module_info(compile)),
?line {ok,w} = cover:compile_beam("w.beam"),
?line {error,{no_abstract_code,"./x.beam"}} = cover:compile_beam(x),
?line {error,{already_cover_compiled,no_beam_found,a}}=cover:compile_beam(a),
@@ -270,12 +279,23 @@ analyse(Config) when is_list(Config) ->
?line f:f2(),
?line {ok, "f.COVER.out"} = cover:analyse_to_file(f),
- %% Source code cannot be found by analyse_to_file
+ %% Source code can be found via source
?line {ok,v} = compile:file("compile_beam/v",[debug_info]),
?line code:purge(v),
?line {module,v} = code:load_file(v),
?line {ok,v} = cover:compile_beam(v),
- ?line {error,no_source_code_found} = cover:analyse_to_file(v),
+ {ok,"v.COVER.out"} = cover:analyse_to_file(v),
+
+ %% Source code cannot be found
+ {ok,_} = file:copy("compile_beam/z.erl", "z.erl"),
+ {ok,z} = compile:file(z,[debug_info]),
+ code:purge(z),
+ {module,z} = code:load_file(z),
+ {ok,z} = cover:compile_beam(z),
+ ok = file:delete("z.erl"),
+ {error,no_source_code_found} = cover:analyse_to_file(z),
+ code:purge(z),
+ code:delete(z),
?line {error,{not_cover_compiled,b}} = cover:analyse(b),
?line {error,{not_cover_compiled,g}} = cover:analyse(g),
@@ -326,14 +346,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 +373,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 +408,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 +428,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 +1015,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 +1458,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/compile_beam/v.erl b/lib/tools/test/cover_SUITE_data/compile_beam/v.erl
index 007957297a..7fb0b08d40 100644
--- a/lib/tools/test/cover_SUITE_data/compile_beam/v.erl
+++ b/lib/tools/test/cover_SUITE_data/compile_beam/v.erl
@@ -1,6 +1,9 @@
-module(v).
-
--export([f/0]).
+-compile({ no_auto_import, [is_integer/1] }).
+-export([f/0,f/1]).
f() ->
ok.
+
+f(Number) when erlang:is_integer(Number) ->
+ Number.
diff --git a/lib/tools/test/cover_SUITE_data/compile_beam/z.erl b/lib/tools/test/cover_SUITE_data/compile_beam/z.erl
new file mode 100644
index 0000000000..7a2b143dde
--- /dev/null
+++ b/lib/tools/test/cover_SUITE_data/compile_beam/z.erl
@@ -0,0 +1 @@
+-module(z).
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/cprof_SUITE.erl b/lib/tools/test/cprof_SUITE.erl
index ce5cf66a14..fa4068dade 100644
--- a/lib/tools/test/cprof_SUITE.erl
+++ b/lib/tools/test/cprof_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl
new file mode 100644
index 0000000000..657a3002a3
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE.erl
@@ -0,0 +1,76 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-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(emacs_SUITE).
+
+%%-define(line_trace, 1).
+
+-export([all/0, init_per_testcase/2, end_per_testcase/2]).
+
+-export([bif_highlight/1]).
+
+all() ->
+ [bif_highlight].
+
+init_per_testcase(_Case, Config) ->
+ ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]),
+ case file:read_file_info(ErlangEl) of
+ {ok, _} ->
+ [{el, ErlangEl}|Config];
+ _ ->
+ {skip, "Could not find erlang.el"}
+ end.
+
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+bif_highlight(Config) ->
+ ErlangEl = proplists:get_value(el,Config),
+ {ok, Bin} = file:read_file(ErlangEl),
+
+ %% All auto-imported bifs
+ IntBifs = lists:usort(
+ [F || {F,A} <- erlang:module_info(exports),
+ erl_internal:bif(F,A)]),
+
+ %% all bif which need erlang: prefix and are not operands
+ ExtBifs = lists:usort(
+ [F || {F,A} <- erlang:module_info(exports),
+ not erl_internal:bif(F,A) andalso
+ not is_atom(catch erl_internal:op_type(F,A))]),
+
+ check_bif_highlight(Bin, <<"erlang-int-bifs">>, IntBifs),
+ check_bif_highlight(Bin, <<"erlang-ext-bifs">>, ExtBifs).
+
+
+check_bif_highlight(Bin, Tag, Compare) ->
+ [_H,IntMatch,_T] =
+ re:split(Bin,<<"defvar ",Tag/binary,
+ "[^(]*\\(([^)]*)">>,[]),
+ EmacsIntBifs = [list_to_atom(S) ||
+ S <- string:tokens(binary_to_list(IntMatch)," '\"\n")],
+
+ ct:log("Emacs ~p",[EmacsIntBifs]),
+ ct:log("Int ~p",[Compare]),
+
+ ct:log("Diff1 ~p",[Compare -- EmacsIntBifs]),
+ ct:log("Diff2 ~p",[EmacsIntBifs -- Compare]),
+ [] = Compare -- EmacsIntBifs,
+ [] = EmacsIntBifs -- Compare.
+
+
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index fd3e111d8d..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,7 +47,7 @@
-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([
@@ -82,7 +83,7 @@ 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]},
@@ -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) ->
@@ -2521,7 +2464,7 @@ otp_10192(doc) ->
otp_10192(Conf) when is_list(Conf) ->
PrivDir = ?privdir,
{ok, _Pid} = xref:start(s),
- Dir = filename:join(PrivDir, "�"),
+ Dir = filename:join(PrivDir, "ä"),
ok = file:make_dir(Dir),
{ok, []} = xref:add_directory(s, Dir),
xref:stop(s),
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 788ee12900..4fb2f30e4f 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.6.8
+TOOLS_VSN = 2.6.10
diff --git a/lib/tv/doc/src/notes.xml b/lib/tv/doc/src/notes.xml
index fa72dd23a9..c95171568f 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,21 @@
</header>
<p>This document describes the changes made to the TV application.</p>
+<section><title>TV 2.1.4.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>TV 2.1.4.9</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/tv/src/Makefile b/lib/tv/src/Makefile
index da1713e156..3d680c1eaf 100644
--- a/lib/tv/src/Makefile
+++ b/lib/tv/src/Makefile
@@ -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..c9db1d9484 100644
--- a/lib/tv/src/tv_main.hrl
+++ b/lib/tv/src/tv_main.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. 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
@@ -131,7 +131,6 @@
ir_WstringDef,
lmcounter,
locks,
- mnemosyne_tmp,
pg2_table,
queue,
snmp_agent_table,
diff --git a/lib/tv/vsn.mk b/lib/tv/vsn.mk
index 1bb8ca30f8..59d124a3b7 100644
--- a/lib/tv/vsn.mk
+++ b/lib/tv/vsn.mk
@@ -1 +1 @@
-TV_VSN = 2.1.4.9
+TV_VSN = 2.1.4.10
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
index 1f94d8fdc8..13af466755 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -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 616684fd69..0ace1d5fb8 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -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 18e328afc1..5ac145d9ff 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.9.4
+TYPER_VSN = 0.9.5
diff --git a/lib/webtool/doc/src/notes.xml b/lib/webtool/doc/src/notes.xml
index a56fdd7b6f..76b73daa59 100644
--- a/lib/webtool/doc/src/notes.xml
+++ b/lib/webtool/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,21 @@
<p>This document describes the changes made to the Webtool
application.</p>
+<section><title>WebTool 0.8.9.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc build updates</p>
+ <p>
+ Own Id: OTP-10784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>WebTool 0.8.9.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile
index 783ffad79a..f28c777240 100644
--- a/lib/webtool/src/Makefile
+++ b/lib/webtool/src/Makefile
@@ -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/webtool/vsn.mk b/lib/webtool/vsn.mk
index 690f9a22cf..d356a8954d 100644
--- a/lib/webtool/vsn.mk
+++ b/lib/webtool/vsn.mk
@@ -1 +1 @@
-WEBTOOL_VSN=0.8.9.1
+WEBTOOL_VSN=0.8.9.2
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 9578cd35c4..25f40944e7 100644
--- a/lib/wx/aclocal.m4
+++ b/lib/wx/aclocal.m4
@@ -1,7 +1,7 @@
dnl
dnl %CopyrightBegin%
dnl
-dnl Copyright Ericsson AB 1998-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 1998-2013. 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
@@ -1849,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/bugs.h b/lib/wx/api_gen/wx_extra/bugs.h
index b8f3dfcb00..e3a4fa200b 100644
--- a/lib/wx/api_gen/wx_extra/bugs.h
+++ b/lib/wx/api_gen/wx_extra/bugs.h
@@ -34,3 +34,9 @@ class WXDLLIMPEXP_ADV wxGridCellNumberRenderer : public wxGridCellStringRenderer
wxGridCellNumberRenderer();
};
+// Enable test for valid wxTreeItemId's
+class WXDLLIMPEXP_ADV wxTreeCtrlBase : public wxControl
+{
+ public:
+ static bool IsTreeItemIdOk(wxTreeItemId id);
+}
diff --git a/lib/wx/api_gen/wx_extra/wxListCtrl.c_src b/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
index 54d6fafd01..8fa31e512e 100644
--- a/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
+++ b/lib/wx/api_gen/wx_extra/wxListCtrl.c_src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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 +27,7 @@ class EwxListCtrl : public wxListCtrl {
int onGetItemText;
int onGetItemAttr;
int onGetItemColumnImage;
- ErlDrvPort port;
+ ErlDrvTermData port;
private:
virtual wxString OnGetItemText(long item, long col) const;
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/erts/test/autoimport_SUITE_data/dummy.txt b/lib/wx/api_gen/wx_extra/wxTreeCtrl.c_src
index 972643e527..50dca4e2bb 100644
--- a/erts/test/autoimport_SUITE_data/dummy.txt
+++ b/lib/wx/api_gen/wx_extra/wxTreeCtrl.c_src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% 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
@@ -16,4 +16,13 @@
%%
%% %CopyrightEnd%
%%
-%% Purpouse: Dummy
+
+
+<<wxTreeCtrl_IsTreeItemIdOk
+case ~s: { // wxTreeCtrl::IsTreeItemIdOk
+ wxTreeItemId item = wxTreeItemId((void *) *(wxUint64 *) bp); bp += 8;
+ bool Result = item.IsOk();
+ rt.addBool(Result);
+ break;
+}
+wxTreeCtrl_IsTreeItemIdOk>>
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..f00fc0c720 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.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
@@ -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}) ->
@@ -1181,7 +1210,7 @@ encode_events(Evs) ->
" WxeApp * app = (WxeApp *) wxTheApp;~n"
" return app->getRef(ptr,memenv);~n"
"}~n~n"),
- w("bool sendevent(wxEvent *event, ErlDrvPort port)~n{~n"
+ w("bool sendevent(wxEvent *event, ErlDrvTermData port)~n{~n"
" int send_res ;~n"
" char * evClass = NULL;~n"
" wxMBConvUTF32 UTFconverter;~n"
@@ -1210,7 +1239,7 @@ encode_events(Evs) ->
w(" rt.addTupleCount(3);~n"),
w(" pre_callback();~n"),
w(" send_res = rt.send();~n"),
- w(" if(send_res) handle_event_callback(port, cb->listener);~n"),
+ w(" if(send_res) handle_event_callback(WXE_DRV_PORT_HANDLE, cb->listener);~n"),
w(" app->clearPtr((void *) event);~n"),
w(" } else {~n"),
w(" send_res = rt.send();~n"),
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index a8f23575f3..2e1a0d617a 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_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
@@ -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]),
@@ -215,36 +218,47 @@ check_class(#type{base={class,Name},xml=Xml}) ->
gen_export(#class{name=Class,abstract=Abs},Ms0) ->
RemoveC = fun(#method{where=merged_c}) -> false;(_Other) -> true end,
Res = filter(RemoveC, Ms0),
+ 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,
+ deprecated(M, "new" ++ "/" ++ integer_to_list(length(Args)+OptLen));
+ (M=#method{method_type=destructor}) ->
+ case Abs of
+ true -> [];
+ _ -> deprecated(M, "destroy/1")
+ end;
+ (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,
+ deprecated(M, erl_func_name(N,A) ++ "/" ++ integer_to_list(length(Args) + OptLen))
+ end,
case Res of
[] -> [];
[M=#method{where=taylormade}|_] ->
- [taylormade_export(Class, M)];
+ try
+ [deprecated(M, taylormade_export(Class, M))]
+ catch error:{badmatch, {error, enoent}} ->
+ lists:map(GetF, Res)
+ end;
Ms ->
- GetF = fun(#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}) ->
- case Abs of
- true -> [];
- _ -> "destroy/1"
- end;
- (#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)
- 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,
@@ -252,7 +266,12 @@ gen_method(Class,Ms0) ->
case Res of
[] -> Ms0;
[#method{where=taylormade}|_] ->
- taylormade_func(Class, Res),
+ try
+ taylormade_func(Class, Res)
+ catch error:{badmatch, {error, enoent}} ->
+ gen_doc(Class,Res),
+ gen_method1(Res)
+ end,
Ms0;
Ms ->
gen_doc(Class,Ms),
@@ -832,15 +851,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 +1038,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 +1094,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 +1109,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..81f2a389ab 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -2,7 +2,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
@@ -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']}.
@@ -925,7 +975,7 @@
'AssignImageList','AssignStateImageList','Collapse','CollapseAndReset',
'Create','Delete','DeleteAllItems','DeleteChildren',
{'EditLabel', [{"textCtrlClass", [nowhere]}]}, %'EndEditLabel',
- 'EnsureVisible','Expand','GetBoundingRect',
+ 'EnsureVisible','Expand',{'GetBoundingRect', [{"rect", [out]}]},
'GetChildrenCount','GetCount','GetEditControl',
{'GetFirstChild',[{"cookie", out}]}, {'GetNextChild',[{"cookie", [both]}]},
'GetFirstVisibleItem',{'GetImageList',0},'GetIndent',
@@ -934,9 +984,10 @@
'GetNextSibling','GetNextVisible','GetItemParent',%'GetParent',
'GetPrevSibling','GetPrevVisible','GetRootItem',
'GetSelection',{'GetSelections', [{return, nowhere},{"val",out}]},
- 'GetStateImageList',{'HitTest', 1},
+ 'GetStateImageList', {{'HitTest', 2}, [{"flags", out}]},
{'InsertItem',[{"insertAfter", skip_member}]},
'IsBold','IsExpanded','IsSelected','IsVisible','ItemHasChildren',
+ {'IsTreeItemIdOk', [{where, taylormade}]},
%%'OnCompareItems',
'PrependItem','ScrollTo','SelectItem',
'SetIndent',{'SetImageList',1},'SetItemBackgroundColour',
@@ -1233,7 +1284,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 +1296,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 +1363,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 +1393,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 +1463,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 +1683,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/autoconf/config.guess b/lib/wx/autoconf/config.guess
index ec46d18caf..f475ceb413 100755
--- a/lib/wx/autoconf/config.guess
+++ b/lib/wx/autoconf/config.guess
@@ -1,14 +1,12 @@
#! /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.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2006-02-27'
+timestamp='2013-02-12'
# 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
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@@ -17,26 +15,22 @@ timestamp='2006-02-27'
# 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.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
#
-# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# Please send patches with a ChangeLog entry to [email protected].
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -56,8 +50,7 @@ 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.
+Copyright 1992-2013 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."
@@ -144,7 +137,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
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*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -161,6 +154,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
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
@@ -169,7 +163,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 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
+ | grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -179,7 +173,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
;;
esac
# The OS release
@@ -200,6 +194,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -211,7 +209,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
exit ;;
macppc:MirBSD:*:*)
- echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
exit ;;
*:MirBSD:*:*)
echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
@@ -222,7 +220,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -268,7 +266,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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 ;;
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
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
@@ -294,12 +295,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-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:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -323,14 +324,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
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.*:*)
- echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
@@ -374,23 +394,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -460,8 +480,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ # 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 ] || \
@@ -474,7 +494,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -531,7 +551,7 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[45])
+ *:AIX:*:[4567])
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
@@ -574,52 +594,52 @@ EOF
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" ;;
+ 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
+ esac ;;
+ esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ sed 's/^ //' << EOF >$dummy.c
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
+ #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);
+ 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);
- }
+ 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
@@ -639,7 +659,7 @@ EOF
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
+ grep -q __LP64__
then
HP_ARCH="hppa2.0w"
else
@@ -710,22 +730,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -749,14 +769,14 @@ EOF
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 ;;
+ 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}"
+ 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}
@@ -768,38 +788,51 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
- i*:MINGW*:*)
- echo ${UNAME_MACHINE}-pc-mingw32
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
exit ;;
- i*:MSYS_NT-*:*:*)
+ *:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
+ i*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
exit ;;
i*:PW*:*)
echo ${UNAME_MACHINE}-pc-pw32
exit ;;
- x86:Interix*:[345]*)
- echo i586-pc-interix${UNAME_RELEASE}
- exit ;;
- EM64T:Interix*:[345]*)
- echo x86_64-unknown-interix${UNAME_RELEASE}
- exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-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 ;;
+ 8664:Windows_NT:*)
+ echo x86_64-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
@@ -829,17 +862,68 @@ EOF
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo ${UNAME_MACHINE}-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 -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-gnu
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -850,74 +934,36 @@ EOF
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:*:*)
+ mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
- #undef mips64
- #undef mips64el
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
+ CPU=${UNAME_MACHINE}el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
+ CPU=${UNAME_MACHINE}
#else
CPU=
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
+ or1k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
+ padre:Linux:*:*)
+ echo sparc-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}
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
@@ -927,14 +973,17 @@ EOF
*) echo hppa-unknown-linux-gnu ;;
esac
exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -942,75 +991,18 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-gnu
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-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(__sun)
- 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
@@ -1018,11 +1010,11 @@ EOF
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,
+ # 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.
+ # Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1039,7 +1031,7 @@ EOF
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
@@ -1054,7 +1046,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1082,10 +1074,13 @@ EOF
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 ;;
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1120,8 +1115,18 @@ EOF
/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; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ 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; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
@@ -1134,7 +1139,7 @@ EOF
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
@@ -1154,10 +1159,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- echo i586-unisys-sysv4
- 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
@@ -1183,11 +1188,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv${UNAME_RELEASE}
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1197,6 +1202,12 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1206,6 +1217,15 @@ EOF
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 ;;
@@ -1215,6 +1235,16 @@ EOF
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
unknown) UNAME_PROCESSOR=powerpc ;;
esac
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1230,7 +1260,10 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
@@ -1275,13 +1308,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ 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`
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1296,11 +1329,14 @@ EOF
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ 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_
@@ -1318,11 +1354,11 @@ main ()
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
- "4"
+ "4"
#else
- ""
+ ""
#endif
- ); exit (0);
+ ); exit (0);
#endif
#endif
@@ -1456,9 +1492,9 @@ 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
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
and
- http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
If the version you run ($0) is already up to date, please
send the following data and any information you think might be
diff --git a/lib/wx/autoconf/config.sub b/lib/wx/autoconf/config.sub
index 9772e87d24..bb6edbdb47 100755
--- a/lib/wx/autoconf/config.sub
+++ b/lib/wx/autoconf/config.sub
@@ -1,42 +1,40 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+# Copyright 1992-2013 Free Software Foundation, Inc.
-timestamp='2003-02-22'
+timestamp='2013-02-12'
-# 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
+# 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 3 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.
+# 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., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to <[email protected]>. Submit a context
-# diff and a properly formatted ChangeLog entry.
+
+# Please send patches with a ChangeLog entry to [email protected].
#
# 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.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
# 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.
@@ -70,8 +68,7 @@ Report bugs and patches to <[email protected]>."
version="\
GNU config.sub ($timestamp)
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
-Free Software Foundation, Inc.
+Copyright 1992-2013 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."
@@ -83,11 +80,11 @@ Try \`$me --help' for more information."
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
- echo "$timestamp" ; exit 0 ;;
+ echo "$timestamp" ; exit ;;
--version | -v )
- echo "$version" ; exit 0 ;;
+ echo "$version" ; exit ;;
--help | --h* | -h )
- echo "$usage"; exit 0 ;;
+ echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
@@ -99,7 +96,7 @@ while test $# -gt 0 ; do
*local*)
# First pass through any local machine types.
echo $1
- exit 0;;
+ exit ;;
* )
break ;;
@@ -118,10 +115,18 @@ esac
# 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* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -144,10 +149,13 @@ case $os in
-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)
+ -apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
+ -bluegene*)
+ os=-cnk
+ ;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
@@ -162,13 +170,17 @@ case $os in
os=-chorusos
basic_machine=$1
;;
- -chorusrdb)
- os=-chorusrdb
+ -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/'`
@@ -185,6 +197,10 @@ case $os in
# 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/'`
@@ -202,6 +218,12 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
-lynx*)
os=-lynxos
;;
@@ -226,55 +248,106 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 | aarch64_be \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
- | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
- | clipper \
+ | am33_2.0 \
+ | arc \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | be32 | be64 \
+ | bfin \
+ | c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
- | fr30 | frv \
+ | epiphany \
+ | fido | fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
| i370 | i860 | i960 | ia64 \
- | ip2k \
- | m32r | m68000 | m68k | m88k | mcore \
+ | ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
- | mips64vr | mips64vrel \
+ | mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
| mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
+ | moxie \
+ | mt \
| msp430 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | openrisc | or32 \
+ | open8 \
+ | or1k | or32 \
| pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
- | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | rl78 | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
- | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
- | strongarm \
- | tahoe | thumb | tic80 | tron \
- | v850 | v850e \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| we32k \
- | x86 | xscale | xstormy16 | xtensa \
- | z8k)
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
basic_machine=$basic_machine-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-unknown
+ ;;
# We use `pc' rather than `unknown'
# because (1) that's what they normally are, and
@@ -290,58 +363,82 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* | aarch64_be-* \
| 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-* \
- | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
- | clipper-* | cydra-* \
+ | avr-* | avr32-* \
+ | be32-* | be64-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
+ | clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| elxsi-* \
- | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
- | ip2k-* \
- | m32r-* \
+ | ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | mcore-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
+ | mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
| mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
| msp430-* \
- | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
- | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
- | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
- | tahoe-* | thumb-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
| tron-* \
- | v850-* | v850e-* | vax-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
| we32k-* \
- | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
- | xtensa-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
| ymp-* \
- | z8k-*)
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -359,6 +456,9 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
adobe68k)
basic_machine=m68010-adobe
os=-scout
@@ -373,6 +473,12 @@ case $basic_machine in
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
@@ -396,6 +502,10 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-bsd
;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -404,10 +514,35 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -432,12 +567,27 @@ case $basic_machine in
basic_machine=j90-cray
os=-unicos
;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16 | cr16-*)
+ basic_machine=cr16-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
;;
@@ -460,6 +610,14 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
dpx20 | dpx20-*)
basic_machine=rs6000-bull
os=-bosx
@@ -571,7 +729,6 @@ case $basic_machine in
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
@@ -610,6 +767,14 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
m88k-omron*)
basic_machine=m88k-omron
;;
@@ -621,10 +786,21 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
mingw32)
basic_machine=i386-pc
os=-mingw32
;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
miniframe)
basic_machine=m68000-convergent
;;
@@ -638,10 +814,6 @@ case $basic_machine in
mips3*)
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
;;
- mmix*)
- basic_machine=mmix-knuth
- os=-mmixware
- ;;
monitor)
basic_machine=m68k-rom68k
os=-coff
@@ -654,10 +826,21 @@ case $basic_machine in
basic_machine=i386-pc
os=-msdos
;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -722,9 +905,11 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
- nv1)
- basic_machine=nv1-cray
- os=-unicosmp
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
;;
nsr-tandem)
basic_machine=nsr-tandem
@@ -733,9 +918,12 @@ case $basic_machine in
basic_machine=hppa1.1-oki
os=-proelf
;;
- or32 | or32-*)
+ openrisc | openrisc-*)
basic_machine=or32-unknown
- os=-coff
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
@@ -753,6 +941,14 @@ case $basic_machine in
basic_machine=i860-intel
os=-osf
;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
pbd)
basic_machine=sparc-tti
;;
@@ -762,32 +958,45 @@ case $basic_machine in
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)
+ 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-*)
+ 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 | ppcbe) basic_machine=powerpc-unknown
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
@@ -812,6 +1021,14 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
rom68k)
basic_machine=m68k-rom68k
os=-coff
@@ -838,6 +1055,14 @@ case $basic_machine in
sb1el)
basic_machine=mipsisa64sb1el-unknown
;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
sequent)
basic_machine=i386-sequent
;;
@@ -845,6 +1070,12 @@ case $basic_machine in
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
@@ -863,6 +1094,9 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sun2)
basic_machine=m68000-sun
;;
@@ -919,21 +1153,9 @@ case $basic_machine in
basic_machine=t90-cray
os=-unicos
;;
- tic4x | c4x*)
- basic_machine=tic4x-unknown
- os=-coff
- ;;
- tic54x | c54x*)
- basic_machine=tic54x-unknown
- os=-coff
- ;;
- tic55x | c55x*)
- basic_machine=tic55x-unknown
- os=-coff
- ;;
- tic6x | c6x*)
- basic_machine=tic6x-unknown
- os=-coff
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
;;
tx39)
basic_machine=mipstx39-unknown
@@ -948,6 +1170,10 @@ case $basic_machine in
tower | tower-32)
basic_machine=m68k-ncr
;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
udi29k)
basic_machine=a29k-amd
os=-udi
@@ -991,9 +1217,16 @@ case $basic_machine in
basic_machine=hppa1.1-winbond
os=-proelf
;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
xps | xps100)
basic_machine=xps100-honeywell
;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1002,6 +1235,10 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
none)
basic_machine=none-none
os=-none
@@ -1021,6 +1258,9 @@ case $basic_machine in
romp)
basic_machine=romp-ibm
;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
rs6000)
basic_machine=rs6000-ibm
;;
@@ -1037,13 +1277,10 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
- sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
- sh64)
- basic_machine=sh64-unknown
- ;;
- sparc | sparcv9 | sparcv9b)
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
basic_machine=sparc-sun
;;
cydra)
@@ -1087,9 +1324,12 @@ esac
if [ x"$os" != x"" ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
@@ -1110,25 +1350,31 @@ case $os in
# 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* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
+ | -aos* | -aros* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
- | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
- | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -bitrig* | -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* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -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*)
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1146,12 +1392,15 @@ case $os in
os=`echo $os | sed -e 's|nto|nto-qnx|'`
;;
-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
- | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -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|'`
;;
@@ -1164,6 +1413,9 @@ case $os in
-opened*)
os=-openedition
;;
+ -os400*)
+ os=-os400
+ ;;
-wince*)
os=-wince
;;
@@ -1185,6 +1437,9 @@ case $os in
-atheos*)
os=-atheos
;;
+ -syllable*)
+ os=-syllable
+ ;;
-386bsd)
os=-bsd
;;
@@ -1207,6 +1462,9 @@ case $os in
-sinix*)
os=-sysv4
;;
+ -tpf*)
+ os=-tpf
+ ;;
-triton*)
os=-sysv3
;;
@@ -1240,8 +1498,13 @@ case $os in
-aros*)
os=-aros
;;
- -kaos*)
- os=-kaos
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
;;
-none)
;;
@@ -1265,6 +1528,12 @@ else
# system, and we'll never get to this point.
case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
*-acorn)
os=-riscix1.2
;;
@@ -1274,6 +1543,21 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
+ ;;
# This must come before the *-dec entry.
pdp10-*)
os=-tops20
@@ -1292,19 +1576,22 @@ case $basic_machine in
;;
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
;;
+ or1k-*)
+ os=-elf
+ ;;
or32-*)
os=-coff
;;
@@ -1317,9 +1604,15 @@ case $basic_machine in
*-be)
os=-beos
;;
+ *-haiku)
+ os=-haiku
+ ;;
*-ibm)
os=-aix
;;
+ *-knuth)
+ os=-mmixware
+ ;;
*-wec)
os=-proelf
;;
@@ -1422,7 +1715,7 @@ case $basic_machine in
-sunos*)
vendor=sun
;;
- -aix*)
+ -cnk*|-aix*)
vendor=ibm
;;
-beos*)
@@ -1452,9 +1745,15 @@ case $basic_machine in
-mvs* | -opened*)
vendor=ibm
;;
+ -os400*)
+ vendor=ibm
+ ;;
-ptx*)
vendor=sequent
;;
+ -tpf*)
+ vendor=ibm
+ ;;
-vxsim* | -vxworks* | -windiss*)
vendor=wrs
;;
@@ -1479,7 +1778,7 @@ case $basic_machine in
esac
echo $basic_machine$os
-exit 0
+exit
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
@@ -1487,3 +1786,4 @@ exit 0
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
+
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/egl_impl.cpp b/lib/wx/c_src/egl_impl.cpp
index 1379f07523..a55a145934 100644
--- a/lib/wx/c_src/egl_impl.cpp
+++ b/lib/wx/c_src/egl_impl.cpp
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2011. All Rights Reserved.
+ * Copyright Ericsson AB 2011-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
@@ -169,33 +169,34 @@ void gl_error() {
* ******************************************************************************/
static GLUtesselator* tess;
-static GLdouble* tess_coords;
-static GLdouble* tess_alloc_vertex;
-static int* tess_vertices;
-void CALLBACK
-egl_ogla_vertex(GLdouble* coords)
-{
- /* fprintf(stderr, "%d\r\n", (int) (coords - tess_coords) / 3); */
+typedef struct {
+ GLdouble * tess_coords;
+ int alloc_n;
+ int alloc_max;
- *tess_vertices++ = (int) (coords - tess_coords) / 3;
-}
+ int * tess_index_list;
+ int index_n;
+ int index_max;
-void CALLBACK
-egl_ogla_edge_flag(GLboolean flag)
-{
-}
+ int error;
+} egl_tess_data;
+
+#define NEED_MORE_ALLOC 1
+#define NEED_MORE_INDEX 2
+
+static egl_tess_data egl_tess;
void CALLBACK
-egl_ogla_error(GLenum errorCode)
+egl_ogla_vertex(GLdouble* coords)
{
- const GLubyte *err;
- err = gluErrorString(errorCode);
- // wxString msg;
- // msg.Printf(wxT("Tesselation error: %d: "), (int)errorCode);
- // msg += wxString::FromAscii((char *) err);
- // send_msg("error", &msg);
- fprintf(stderr, "Tesselation error: %d: %s\r\n", (int) errorCode, err);
+ /* fprintf(stderr, "%d\r\n", (int) (coords - tess_coords) / 3); */
+ if(egl_tess.index_n < egl_tess.index_max) {
+ egl_tess.tess_index_list[egl_tess.index_n] = (int) (coords - egl_tess.tess_coords) / 3;
+ egl_tess.index_n++;
+ }
+ else
+ egl_tess.error = NEED_MORE_INDEX;
}
void CALLBACK
@@ -204,26 +205,43 @@ egl_ogla_combine(GLdouble coords[3],
GLfloat w[4],
void **dataOut)
{
- GLdouble* vertex = tess_alloc_vertex;
-
- tess_alloc_vertex += 3;
+ GLdouble* vertex = &egl_tess.tess_coords[egl_tess.alloc_n];
+ if(egl_tess.alloc_n < egl_tess.alloc_max) {
+ egl_tess.alloc_n += 3;
+ vertex[0] = coords[0];
+ vertex[1] = coords[1];
+ vertex[2] = coords[2];
+ *dataOut = vertex;
#if 0
- fprintf(stderr, "combine: ");
- int i;
- for (i = 0; i < 4; i++) {
- if (w[i] > 0.0) {
- fprintf(stderr, "%d(%g) ", (int) vertex_data[i], w[i]);
+ fprintf(stderr, "combine: ");
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (w[i] > 0.0) {
+ fprintf(stderr, "%d(%g) ", (int) vertex_data[i], w[i]);
+ }
}
- }
- fprintf(stderr, "\r\n");
- fprintf(stderr, "%g %g %g\r\n", vertex[0], vertex[1], vertex[2]);
+ fprintf(stderr, "\r\n");
+ fprintf(stderr, "%g %g %g\r\n", vertex[0], vertex[1], vertex[2]);
#endif
- vertex[0] = coords[0];
- vertex[1] = coords[1];
- vertex[2] = coords[2];
- *dataOut = vertex;
+ } else {
+ egl_tess.error = NEED_MORE_ALLOC;
+ *dataOut = NULL;
+ }
+}
+
+void CALLBACK
+egl_ogla_edge_flag(GLboolean flag)
+{
+}
+
+void CALLBACK
+egl_ogla_error(GLenum errorCode)
+{
+ // const GLubyte *err;
+ // err = gluErrorString(errorCode);
+ // fprintf(stderr, "Tesselation error: %d: %s\r\n", (int) errorCode, err);
}
void init_tess()
@@ -246,49 +264,46 @@ int erl_tess_impl(char* buff, ErlDrvPort port, ErlDrvTermData caller)
{
ErlDrvBinary* bin;
int i;
- GLdouble* new_vertices;
- int *vertices;
int num_vertices;
GLdouble *n;
- int n_pos, AP;
-
+ int AP;
+ int a_max = 2;
+ int i_max = 6;
num_vertices = * (int *) buff; buff += 8; /* Align */
n = (double *) buff; buff += 8*3;
- bin = driver_alloc_binary(num_vertices*6*sizeof(GLdouble));
- new_vertices = tess_coords = (double *) bin->orig_bytes;
- memcpy(tess_coords,buff,num_vertices*3*sizeof(GLdouble));
- tess_alloc_vertex = tess_coords + num_vertices*3;
+ egl_tess.alloc_max = a_max*num_vertices*3;
+ bin = driver_alloc_binary(egl_tess.alloc_max*sizeof(GLdouble));
+ egl_tess.error = 0;
+ egl_tess.tess_coords = (double *) bin->orig_bytes;
+ memcpy(egl_tess.tess_coords,buff,num_vertices*3*sizeof(GLdouble));
+ egl_tess.index_max = i_max*3*num_vertices;
+ egl_tess.tess_index_list = (int *) driver_alloc(sizeof(int) * egl_tess.index_max);
-#if 0
- fprintf(stderr, "n=%d\r\n", num_vertices);
-#endif
- vertices = (int *) driver_alloc(sizeof(int) * 16*num_vertices);
-
- tess_vertices = vertices;
+ egl_tess.tess_coords = (double *) bin->orig_bytes;
+ egl_tess.index_n = 0;
+ egl_tess.alloc_n = num_vertices*3;
gluTessNormal(tess, n[0], n[1], n[2]);
gluTessBeginPolygon(tess, 0);
gluTessBeginContour(tess);
for (i = 0; i < num_vertices; i++) {
- gluTessVertex(tess, tess_coords+3*i, tess_coords+3*i);
+ gluTessVertex(tess, egl_tess.tess_coords+3*i, egl_tess.tess_coords+3*i);
}
gluTessEndContour(tess);
gluTessEndPolygon(tess);
- n_pos = (tess_vertices - vertices);
-
AP = 0; ErlDrvTermData *rt;
- rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData) * (13+n_pos*2));
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData) * (13+egl_tess.index_n*2));
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
- for(i=0; i < n_pos; i++) {
- rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) vertices[i];
+ for(i=0; i < egl_tess.index_n; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) egl_tess.tess_index_list[i];
};
- rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = n_pos+1;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = egl_tess.index_n+1;
rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) bin;
- rt[AP++] = (tess_alloc_vertex-new_vertices)*sizeof(GLdouble); rt[AP++] = 0;
+ rt[AP++] = egl_tess.alloc_n*sizeof(GLdouble); rt[AP++] = 0;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Return tuple {list, Bin}
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Result tuple
@@ -300,7 +315,7 @@ int erl_tess_impl(char* buff, ErlDrvPort port, ErlDrvTermData caller)
/* (tess_alloc_vertex-new_vertices)*sizeof(GLdouble), */
/* num_vertices*6*sizeof(GLdouble)); */
driver_free_binary(bin);
- driver_free(vertices);
+ driver_free(egl_tess.tess_index_list);
driver_free(rt);
return 0;
}
diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h
index 6b7a5378cb..8dcaf1c1ac 100644
--- a/lib/wx/c_src/gen/wxe_derived_dest.h
+++ b/lib/wx/c_src/gen/wxe_derived_dest.h
@@ -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
@@ -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() {};
};
@@ -383,7 +391,7 @@ class EwxListCtrl : public wxListCtrl {
int onGetItemText;
int onGetItemAttr;
int onGetItemColumnImage;
- ErlDrvPort port;
+ ErlDrvTermData port;
private:
virtual wxString OnGetItemText(long item, long col) const;
@@ -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..c9cdee3160 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -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
@@ -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++) {
@@ -342,7 +328,7 @@ int getRef(void* ptr, wxeMemEnv* memenv)
return app->getRef(ptr,memenv);
}
-bool sendevent(wxEvent *event, ErlDrvPort port)
+bool sendevent(wxEvent *event, ErlDrvTermData port)
{
int send_res ;
char * evClass = NULL;
@@ -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);
@@ -832,7 +815,7 @@ case 227: {// wxTaskBarIconEvent
rt.addTupleCount(3);
pre_callback();
send_res = rt.send();
- if(send_res) handle_event_callback(port, cb->listener);
+ if(send_res) handle_event_callback(WXE_DRV_PORT_HANDLE, cb->listener);
app->clearPtr((void *) event);
} else {
send_res = rt.send();
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index 15012011ed..2d55f34346 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -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
@@ -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;
}
@@ -18650,15 +18687,11 @@ case wxTreeCtrl_Expand: { // wxTreeCtrl::Expand
break;
}
case wxTreeCtrl_GetBoundingRect: { // wxTreeCtrl::GetBoundingRect
+ wxRect rect;
bool textOnly=false;
wxTreeCtrl *This = (wxTreeCtrl *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
wxTreeItemId item = wxTreeItemId((void *) *(wxUint64 *) bp); bp += 8;
- int * rectX = (int *) bp; bp += 4;
- int * rectY = (int *) bp; bp += 4;
- int * rectW = (int *) bp; bp += 4;
- int * rectH = (int *) bp; bp += 4;
- wxRect rect = wxRect(*rectX,*rectY,*rectW,*rectH);
while( * (int*) bp) { switch (* (int*) bp) {
case 1: {bp += 4;
textOnly = *(bool *) bp; bp += 4;
@@ -18667,6 +18700,8 @@ case wxTreeCtrl_GetBoundingRect: { // wxTreeCtrl::GetBoundingRect
if(!This) throw wxe_badarg(0);
bool Result = This->GetBoundingRect(item,rect,textOnly);
rt.addBool(Result);
+ rt.add(rect);
+ rt.addTupleCount(2);
break;
}
case wxTreeCtrl_GetChildrenCount: { // wxTreeCtrl::GetChildrenCount
@@ -18790,7 +18825,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;
}
@@ -18900,13 +18935,16 @@ case wxTreeCtrl_GetStateImageList: { // wxTreeCtrl::GetStateImageList
break;
}
case wxTreeCtrl_HitTest: { // wxTreeCtrl::HitTest
+ int flags;
wxTreeCtrl *This = (wxTreeCtrl *) getPtr(bp,memenv); bp += 4;
int * pointX = (int *) bp; bp += 4;
int * pointY = (int *) bp; bp += 4;
wxPoint point = wxPoint(*pointX,*pointY);
if(!This) throw wxe_badarg(0);
- wxTreeItemId Result = This->HitTest(point);
+ wxTreeItemId Result = This->HitTest(point,flags);
rt.add((wxUIntPtr *) Result.m_pItem);
+ rt.addInt(flags);
+ rt.addTupleCount(2);
break;
}
case wxTreeCtrl_InsertItem: { // wxTreeCtrl::InsertItem
@@ -18933,7 +18971,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;
}
@@ -18982,6 +19020,13 @@ case wxTreeCtrl_ItemHasChildren: { // wxTreeCtrl::ItemHasChildren
rt.addBool(Result);
break;
}
+
+case wxTreeCtrl_IsTreeItemIdOk: { // wxTreeCtrl::IsTreeItemIdOk
+ wxTreeItemId item = wxTreeItemId((void *) *(wxUint64 *) bp); bp += 8;
+ bool Result = item.IsOk();
+ rt.addBool(Result);
+ break;
+}
case wxTreeCtrl_PrependItem: { // wxTreeCtrl::PrependItem
int image=-1;
int selectedImage=-1;
@@ -19043,7 +19088,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 +19177,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 +19193,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 +19231,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 +19311,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 +19346,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 +19382,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 +19399,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 +19498,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 +19627,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 +19642,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 +19650,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 +19699,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 +19732,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 +19756,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 +19790,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 +19821,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 +19875,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 +19918,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 +19932,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 +19961,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 +19969,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 +19982,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 +20011,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 +20019,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 +20044,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 +20052,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 +20063,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 +20073,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 +20112,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 +20149,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 +20164,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 +20198,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 +20241,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 +20291,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 +20306,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 +20349,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 +20388,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 +20396,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 +20404,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 +20412,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 +20440,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 +20543,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 +20551,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 +20574,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 +20615,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 +20626,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 +20845,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 +20879,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 +20893,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 +21022,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 +21075,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 +21140,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 +21187,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 +21249,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 +21293,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 +21369,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 +21472,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 +21511,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 +21540,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 +21599,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 +21629,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 +21770,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 +21876,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 +21925,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 +21958,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 +22042,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 +22133,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 +22244,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 +22273,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 +22321,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 +22337,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 +22409,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 +22514,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 +22556,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 +22631,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 +22660,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 +22718,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 +22817,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 +22825,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 +22840,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 +22862,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 +22949,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 +22978,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 +23134,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 +23328,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 +23856,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 +23932,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 +24034,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 +24102,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 +24121,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 +24178,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 +24319,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 +24359,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 +24392,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 +24466,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 +24509,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 +24525,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 +24676,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 +24691,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 +24699,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 +24722,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 +24751,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 +24759,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 +24783,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 +24794,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 +24802,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 +24810,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 +24859,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 +24916,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 +24944,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 +24979,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 +25018,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 +25047,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 +25062,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 +25162,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 +25245,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 +25296,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 +25760,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 +25787,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 +25912,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 +26001,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 +26015,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 +26043,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 +26064,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 +26078,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 +26092,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 +26127,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 +26155,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 +26190,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 +26578,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 +26598,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 +26655,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 +26708,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 +26911,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 +26919,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 +27015,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 +27041,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 +27063,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 +27077,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 +27097,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 +27112,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 +27120,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 +27141,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 +27159,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 +27167,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 +27205,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 +27219,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 +27227,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 +27235,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 +27249,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 +27270,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 +27299,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 +27311,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 +27323,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 +27331,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 +27340,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 +27363,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 +27372,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 +27381,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 +27389,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 +27397,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 +27405,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 +27421,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 +27437,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 +27453,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 +27479,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 +27491,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 +27499,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 +27507,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 +27515,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 +27525,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 +27533,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 +27547,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 +27555,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 +27563,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 +27575,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 +27587,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 +27601,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 +27621,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 +27629,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 +27643,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 +27652,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 +27666,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 +27695,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 +27715,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 +27735,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 +27747,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 +27762,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 +27791,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 +27819,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 +27861,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 +27884,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 +27907,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 +27921,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 +27938,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 +27959,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 +27974,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 +27994,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 +28015,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 +28029,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 +28043,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 +28058,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 +28073,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 +28081,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 +28089,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 +28103,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 +28117,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 +28131,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 +28160,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 +28181,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 +28195,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 +28209,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 +28233,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 +28255,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 +28270,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 +28285,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 +28299,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 +28321,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 +28336,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 +28344,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 +28359,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 +28368,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 +28390,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 +28477,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 +28491,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 +28505,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 +28539,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 +28556,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 +28584,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 +28624,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 +28639,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 +28647,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 +28656,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 +28672,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 +28680,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 +28689,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 +28697,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 +28713,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 +28728,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 +28770,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 +28784,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 +28799,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 +28808,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 +28816,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 +28830,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 +28844,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 +28858,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 +28872,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 +28886,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 +28903,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 +28918,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 +28926,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 +28956,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 +28975,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 +28987,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 +28999,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 +29348,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 +29357,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 +29386,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 +29407,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 +29455,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 +29466,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 +29481,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 +29495,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 +29516,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 +29530,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 +29544,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 +29558,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 +29572,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 +29611,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 +29644,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 +29652,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 +29671,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 +29683,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 +29722,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 +29730,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 +29739,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 +29749,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 +29894,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 +29902,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 +29917,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 +29930,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 +29950,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 +29976,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 +29991,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 +30013,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 +30056,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 +30064,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 +30085,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 +30093,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 +30101,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 +30116,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 +30134,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 +30142,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 +30177,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 +30206,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 +30218,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 +30241,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 +30260,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 +30280,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 +30431,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 +30479,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 +30515,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 +30527,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 +30629,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 +30651,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 +30794,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 +30807,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 +30915,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 +31084,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 +31129,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 +31148,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 +31167,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 +31223,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 +31244,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 +31300,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 +31348,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 +31360,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 +31447,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..c341825d8d 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -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
@@ -1931,1277 +1931,1277 @@
#define wxTreeCtrl_IsSelected 2058
#define wxTreeCtrl_IsVisible 2059
#define wxTreeCtrl_ItemHasChildren 2060
-#define wxTreeCtrl_PrependItem 2061
-#define wxTreeCtrl_ScrollTo 2062
-#define wxTreeCtrl_SelectItem_1 2063
-#define wxTreeCtrl_SelectItem_2 2064
-#define wxTreeCtrl_SetIndent 2065
-#define wxTreeCtrl_SetImageList 2066
-#define wxTreeCtrl_SetItemBackgroundColour 2067
-#define wxTreeCtrl_SetItemBold 2068
-#define wxTreeCtrl_SetItemData 2069
-#define wxTreeCtrl_SetItemDropHighlight 2070
-#define wxTreeCtrl_SetItemFont 2071
-#define wxTreeCtrl_SetItemHasChildren 2072
-#define wxTreeCtrl_SetItemImage_2 2073
-#define wxTreeCtrl_SetItemImage_3 2074
-#define wxTreeCtrl_SetItemText 2075
-#define wxTreeCtrl_SetItemTextColour 2076
-#define wxTreeCtrl_SetStateImageList 2077
-#define wxTreeCtrl_SetWindowStyle 2078
-#define wxTreeCtrl_SortChildren 2079
-#define wxTreeCtrl_Toggle 2080
-#define wxTreeCtrl_ToggleItemSelection 2081
-#define wxTreeCtrl_Unselect 2082
-#define wxTreeCtrl_UnselectAll 2083
-#define wxTreeCtrl_UnselectItem 2084
-#define wxScrollBar_new_0 2085
-#define wxScrollBar_new_3 2086
-#define wxScrollBar_destruct 2087
-#define wxScrollBar_Create 2088
-#define wxScrollBar_GetRange 2089
-#define wxScrollBar_GetPageSize 2090
-#define wxScrollBar_GetThumbPosition 2091
-#define wxScrollBar_GetThumbSize 2092
-#define wxScrollBar_SetThumbPosition 2093
-#define wxScrollBar_SetScrollbar 2094
-#define wxSpinButton_new_2 2096
-#define wxSpinButton_new_0 2097
-#define wxSpinButton_Create 2098
-#define wxSpinButton_GetMax 2099
-#define wxSpinButton_GetMin 2100
-#define wxSpinButton_GetValue 2101
-#define wxSpinButton_SetRange 2102
-#define wxSpinButton_SetValue 2103
-#define wxSpinButton_destroy 2104
-#define wxSpinCtrl_new_0 2105
-#define wxSpinCtrl_new_2 2106
-#define wxSpinCtrl_Create 2108
-#define wxSpinCtrl_SetValue_1_1 2111
-#define wxSpinCtrl_SetValue_1_0 2112
-#define wxSpinCtrl_GetValue 2114
-#define wxSpinCtrl_SetRange 2116
-#define wxSpinCtrl_SetSelection 2117
-#define wxSpinCtrl_GetMin 2119
-#define wxSpinCtrl_GetMax 2121
-#define wxSpinCtrl_destroy 2122
-#define wxStaticText_new_0 2123
-#define wxStaticText_new_4 2124
-#define wxStaticText_Create 2125
-#define wxStaticText_GetLabel 2126
-#define wxStaticText_SetLabel 2127
-#define wxStaticText_Wrap 2128
-#define wxStaticText_destroy 2129
-#define wxStaticBitmap_new_0 2130
-#define wxStaticBitmap_new_4 2131
-#define wxStaticBitmap_Create 2132
-#define wxStaticBitmap_GetBitmap 2133
-#define wxStaticBitmap_SetBitmap 2134
-#define wxStaticBitmap_destroy 2135
-#define wxRadioBox_new 2136
-#define wxRadioBox_destruct 2138
-#define wxRadioBox_Create 2139
-#define wxRadioBox_Enable_2 2140
-#define wxRadioBox_Enable_1 2141
-#define wxRadioBox_GetSelection 2142
-#define wxRadioBox_GetString 2143
-#define wxRadioBox_SetSelection 2144
-#define wxRadioBox_Show_2 2145
-#define wxRadioBox_Show_1 2146
-#define wxRadioBox_GetColumnCount 2147
-#define wxRadioBox_GetItemHelpText 2148
-#define wxRadioBox_GetItemToolTip 2149
-#define wxRadioBox_GetItemFromPoint 2151
-#define wxRadioBox_GetRowCount 2152
-#define wxRadioBox_IsItemEnabled 2153
-#define wxRadioBox_IsItemShown 2154
-#define wxRadioBox_SetItemHelpText 2155
-#define wxRadioBox_SetItemToolTip 2156
-#define wxRadioButton_new_0 2157
-#define wxRadioButton_new_4 2158
-#define wxRadioButton_Create 2159
-#define wxRadioButton_GetValue 2160
-#define wxRadioButton_SetValue 2161
-#define wxRadioButton_destroy 2162
-#define wxSlider_new_6 2164
-#define wxSlider_new_0 2165
-#define wxSlider_Create 2166
-#define wxSlider_GetLineSize 2167
-#define wxSlider_GetMax 2168
-#define wxSlider_GetMin 2169
-#define wxSlider_GetPageSize 2170
-#define wxSlider_GetThumbLength 2171
-#define wxSlider_GetValue 2172
-#define wxSlider_SetLineSize 2173
-#define wxSlider_SetPageSize 2174
-#define wxSlider_SetRange 2175
-#define wxSlider_SetThumbLength 2176
-#define wxSlider_SetValue 2177
-#define wxSlider_destroy 2178
-#define wxDialog_new_4 2180
-#define wxDialog_new_0 2181
-#define wxDialog_destruct 2183
-#define wxDialog_Create 2184
-#define wxDialog_CreateButtonSizer 2185
-#define wxDialog_CreateStdDialogButtonSizer 2186
-#define wxDialog_EndModal 2187
-#define wxDialog_GetAffirmativeId 2188
-#define wxDialog_GetReturnCode 2189
-#define wxDialog_IsModal 2190
-#define wxDialog_SetAffirmativeId 2191
-#define wxDialog_SetReturnCode 2192
-#define wxDialog_Show 2193
-#define wxDialog_ShowModal 2194
-#define wxColourDialog_new_0 2195
-#define wxColourDialog_new_2 2196
-#define wxColourDialog_destruct 2197
-#define wxColourDialog_Create 2198
-#define wxColourDialog_GetColourData 2199
-#define wxColourData_new_0 2200
-#define wxColourData_new_1 2201
-#define wxColourData_destruct 2202
-#define wxColourData_GetChooseFull 2203
-#define wxColourData_GetColour 2204
-#define wxColourData_GetCustomColour 2206
-#define wxColourData_SetChooseFull 2207
-#define wxColourData_SetColour 2208
-#define wxColourData_SetCustomColour 2209
-#define wxPalette_new_0 2210
-#define wxPalette_new_4 2211
-#define wxPalette_destruct 2213
-#define wxPalette_Create 2214
-#define wxPalette_GetColoursCount 2215
-#define wxPalette_GetPixel 2216
-#define wxPalette_GetRGB 2217
-#define wxPalette_IsOk 2218
-#define wxDirDialog_new 2222
-#define wxDirDialog_destruct 2223
-#define wxDirDialog_GetPath 2224
-#define wxDirDialog_GetMessage 2225
-#define wxDirDialog_SetMessage 2226
-#define wxDirDialog_SetPath 2227
-#define wxFileDialog_new 2231
-#define wxFileDialog_destruct 2232
-#define wxFileDialog_GetDirectory 2233
-#define wxFileDialog_GetFilename 2234
-#define wxFileDialog_GetFilenames 2235
-#define wxFileDialog_GetFilterIndex 2236
-#define wxFileDialog_GetMessage 2237
-#define wxFileDialog_GetPath 2238
-#define wxFileDialog_GetPaths 2239
-#define wxFileDialog_GetWildcard 2240
-#define wxFileDialog_SetDirectory 2241
-#define wxFileDialog_SetFilename 2242
-#define wxFileDialog_SetFilterIndex 2243
-#define wxFileDialog_SetMessage 2244
-#define wxFileDialog_SetPath 2245
-#define wxFileDialog_SetWildcard 2246
-#define wxPickerBase_SetInternalMargin 2247
-#define wxPickerBase_GetInternalMargin 2248
-#define wxPickerBase_SetTextCtrlProportion 2249
-#define wxPickerBase_SetPickerCtrlProportion 2250
-#define wxPickerBase_GetTextCtrlProportion 2251
-#define wxPickerBase_GetPickerCtrlProportion 2252
-#define wxPickerBase_HasTextCtrl 2253
-#define wxPickerBase_GetTextCtrl 2254
-#define wxPickerBase_IsTextCtrlGrowable 2255
-#define wxPickerBase_SetPickerCtrlGrowable 2256
-#define wxPickerBase_SetTextCtrlGrowable 2257
-#define wxPickerBase_IsPickerCtrlGrowable 2258
-#define wxFilePickerCtrl_new_0 2259
-#define wxFilePickerCtrl_new_3 2260
-#define wxFilePickerCtrl_Create 2261
-#define wxFilePickerCtrl_GetPath 2262
-#define wxFilePickerCtrl_SetPath 2263
-#define wxFilePickerCtrl_destroy 2264
-#define wxDirPickerCtrl_new_0 2265
-#define wxDirPickerCtrl_new_3 2266
-#define wxDirPickerCtrl_Create 2267
-#define wxDirPickerCtrl_GetPath 2268
-#define wxDirPickerCtrl_SetPath 2269
-#define wxDirPickerCtrl_destroy 2270
-#define wxColourPickerCtrl_new_0 2271
-#define wxColourPickerCtrl_new_3 2272
-#define wxColourPickerCtrl_Create 2273
-#define wxColourPickerCtrl_GetColour 2274
-#define wxColourPickerCtrl_SetColour_1_1 2275
-#define wxColourPickerCtrl_SetColour_1_0 2276
-#define wxColourPickerCtrl_destroy 2277
-#define wxDatePickerCtrl_new_0 2278
-#define wxDatePickerCtrl_new_3 2279
-#define wxDatePickerCtrl_GetRange 2280
-#define wxDatePickerCtrl_GetValue 2281
-#define wxDatePickerCtrl_SetRange 2282
-#define wxDatePickerCtrl_SetValue 2283
-#define wxDatePickerCtrl_destroy 2284
-#define wxFontPickerCtrl_new_0 2285
-#define wxFontPickerCtrl_new_3 2286
-#define wxFontPickerCtrl_Create 2287
-#define wxFontPickerCtrl_GetSelectedFont 2288
-#define wxFontPickerCtrl_SetSelectedFont 2289
-#define wxFontPickerCtrl_GetMaxPointSize 2290
-#define wxFontPickerCtrl_SetMaxPointSize 2291
-#define wxFontPickerCtrl_destroy 2292
-#define wxFindReplaceDialog_new_0 2295
-#define wxFindReplaceDialog_new_4 2296
-#define wxFindReplaceDialog_destruct 2297
-#define wxFindReplaceDialog_Create 2298
-#define wxFindReplaceDialog_GetData 2299
-#define wxFindReplaceData_new_0 2300
-#define wxFindReplaceData_new_1 2301
-#define wxFindReplaceData_GetFindString 2302
-#define wxFindReplaceData_GetReplaceString 2303
-#define wxFindReplaceData_GetFlags 2304
-#define wxFindReplaceData_SetFlags 2305
-#define wxFindReplaceData_SetFindString 2306
-#define wxFindReplaceData_SetReplaceString 2307
-#define wxFindReplaceData_destroy 2308
-#define wxMultiChoiceDialog_new_0 2309
-#define wxMultiChoiceDialog_new_5 2311
-#define wxMultiChoiceDialog_GetSelections 2312
-#define wxMultiChoiceDialog_SetSelections 2313
-#define wxMultiChoiceDialog_destroy 2314
-#define wxSingleChoiceDialog_new_0 2315
-#define wxSingleChoiceDialog_new_5 2317
-#define wxSingleChoiceDialog_GetSelection 2318
-#define wxSingleChoiceDialog_GetStringSelection 2319
-#define wxSingleChoiceDialog_SetSelection 2320
-#define wxSingleChoiceDialog_destroy 2321
-#define wxTextEntryDialog_new 2322
-#define wxTextEntryDialog_GetValue 2323
-#define wxTextEntryDialog_SetValue 2324
-#define wxTextEntryDialog_destroy 2325
-#define wxPasswordEntryDialog_new 2326
-#define wxPasswordEntryDialog_destroy 2327
-#define wxFontData_new_0 2328
-#define wxFontData_new_1 2329
-#define wxFontData_destruct 2330
-#define wxFontData_EnableEffects 2331
-#define wxFontData_GetAllowSymbols 2332
-#define wxFontData_GetColour 2333
-#define wxFontData_GetChosenFont 2334
-#define wxFontData_GetEnableEffects 2335
-#define wxFontData_GetInitialFont 2336
-#define wxFontData_GetShowHelp 2337
-#define wxFontData_SetAllowSymbols 2338
-#define wxFontData_SetChosenFont 2339
-#define wxFontData_SetColour 2340
-#define wxFontData_SetInitialFont 2341
-#define wxFontData_SetRange 2342
-#define wxFontData_SetShowHelp 2343
-#define wxFontDialog_new_0 2347
-#define wxFontDialog_new_2 2349
-#define wxFontDialog_Create 2351
-#define wxFontDialog_GetFontData 2352
-#define wxFontDialog_destroy 2354
-#define wxProgressDialog_new 2355
-#define wxProgressDialog_destruct 2356
-#define wxProgressDialog_Resume 2357
-#define wxProgressDialog_Update_2 2358
-#define wxProgressDialog_Update_0 2359
-#define wxMessageDialog_new 2360
-#define wxMessageDialog_destruct 2361
-#define wxPageSetupDialog_new 2362
-#define wxPageSetupDialog_destruct 2363
-#define wxPageSetupDialog_GetPageSetupData 2364
-#define wxPageSetupDialog_ShowModal 2365
-#define wxPageSetupDialogData_new_0 2366
-#define wxPageSetupDialogData_new_1_0 2367
-#define wxPageSetupDialogData_new_1_1 2368
-#define wxPageSetupDialogData_destruct 2369
-#define wxPageSetupDialogData_EnableHelp 2370
-#define wxPageSetupDialogData_EnableMargins 2371
-#define wxPageSetupDialogData_EnableOrientation 2372
-#define wxPageSetupDialogData_EnablePaper 2373
-#define wxPageSetupDialogData_EnablePrinter 2374
-#define wxPageSetupDialogData_GetDefaultMinMargins 2375
-#define wxPageSetupDialogData_GetEnableMargins 2376
-#define wxPageSetupDialogData_GetEnableOrientation 2377
-#define wxPageSetupDialogData_GetEnablePaper 2378
-#define wxPageSetupDialogData_GetEnablePrinter 2379
-#define wxPageSetupDialogData_GetEnableHelp 2380
-#define wxPageSetupDialogData_GetDefaultInfo 2381
-#define wxPageSetupDialogData_GetMarginTopLeft 2382
-#define wxPageSetupDialogData_GetMarginBottomRight 2383
-#define wxPageSetupDialogData_GetMinMarginTopLeft 2384
-#define wxPageSetupDialogData_GetMinMarginBottomRight 2385
-#define wxPageSetupDialogData_GetPaperId 2386
-#define wxPageSetupDialogData_GetPaperSize 2387
-#define wxPageSetupDialogData_GetPrintData 2389
-#define wxPageSetupDialogData_IsOk 2390
-#define wxPageSetupDialogData_SetDefaultInfo 2391
-#define wxPageSetupDialogData_SetDefaultMinMargins 2392
-#define wxPageSetupDialogData_SetMarginTopLeft 2393
-#define wxPageSetupDialogData_SetMarginBottomRight 2394
-#define wxPageSetupDialogData_SetMinMarginTopLeft 2395
-#define wxPageSetupDialogData_SetMinMarginBottomRight 2396
-#define wxPageSetupDialogData_SetPaperId 2397
-#define wxPageSetupDialogData_SetPaperSize_1_1 2398
-#define wxPageSetupDialogData_SetPaperSize_1_0 2399
-#define wxPageSetupDialogData_SetPrintData 2400
-#define wxPrintDialog_new_2_0 2401
-#define wxPrintDialog_new_2_1 2402
-#define wxPrintDialog_destruct 2403
-#define wxPrintDialog_GetPrintDialogData 2404
-#define wxPrintDialog_GetPrintDC 2405
-#define wxPrintDialogData_new_0 2406
-#define wxPrintDialogData_new_1_1 2407
-#define wxPrintDialogData_new_1_0 2408
-#define wxPrintDialogData_destruct 2409
-#define wxPrintDialogData_EnableHelp 2410
-#define wxPrintDialogData_EnablePageNumbers 2411
-#define wxPrintDialogData_EnablePrintToFile 2412
-#define wxPrintDialogData_EnableSelection 2413
-#define wxPrintDialogData_GetAllPages 2414
-#define wxPrintDialogData_GetCollate 2415
-#define wxPrintDialogData_GetFromPage 2416
-#define wxPrintDialogData_GetMaxPage 2417
-#define wxPrintDialogData_GetMinPage 2418
-#define wxPrintDialogData_GetNoCopies 2419
-#define wxPrintDialogData_GetPrintData 2420
-#define wxPrintDialogData_GetPrintToFile 2421
-#define wxPrintDialogData_GetSelection 2422
-#define wxPrintDialogData_GetToPage 2423
-#define wxPrintDialogData_IsOk 2424
-#define wxPrintDialogData_SetCollate 2425
-#define wxPrintDialogData_SetFromPage 2426
-#define wxPrintDialogData_SetMaxPage 2427
-#define wxPrintDialogData_SetMinPage 2428
-#define wxPrintDialogData_SetNoCopies 2429
-#define wxPrintDialogData_SetPrintData 2430
-#define wxPrintDialogData_SetPrintToFile 2431
-#define wxPrintDialogData_SetSelection 2432
-#define wxPrintDialogData_SetToPage 2433
-#define wxPrintData_new_0 2434
-#define wxPrintData_new_1 2435
-#define wxPrintData_destruct 2436
-#define wxPrintData_GetCollate 2437
-#define wxPrintData_GetBin 2438
-#define wxPrintData_GetColour 2439
-#define wxPrintData_GetDuplex 2440
-#define wxPrintData_GetNoCopies 2441
-#define wxPrintData_GetOrientation 2442
-#define wxPrintData_GetPaperId 2443
-#define wxPrintData_GetPrinterName 2444
-#define wxPrintData_GetQuality 2445
-#define wxPrintData_IsOk 2446
-#define wxPrintData_SetBin 2447
-#define wxPrintData_SetCollate 2448
-#define wxPrintData_SetColour 2449
-#define wxPrintData_SetDuplex 2450
-#define wxPrintData_SetNoCopies 2451
-#define wxPrintData_SetOrientation 2452
-#define wxPrintData_SetPaperId 2453
-#define wxPrintData_SetPrinterName 2454
-#define wxPrintData_SetQuality 2455
-#define wxPrintPreview_new_2 2458
-#define wxPrintPreview_new_3 2459
-#define wxPrintPreview_destruct 2461
-#define wxPrintPreview_GetCanvas 2462
-#define wxPrintPreview_GetCurrentPage 2463
-#define wxPrintPreview_GetFrame 2464
-#define wxPrintPreview_GetMaxPage 2465
-#define wxPrintPreview_GetMinPage 2466
-#define wxPrintPreview_GetPrintout 2467
-#define wxPrintPreview_GetPrintoutForPrinting 2468
-#define wxPrintPreview_IsOk 2469
-#define wxPrintPreview_PaintPage 2470
-#define wxPrintPreview_Print 2471
-#define wxPrintPreview_RenderPage 2472
-#define wxPrintPreview_SetCanvas 2473
-#define wxPrintPreview_SetCurrentPage 2474
-#define wxPrintPreview_SetFrame 2475
-#define wxPrintPreview_SetPrintout 2476
-#define wxPrintPreview_SetZoom 2477
-#define wxPreviewFrame_new 2478
-#define wxPreviewFrame_destruct 2479
-#define wxPreviewFrame_CreateControlBar 2480
-#define wxPreviewFrame_CreateCanvas 2481
-#define wxPreviewFrame_Initialize 2482
-#define wxPreviewFrame_OnCloseWindow 2483
-#define wxPreviewControlBar_new 2484
-#define wxPreviewControlBar_destruct 2485
-#define wxPreviewControlBar_CreateButtons 2486
-#define wxPreviewControlBar_GetPrintPreview 2487
-#define wxPreviewControlBar_GetZoomControl 2488
-#define wxPreviewControlBar_SetZoomControl 2489
-#define wxPrinter_new 2491
-#define wxPrinter_CreateAbortWindow 2492
-#define wxPrinter_GetAbort 2493
-#define wxPrinter_GetLastError 2494
-#define wxPrinter_GetPrintDialogData 2495
-#define wxPrinter_Print 2496
-#define wxPrinter_PrintDialog 2497
-#define wxPrinter_ReportError 2498
-#define wxPrinter_Setup 2499
-#define wxPrinter_destroy 2500
-#define wxXmlResource_new_1 2501
-#define wxXmlResource_new_2 2502
-#define wxXmlResource_destruct 2503
-#define wxXmlResource_AttachUnknownControl 2504
-#define wxXmlResource_ClearHandlers 2505
-#define wxXmlResource_CompareVersion 2506
-#define wxXmlResource_Get 2507
-#define wxXmlResource_GetFlags 2508
-#define wxXmlResource_GetVersion 2509
-#define wxXmlResource_GetXRCID 2510
-#define wxXmlResource_InitAllHandlers 2511
-#define wxXmlResource_Load 2512
-#define wxXmlResource_LoadBitmap 2513
-#define wxXmlResource_LoadDialog_2 2514
-#define wxXmlResource_LoadDialog_3 2515
-#define wxXmlResource_LoadFrame_2 2516
-#define wxXmlResource_LoadFrame_3 2517
-#define wxXmlResource_LoadIcon 2518
-#define wxXmlResource_LoadMenu 2519
-#define wxXmlResource_LoadMenuBar_2 2520
-#define wxXmlResource_LoadMenuBar_1 2521
-#define wxXmlResource_LoadPanel_2 2522
-#define wxXmlResource_LoadPanel_3 2523
-#define wxXmlResource_LoadToolBar 2524
-#define wxXmlResource_Set 2525
-#define wxXmlResource_SetFlags 2526
-#define wxXmlResource_Unload 2527
-#define wxXmlResource_xrcctrl 2528
-#define wxHtmlEasyPrinting_new 2529
-#define wxHtmlEasyPrinting_destruct 2530
-#define wxHtmlEasyPrinting_GetPrintData 2531
-#define wxHtmlEasyPrinting_GetPageSetupData 2532
-#define wxHtmlEasyPrinting_PreviewFile 2533
-#define wxHtmlEasyPrinting_PreviewText 2534
-#define wxHtmlEasyPrinting_PrintFile 2535
-#define wxHtmlEasyPrinting_PrintText 2536
-#define wxHtmlEasyPrinting_PageSetup 2537
-#define wxHtmlEasyPrinting_SetFonts 2538
-#define wxHtmlEasyPrinting_SetHeader 2539
-#define wxHtmlEasyPrinting_SetFooter 2540
-#define wxGLCanvas_new_2 2542
-#define wxGLCanvas_new_3_1 2543
-#define wxGLCanvas_new_3_0 2544
-#define wxGLCanvas_GetContext 2545
-#define wxGLCanvas_SetCurrent 2547
-#define wxGLCanvas_SwapBuffers 2548
-#define wxGLCanvas_destroy 2549
-#define wxAuiManager_new 2550
-#define wxAuiManager_destruct 2551
-#define wxAuiManager_AddPane_2_1 2552
-#define wxAuiManager_AddPane_3 2553
-#define wxAuiManager_AddPane_2_0 2554
-#define wxAuiManager_DetachPane 2555
-#define wxAuiManager_GetAllPanes 2556
-#define wxAuiManager_GetArtProvider 2557
-#define wxAuiManager_GetDockSizeConstraint 2558
-#define wxAuiManager_GetFlags 2559
-#define wxAuiManager_GetManagedWindow 2560
-#define wxAuiManager_GetManager 2561
-#define wxAuiManager_GetPane_1_1 2562
-#define wxAuiManager_GetPane_1_0 2563
-#define wxAuiManager_HideHint 2564
-#define wxAuiManager_InsertPane 2565
-#define wxAuiManager_LoadPaneInfo 2566
-#define wxAuiManager_LoadPerspective 2567
-#define wxAuiManager_SavePaneInfo 2568
-#define wxAuiManager_SavePerspective 2569
-#define wxAuiManager_SetArtProvider 2570
-#define wxAuiManager_SetDockSizeConstraint 2571
-#define wxAuiManager_SetFlags 2572
-#define wxAuiManager_SetManagedWindow 2573
-#define wxAuiManager_ShowHint 2574
-#define wxAuiManager_UnInit 2575
-#define wxAuiManager_Update 2576
-#define wxAuiPaneInfo_new_0 2577
-#define wxAuiPaneInfo_new_1 2578
-#define wxAuiPaneInfo_destruct 2579
-#define wxAuiPaneInfo_BestSize_1 2580
-#define wxAuiPaneInfo_BestSize_2 2581
-#define wxAuiPaneInfo_Bottom 2582
-#define wxAuiPaneInfo_BottomDockable 2583
-#define wxAuiPaneInfo_Caption 2584
-#define wxAuiPaneInfo_CaptionVisible 2585
-#define wxAuiPaneInfo_Centre 2586
-#define wxAuiPaneInfo_CentrePane 2587
-#define wxAuiPaneInfo_CloseButton 2588
-#define wxAuiPaneInfo_DefaultPane 2589
-#define wxAuiPaneInfo_DestroyOnClose 2590
-#define wxAuiPaneInfo_Direction 2591
-#define wxAuiPaneInfo_Dock 2592
-#define wxAuiPaneInfo_Dockable 2593
-#define wxAuiPaneInfo_Fixed 2594
-#define wxAuiPaneInfo_Float 2595
-#define wxAuiPaneInfo_Floatable 2596
-#define wxAuiPaneInfo_FloatingPosition_1 2597
-#define wxAuiPaneInfo_FloatingPosition_2 2598
-#define wxAuiPaneInfo_FloatingSize_1 2599
-#define wxAuiPaneInfo_FloatingSize_2 2600
-#define wxAuiPaneInfo_Gripper 2601
-#define wxAuiPaneInfo_GripperTop 2602
-#define wxAuiPaneInfo_HasBorder 2603
-#define wxAuiPaneInfo_HasCaption 2604
-#define wxAuiPaneInfo_HasCloseButton 2605
-#define wxAuiPaneInfo_HasFlag 2606
-#define wxAuiPaneInfo_HasGripper 2607
-#define wxAuiPaneInfo_HasGripperTop 2608
-#define wxAuiPaneInfo_HasMaximizeButton 2609
-#define wxAuiPaneInfo_HasMinimizeButton 2610
-#define wxAuiPaneInfo_HasPinButton 2611
-#define wxAuiPaneInfo_Hide 2612
-#define wxAuiPaneInfo_IsBottomDockable 2613
-#define wxAuiPaneInfo_IsDocked 2614
-#define wxAuiPaneInfo_IsFixed 2615
-#define wxAuiPaneInfo_IsFloatable 2616
-#define wxAuiPaneInfo_IsFloating 2617
-#define wxAuiPaneInfo_IsLeftDockable 2618
-#define wxAuiPaneInfo_IsMovable 2619
-#define wxAuiPaneInfo_IsOk 2620
-#define wxAuiPaneInfo_IsResizable 2621
-#define wxAuiPaneInfo_IsRightDockable 2622
-#define wxAuiPaneInfo_IsShown 2623
-#define wxAuiPaneInfo_IsToolbar 2624
-#define wxAuiPaneInfo_IsTopDockable 2625
-#define wxAuiPaneInfo_Layer 2626
-#define wxAuiPaneInfo_Left 2627
-#define wxAuiPaneInfo_LeftDockable 2628
-#define wxAuiPaneInfo_MaxSize_1 2629
-#define wxAuiPaneInfo_MaxSize_2 2630
-#define wxAuiPaneInfo_MaximizeButton 2631
-#define wxAuiPaneInfo_MinSize_1 2632
-#define wxAuiPaneInfo_MinSize_2 2633
-#define wxAuiPaneInfo_MinimizeButton 2634
-#define wxAuiPaneInfo_Movable 2635
-#define wxAuiPaneInfo_Name 2636
-#define wxAuiPaneInfo_PaneBorder 2637
-#define wxAuiPaneInfo_PinButton 2638
-#define wxAuiPaneInfo_Position 2639
-#define wxAuiPaneInfo_Resizable 2640
-#define wxAuiPaneInfo_Right 2641
-#define wxAuiPaneInfo_RightDockable 2642
-#define wxAuiPaneInfo_Row 2643
-#define wxAuiPaneInfo_SafeSet 2644
-#define wxAuiPaneInfo_SetFlag 2645
-#define wxAuiPaneInfo_Show 2646
-#define wxAuiPaneInfo_ToolbarPane 2647
-#define wxAuiPaneInfo_Top 2648
-#define wxAuiPaneInfo_TopDockable 2649
-#define wxAuiPaneInfo_Window 2650
-#define wxAuiNotebook_new_0 2651
-#define wxAuiNotebook_new_2 2652
-#define wxAuiNotebook_AddPage 2653
-#define wxAuiNotebook_Create 2654
-#define wxAuiNotebook_DeletePage 2655
-#define wxAuiNotebook_GetArtProvider 2656
-#define wxAuiNotebook_GetPage 2657
-#define wxAuiNotebook_GetPageBitmap 2658
-#define wxAuiNotebook_GetPageCount 2659
-#define wxAuiNotebook_GetPageIndex 2660
-#define wxAuiNotebook_GetPageText 2661
-#define wxAuiNotebook_GetSelection 2662
-#define wxAuiNotebook_InsertPage 2663
-#define wxAuiNotebook_RemovePage 2664
-#define wxAuiNotebook_SetArtProvider 2665
-#define wxAuiNotebook_SetFont 2666
-#define wxAuiNotebook_SetPageBitmap 2667
-#define wxAuiNotebook_SetPageText 2668
-#define wxAuiNotebook_SetSelection 2669
-#define wxAuiNotebook_SetTabCtrlHeight 2670
-#define wxAuiNotebook_SetUniformBitmapSize 2671
-#define wxAuiNotebook_destroy 2672
-#define wxMDIParentFrame_new_0 2673
-#define wxMDIParentFrame_new_4 2674
-#define wxMDIParentFrame_destruct 2675
-#define wxMDIParentFrame_ActivateNext 2676
-#define wxMDIParentFrame_ActivatePrevious 2677
-#define wxMDIParentFrame_ArrangeIcons 2678
-#define wxMDIParentFrame_Cascade 2679
-#define wxMDIParentFrame_Create 2680
-#define wxMDIParentFrame_GetActiveChild 2681
-#define wxMDIParentFrame_GetClientWindow 2682
-#define wxMDIParentFrame_Tile 2683
-#define wxMDIChildFrame_new_0 2684
-#define wxMDIChildFrame_new_4 2685
-#define wxMDIChildFrame_destruct 2686
-#define wxMDIChildFrame_Activate 2687
-#define wxMDIChildFrame_Create 2688
-#define wxMDIChildFrame_Maximize 2689
-#define wxMDIChildFrame_Restore 2690
-#define wxMDIClientWindow_new_0 2691
-#define wxMDIClientWindow_new_2 2692
-#define wxMDIClientWindow_destruct 2693
-#define wxMDIClientWindow_CreateClient 2694
-#define wxLayoutAlgorithm_new 2695
-#define wxLayoutAlgorithm_LayoutFrame 2696
-#define wxLayoutAlgorithm_LayoutMDIFrame 2697
-#define wxLayoutAlgorithm_LayoutWindow 2698
-#define wxLayoutAlgorithm_destroy 2699
-#define wxEvent_GetId 2700
-#define wxEvent_GetSkipped 2701
-#define wxEvent_GetTimestamp 2702
-#define wxEvent_IsCommandEvent 2703
-#define wxEvent_ResumePropagation 2704
-#define wxEvent_ShouldPropagate 2705
-#define wxEvent_Skip 2706
-#define wxEvent_StopPropagation 2707
-#define wxCommandEvent_getClientData 2708
-#define wxCommandEvent_GetExtraLong 2709
-#define wxCommandEvent_GetInt 2710
-#define wxCommandEvent_GetSelection 2711
-#define wxCommandEvent_GetString 2712
-#define wxCommandEvent_IsChecked 2713
-#define wxCommandEvent_IsSelection 2714
-#define wxCommandEvent_SetInt 2715
-#define wxCommandEvent_SetString 2716
-#define wxScrollEvent_GetOrientation 2717
-#define wxScrollEvent_GetPosition 2718
-#define wxScrollWinEvent_GetOrientation 2719
-#define wxScrollWinEvent_GetPosition 2720
-#define wxMouseEvent_AltDown 2721
-#define wxMouseEvent_Button 2722
-#define wxMouseEvent_ButtonDClick 2723
-#define wxMouseEvent_ButtonDown 2724
-#define wxMouseEvent_ButtonUp 2725
-#define wxMouseEvent_CmdDown 2726
-#define wxMouseEvent_ControlDown 2727
-#define wxMouseEvent_Dragging 2728
-#define wxMouseEvent_Entering 2729
-#define wxMouseEvent_GetButton 2730
-#define wxMouseEvent_GetPosition 2733
-#define wxMouseEvent_GetLogicalPosition 2734
-#define wxMouseEvent_GetLinesPerAction 2735
-#define wxMouseEvent_GetWheelRotation 2736
-#define wxMouseEvent_GetWheelDelta 2737
-#define wxMouseEvent_GetX 2738
-#define wxMouseEvent_GetY 2739
-#define wxMouseEvent_IsButton 2740
-#define wxMouseEvent_IsPageScroll 2741
-#define wxMouseEvent_Leaving 2742
-#define wxMouseEvent_LeftDClick 2743
-#define wxMouseEvent_LeftDown 2744
-#define wxMouseEvent_LeftIsDown 2745
-#define wxMouseEvent_LeftUp 2746
-#define wxMouseEvent_MetaDown 2747
-#define wxMouseEvent_MiddleDClick 2748
-#define wxMouseEvent_MiddleDown 2749
-#define wxMouseEvent_MiddleIsDown 2750
-#define wxMouseEvent_MiddleUp 2751
-#define wxMouseEvent_Moving 2752
-#define wxMouseEvent_RightDClick 2753
-#define wxMouseEvent_RightDown 2754
-#define wxMouseEvent_RightIsDown 2755
-#define wxMouseEvent_RightUp 2756
-#define wxMouseEvent_ShiftDown 2757
-#define wxSetCursorEvent_GetCursor 2758
-#define wxSetCursorEvent_GetX 2759
-#define wxSetCursorEvent_GetY 2760
-#define wxSetCursorEvent_HasCursor 2761
-#define wxSetCursorEvent_SetCursor 2762
-#define wxKeyEvent_AltDown 2763
-#define wxKeyEvent_CmdDown 2764
-#define wxKeyEvent_ControlDown 2765
-#define wxKeyEvent_GetKeyCode 2766
-#define wxKeyEvent_GetModifiers 2767
-#define wxKeyEvent_GetPosition 2770
-#define wxKeyEvent_GetRawKeyCode 2771
-#define wxKeyEvent_GetRawKeyFlags 2772
-#define wxKeyEvent_GetUnicodeKey 2773
-#define wxKeyEvent_GetX 2774
-#define wxKeyEvent_GetY 2775
-#define wxKeyEvent_HasModifiers 2776
-#define wxKeyEvent_MetaDown 2777
-#define wxKeyEvent_ShiftDown 2778
-#define wxSizeEvent_GetSize 2779
-#define wxMoveEvent_GetPosition 2780
-#define wxEraseEvent_GetDC 2781
-#define wxFocusEvent_GetWindow 2782
-#define wxChildFocusEvent_GetWindow 2783
-#define wxMenuEvent_GetMenu 2784
-#define wxMenuEvent_GetMenuId 2785
-#define wxMenuEvent_IsPopup 2786
-#define wxCloseEvent_CanVeto 2787
-#define wxCloseEvent_GetLoggingOff 2788
-#define wxCloseEvent_SetCanVeto 2789
-#define wxCloseEvent_SetLoggingOff 2790
-#define wxCloseEvent_Veto 2791
-#define wxShowEvent_SetShow 2792
-#define wxShowEvent_GetShow 2793
-#define wxIconizeEvent_Iconized 2794
-#define wxJoystickEvent_ButtonDown 2795
-#define wxJoystickEvent_ButtonIsDown 2796
-#define wxJoystickEvent_ButtonUp 2797
-#define wxJoystickEvent_GetButtonChange 2798
-#define wxJoystickEvent_GetButtonState 2799
-#define wxJoystickEvent_GetJoystick 2800
-#define wxJoystickEvent_GetPosition 2801
-#define wxJoystickEvent_GetZPosition 2802
-#define wxJoystickEvent_IsButton 2803
-#define wxJoystickEvent_IsMove 2804
-#define wxJoystickEvent_IsZMove 2805
-#define wxUpdateUIEvent_CanUpdate 2806
-#define wxUpdateUIEvent_Check 2807
-#define wxUpdateUIEvent_Enable 2808
-#define wxUpdateUIEvent_Show 2809
-#define wxUpdateUIEvent_GetChecked 2810
-#define wxUpdateUIEvent_GetEnabled 2811
-#define wxUpdateUIEvent_GetShown 2812
-#define wxUpdateUIEvent_GetSetChecked 2813
-#define wxUpdateUIEvent_GetSetEnabled 2814
-#define wxUpdateUIEvent_GetSetShown 2815
-#define wxUpdateUIEvent_GetSetText 2816
-#define wxUpdateUIEvent_GetText 2817
-#define wxUpdateUIEvent_GetMode 2818
-#define wxUpdateUIEvent_GetUpdateInterval 2819
-#define wxUpdateUIEvent_ResetUpdateTime 2820
-#define wxUpdateUIEvent_SetMode 2821
-#define wxUpdateUIEvent_SetText 2822
-#define wxUpdateUIEvent_SetUpdateInterval 2823
-#define wxMouseCaptureChangedEvent_GetCapturedWindow 2824
-#define wxPaletteChangedEvent_SetChangedWindow 2825
-#define wxPaletteChangedEvent_GetChangedWindow 2826
-#define wxQueryNewPaletteEvent_SetPaletteRealized 2827
-#define wxQueryNewPaletteEvent_GetPaletteRealized 2828
-#define wxNavigationKeyEvent_GetDirection 2829
-#define wxNavigationKeyEvent_SetDirection 2830
-#define wxNavigationKeyEvent_IsWindowChange 2831
-#define wxNavigationKeyEvent_SetWindowChange 2832
-#define wxNavigationKeyEvent_IsFromTab 2833
-#define wxNavigationKeyEvent_SetFromTab 2834
-#define wxNavigationKeyEvent_GetCurrentFocus 2835
-#define wxNavigationKeyEvent_SetCurrentFocus 2836
-#define wxHelpEvent_GetOrigin 2837
-#define wxHelpEvent_GetPosition 2838
-#define wxHelpEvent_SetOrigin 2839
-#define wxHelpEvent_SetPosition 2840
-#define wxContextMenuEvent_GetPosition 2841
-#define wxContextMenuEvent_SetPosition 2842
-#define wxIdleEvent_CanSend 2843
-#define wxIdleEvent_GetMode 2844
-#define wxIdleEvent_RequestMore 2845
-#define wxIdleEvent_MoreRequested 2846
-#define wxIdleEvent_SetMode 2847
-#define wxGridEvent_AltDown 2848
-#define wxGridEvent_ControlDown 2849
-#define wxGridEvent_GetCol 2850
-#define wxGridEvent_GetPosition 2851
-#define wxGridEvent_GetRow 2852
-#define wxGridEvent_MetaDown 2853
-#define wxGridEvent_Selecting 2854
-#define wxGridEvent_ShiftDown 2855
-#define wxNotifyEvent_Allow 2856
-#define wxNotifyEvent_IsAllowed 2857
-#define wxNotifyEvent_Veto 2858
-#define wxSashEvent_GetEdge 2859
-#define wxSashEvent_GetDragRect 2860
-#define wxSashEvent_GetDragStatus 2861
-#define wxListEvent_GetCacheFrom 2862
-#define wxListEvent_GetCacheTo 2863
-#define wxListEvent_GetKeyCode 2864
-#define wxListEvent_GetIndex 2865
-#define wxListEvent_GetColumn 2866
-#define wxListEvent_GetPoint 2867
-#define wxListEvent_GetLabel 2868
-#define wxListEvent_GetText 2869
-#define wxListEvent_GetImage 2870
-#define wxListEvent_GetData 2871
-#define wxListEvent_GetMask 2872
-#define wxListEvent_GetItem 2873
-#define wxListEvent_IsEditCancelled 2874
-#define wxDateEvent_GetDate 2875
-#define wxCalendarEvent_GetWeekDay 2876
-#define wxFileDirPickerEvent_GetPath 2877
-#define wxColourPickerEvent_GetColour 2878
-#define wxFontPickerEvent_GetFont 2879
-#define wxStyledTextEvent_GetPosition 2880
-#define wxStyledTextEvent_GetKey 2881
-#define wxStyledTextEvent_GetModifiers 2882
-#define wxStyledTextEvent_GetModificationType 2883
-#define wxStyledTextEvent_GetText 2884
-#define wxStyledTextEvent_GetLength 2885
-#define wxStyledTextEvent_GetLinesAdded 2886
-#define wxStyledTextEvent_GetLine 2887
-#define wxStyledTextEvent_GetFoldLevelNow 2888
-#define wxStyledTextEvent_GetFoldLevelPrev 2889
-#define wxStyledTextEvent_GetMargin 2890
-#define wxStyledTextEvent_GetMessage 2891
-#define wxStyledTextEvent_GetWParam 2892
-#define wxStyledTextEvent_GetLParam 2893
-#define wxStyledTextEvent_GetListType 2894
-#define wxStyledTextEvent_GetX 2895
-#define wxStyledTextEvent_GetY 2896
-#define wxStyledTextEvent_GetDragText 2897
-#define wxStyledTextEvent_GetDragAllowMove 2898
-#define wxStyledTextEvent_GetDragResult 2899
-#define wxStyledTextEvent_GetShift 2900
-#define wxStyledTextEvent_GetControl 2901
-#define wxStyledTextEvent_GetAlt 2902
-#define utils_wxGetKeyState 2903
-#define utils_wxGetMousePosition 2904
-#define utils_wxGetMouseState 2905
-#define utils_wxSetDetectableAutoRepeat 2906
-#define utils_wxBell 2907
-#define utils_wxFindMenuItemId 2908
-#define utils_wxGenericFindWindowAtPoint 2909
-#define utils_wxFindWindowAtPoint 2910
-#define utils_wxBeginBusyCursor 2911
-#define utils_wxEndBusyCursor 2912
-#define utils_wxIsBusy 2913
-#define utils_wxShutdown 2914
-#define utils_wxShell 2915
-#define utils_wxLaunchDefaultBrowser 2916
-#define utils_wxGetEmailAddress 2917
-#define utils_wxGetUserId 2918
-#define utils_wxGetHomeDir 2919
-#define utils_wxNewId 2920
-#define utils_wxRegisterId 2921
-#define utils_wxGetCurrentId 2922
-#define utils_wxGetOsDescription 2923
-#define utils_wxIsPlatformLittleEndian 2924
-#define utils_wxIsPlatform64Bit 2925
-#define wxPrintout_new 2926
-#define wxPrintout_destruct 2927
-#define wxPrintout_GetDC 2928
-#define wxPrintout_GetPageSizeMM 2929
-#define wxPrintout_GetPageSizePixels 2930
-#define wxPrintout_GetPaperRectPixels 2931
-#define wxPrintout_GetPPIPrinter 2932
-#define wxPrintout_GetPPIScreen 2933
-#define wxPrintout_GetTitle 2934
-#define wxPrintout_IsPreview 2935
-#define wxPrintout_FitThisSizeToPaper 2936
-#define wxPrintout_FitThisSizeToPage 2937
-#define wxPrintout_FitThisSizeToPageMargins 2938
-#define wxPrintout_MapScreenSizeToPaper 2939
-#define wxPrintout_MapScreenSizeToPage 2940
-#define wxPrintout_MapScreenSizeToPageMargins 2941
-#define wxPrintout_MapScreenSizeToDevice 2942
-#define wxPrintout_GetLogicalPaperRect 2943
-#define wxPrintout_GetLogicalPageRect 2944
-#define wxPrintout_GetLogicalPageMarginsRect 2945
-#define wxPrintout_SetLogicalOrigin 2946
-#define wxPrintout_OffsetLogicalOrigin 2947
-#define wxStyledTextCtrl_new_2 2948
-#define wxStyledTextCtrl_new_0 2949
-#define wxStyledTextCtrl_destruct 2950
-#define wxStyledTextCtrl_Create 2951
-#define wxStyledTextCtrl_AddText 2952
-#define wxStyledTextCtrl_AddStyledText 2953
-#define wxStyledTextCtrl_InsertText 2954
-#define wxStyledTextCtrl_ClearAll 2955
-#define wxStyledTextCtrl_ClearDocumentStyle 2956
-#define wxStyledTextCtrl_GetLength 2957
-#define wxStyledTextCtrl_GetCharAt 2958
-#define wxStyledTextCtrl_GetCurrentPos 2959
-#define wxStyledTextCtrl_GetAnchor 2960
-#define wxStyledTextCtrl_GetStyleAt 2961
-#define wxStyledTextCtrl_Redo 2962
-#define wxStyledTextCtrl_SetUndoCollection 2963
-#define wxStyledTextCtrl_SelectAll 2964
-#define wxStyledTextCtrl_SetSavePoint 2965
-#define wxStyledTextCtrl_GetStyledText 2966
-#define wxStyledTextCtrl_CanRedo 2967
-#define wxStyledTextCtrl_MarkerLineFromHandle 2968
-#define wxStyledTextCtrl_MarkerDeleteHandle 2969
-#define wxStyledTextCtrl_GetUndoCollection 2970
-#define wxStyledTextCtrl_GetViewWhiteSpace 2971
-#define wxStyledTextCtrl_SetViewWhiteSpace 2972
-#define wxStyledTextCtrl_PositionFromPoint 2973
-#define wxStyledTextCtrl_PositionFromPointClose 2974
-#define wxStyledTextCtrl_GotoLine 2975
-#define wxStyledTextCtrl_GotoPos 2976
-#define wxStyledTextCtrl_SetAnchor 2977
-#define wxStyledTextCtrl_GetCurLine 2978
-#define wxStyledTextCtrl_GetEndStyled 2979
-#define wxStyledTextCtrl_ConvertEOLs 2980
-#define wxStyledTextCtrl_GetEOLMode 2981
-#define wxStyledTextCtrl_SetEOLMode 2982
-#define wxStyledTextCtrl_StartStyling 2983
-#define wxStyledTextCtrl_SetStyling 2984
-#define wxStyledTextCtrl_GetBufferedDraw 2985
-#define wxStyledTextCtrl_SetBufferedDraw 2986
-#define wxStyledTextCtrl_SetTabWidth 2987
-#define wxStyledTextCtrl_GetTabWidth 2988
-#define wxStyledTextCtrl_SetCodePage 2989
-#define wxStyledTextCtrl_MarkerDefine 2990
-#define wxStyledTextCtrl_MarkerSetForeground 2991
-#define wxStyledTextCtrl_MarkerSetBackground 2992
-#define wxStyledTextCtrl_MarkerAdd 2993
-#define wxStyledTextCtrl_MarkerDelete 2994
-#define wxStyledTextCtrl_MarkerDeleteAll 2995
-#define wxStyledTextCtrl_MarkerGet 2996
-#define wxStyledTextCtrl_MarkerNext 2997
-#define wxStyledTextCtrl_MarkerPrevious 2998
-#define wxStyledTextCtrl_MarkerDefineBitmap 2999
-#define wxStyledTextCtrl_MarkerAddSet 3000
-#define wxStyledTextCtrl_MarkerSetAlpha 3001
-#define wxStyledTextCtrl_SetMarginType 3002
-#define wxStyledTextCtrl_GetMarginType 3003
-#define wxStyledTextCtrl_SetMarginWidth 3004
-#define wxStyledTextCtrl_GetMarginWidth 3005
-#define wxStyledTextCtrl_SetMarginMask 3006
-#define wxStyledTextCtrl_GetMarginMask 3007
-#define wxStyledTextCtrl_SetMarginSensitive 3008
-#define wxStyledTextCtrl_GetMarginSensitive 3009
-#define wxStyledTextCtrl_StyleClearAll 3010
-#define wxStyledTextCtrl_StyleSetForeground 3011
-#define wxStyledTextCtrl_StyleSetBackground 3012
-#define wxStyledTextCtrl_StyleSetBold 3013
-#define wxStyledTextCtrl_StyleSetItalic 3014
-#define wxStyledTextCtrl_StyleSetSize 3015
-#define wxStyledTextCtrl_StyleSetFaceName 3016
-#define wxStyledTextCtrl_StyleSetEOLFilled 3017
-#define wxStyledTextCtrl_StyleResetDefault 3018
-#define wxStyledTextCtrl_StyleSetUnderline 3019
-#define wxStyledTextCtrl_StyleSetCase 3020
-#define wxStyledTextCtrl_StyleSetHotSpot 3021
-#define wxStyledTextCtrl_SetSelForeground 3022
-#define wxStyledTextCtrl_SetSelBackground 3023
-#define wxStyledTextCtrl_GetSelAlpha 3024
-#define wxStyledTextCtrl_SetSelAlpha 3025
-#define wxStyledTextCtrl_SetCaretForeground 3026
-#define wxStyledTextCtrl_CmdKeyAssign 3027
-#define wxStyledTextCtrl_CmdKeyClear 3028
-#define wxStyledTextCtrl_CmdKeyClearAll 3029
-#define wxStyledTextCtrl_SetStyleBytes 3030
-#define wxStyledTextCtrl_StyleSetVisible 3031
-#define wxStyledTextCtrl_GetCaretPeriod 3032
-#define wxStyledTextCtrl_SetCaretPeriod 3033
-#define wxStyledTextCtrl_SetWordChars 3034
-#define wxStyledTextCtrl_BeginUndoAction 3035
-#define wxStyledTextCtrl_EndUndoAction 3036
-#define wxStyledTextCtrl_IndicatorSetStyle 3037
-#define wxStyledTextCtrl_IndicatorGetStyle 3038
-#define wxStyledTextCtrl_IndicatorSetForeground 3039
-#define wxStyledTextCtrl_IndicatorGetForeground 3040
-#define wxStyledTextCtrl_SetWhitespaceForeground 3041
-#define wxStyledTextCtrl_SetWhitespaceBackground 3042
-#define wxStyledTextCtrl_GetStyleBits 3043
-#define wxStyledTextCtrl_SetLineState 3044
-#define wxStyledTextCtrl_GetLineState 3045
-#define wxStyledTextCtrl_GetMaxLineState 3046
-#define wxStyledTextCtrl_GetCaretLineVisible 3047
-#define wxStyledTextCtrl_SetCaretLineVisible 3048
-#define wxStyledTextCtrl_GetCaretLineBackground 3049
-#define wxStyledTextCtrl_SetCaretLineBackground 3050
-#define wxStyledTextCtrl_AutoCompShow 3051
-#define wxStyledTextCtrl_AutoCompCancel 3052
-#define wxStyledTextCtrl_AutoCompActive 3053
-#define wxStyledTextCtrl_AutoCompPosStart 3054
-#define wxStyledTextCtrl_AutoCompComplete 3055
-#define wxStyledTextCtrl_AutoCompStops 3056
-#define wxStyledTextCtrl_AutoCompSetSeparator 3057
-#define wxStyledTextCtrl_AutoCompGetSeparator 3058
-#define wxStyledTextCtrl_AutoCompSelect 3059
-#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3060
-#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3061
-#define wxStyledTextCtrl_AutoCompSetFillUps 3062
-#define wxStyledTextCtrl_AutoCompSetChooseSingle 3063
-#define wxStyledTextCtrl_AutoCompGetChooseSingle 3064
-#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3065
-#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3066
-#define wxStyledTextCtrl_UserListShow 3067
-#define wxStyledTextCtrl_AutoCompSetAutoHide 3068
-#define wxStyledTextCtrl_AutoCompGetAutoHide 3069
-#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3070
-#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3071
-#define wxStyledTextCtrl_RegisterImage 3072
-#define wxStyledTextCtrl_ClearRegisteredImages 3073
-#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3074
-#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3075
-#define wxStyledTextCtrl_AutoCompSetMaxWidth 3076
-#define wxStyledTextCtrl_AutoCompGetMaxWidth 3077
-#define wxStyledTextCtrl_AutoCompSetMaxHeight 3078
-#define wxStyledTextCtrl_AutoCompGetMaxHeight 3079
-#define wxStyledTextCtrl_SetIndent 3080
-#define wxStyledTextCtrl_GetIndent 3081
-#define wxStyledTextCtrl_SetUseTabs 3082
-#define wxStyledTextCtrl_GetUseTabs 3083
-#define wxStyledTextCtrl_SetLineIndentation 3084
-#define wxStyledTextCtrl_GetLineIndentation 3085
-#define wxStyledTextCtrl_GetLineIndentPosition 3086
-#define wxStyledTextCtrl_GetColumn 3087
-#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3088
-#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3089
-#define wxStyledTextCtrl_SetIndentationGuides 3090
-#define wxStyledTextCtrl_GetIndentationGuides 3091
-#define wxStyledTextCtrl_SetHighlightGuide 3092
-#define wxStyledTextCtrl_GetHighlightGuide 3093
-#define wxStyledTextCtrl_GetLineEndPosition 3094
-#define wxStyledTextCtrl_GetCodePage 3095
-#define wxStyledTextCtrl_GetCaretForeground 3096
-#define wxStyledTextCtrl_GetReadOnly 3097
-#define wxStyledTextCtrl_SetCurrentPos 3098
-#define wxStyledTextCtrl_SetSelectionStart 3099
-#define wxStyledTextCtrl_GetSelectionStart 3100
-#define wxStyledTextCtrl_SetSelectionEnd 3101
-#define wxStyledTextCtrl_GetSelectionEnd 3102
-#define wxStyledTextCtrl_SetPrintMagnification 3103
-#define wxStyledTextCtrl_GetPrintMagnification 3104
-#define wxStyledTextCtrl_SetPrintColourMode 3105
-#define wxStyledTextCtrl_GetPrintColourMode 3106
-#define wxStyledTextCtrl_FindText 3107
-#define wxStyledTextCtrl_FormatRange 3108
-#define wxStyledTextCtrl_GetFirstVisibleLine 3109
-#define wxStyledTextCtrl_GetLine 3110
-#define wxStyledTextCtrl_GetLineCount 3111
-#define wxStyledTextCtrl_SetMarginLeft 3112
-#define wxStyledTextCtrl_GetMarginLeft 3113
-#define wxStyledTextCtrl_SetMarginRight 3114
-#define wxStyledTextCtrl_GetMarginRight 3115
-#define wxStyledTextCtrl_GetModify 3116
-#define wxStyledTextCtrl_SetSelection 3117
-#define wxStyledTextCtrl_GetSelectedText 3118
-#define wxStyledTextCtrl_GetTextRange 3119
-#define wxStyledTextCtrl_HideSelection 3120
-#define wxStyledTextCtrl_LineFromPosition 3121
-#define wxStyledTextCtrl_PositionFromLine 3122
-#define wxStyledTextCtrl_LineScroll 3123
-#define wxStyledTextCtrl_EnsureCaretVisible 3124
-#define wxStyledTextCtrl_ReplaceSelection 3125
-#define wxStyledTextCtrl_SetReadOnly 3126
-#define wxStyledTextCtrl_CanPaste 3127
-#define wxStyledTextCtrl_CanUndo 3128
-#define wxStyledTextCtrl_EmptyUndoBuffer 3129
-#define wxStyledTextCtrl_Undo 3130
-#define wxStyledTextCtrl_Cut 3131
-#define wxStyledTextCtrl_Copy 3132
-#define wxStyledTextCtrl_Paste 3133
-#define wxStyledTextCtrl_Clear 3134
-#define wxStyledTextCtrl_SetText 3135
-#define wxStyledTextCtrl_GetText 3136
-#define wxStyledTextCtrl_GetTextLength 3137
-#define wxStyledTextCtrl_GetOvertype 3138
-#define wxStyledTextCtrl_SetCaretWidth 3139
-#define wxStyledTextCtrl_GetCaretWidth 3140
-#define wxStyledTextCtrl_SetTargetStart 3141
-#define wxStyledTextCtrl_GetTargetStart 3142
-#define wxStyledTextCtrl_SetTargetEnd 3143
-#define wxStyledTextCtrl_GetTargetEnd 3144
-#define wxStyledTextCtrl_ReplaceTarget 3145
-#define wxStyledTextCtrl_SearchInTarget 3146
-#define wxStyledTextCtrl_SetSearchFlags 3147
-#define wxStyledTextCtrl_GetSearchFlags 3148
-#define wxStyledTextCtrl_CallTipShow 3149
-#define wxStyledTextCtrl_CallTipCancel 3150
-#define wxStyledTextCtrl_CallTipActive 3151
-#define wxStyledTextCtrl_CallTipPosAtStart 3152
-#define wxStyledTextCtrl_CallTipSetHighlight 3153
-#define wxStyledTextCtrl_CallTipSetBackground 3154
-#define wxStyledTextCtrl_CallTipSetForeground 3155
-#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3156
-#define wxStyledTextCtrl_CallTipUseStyle 3157
-#define wxStyledTextCtrl_VisibleFromDocLine 3158
-#define wxStyledTextCtrl_DocLineFromVisible 3159
-#define wxStyledTextCtrl_WrapCount 3160
-#define wxStyledTextCtrl_SetFoldLevel 3161
-#define wxStyledTextCtrl_GetFoldLevel 3162
-#define wxStyledTextCtrl_GetLastChild 3163
-#define wxStyledTextCtrl_GetFoldParent 3164
-#define wxStyledTextCtrl_ShowLines 3165
-#define wxStyledTextCtrl_HideLines 3166
-#define wxStyledTextCtrl_GetLineVisible 3167
-#define wxStyledTextCtrl_SetFoldExpanded 3168
-#define wxStyledTextCtrl_GetFoldExpanded 3169
-#define wxStyledTextCtrl_ToggleFold 3170
-#define wxStyledTextCtrl_EnsureVisible 3171
-#define wxStyledTextCtrl_SetFoldFlags 3172
-#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3173
-#define wxStyledTextCtrl_SetTabIndents 3174
-#define wxStyledTextCtrl_GetTabIndents 3175
-#define wxStyledTextCtrl_SetBackSpaceUnIndents 3176
-#define wxStyledTextCtrl_GetBackSpaceUnIndents 3177
-#define wxStyledTextCtrl_SetMouseDwellTime 3178
-#define wxStyledTextCtrl_GetMouseDwellTime 3179
-#define wxStyledTextCtrl_WordStartPosition 3180
-#define wxStyledTextCtrl_WordEndPosition 3181
-#define wxStyledTextCtrl_SetWrapMode 3182
-#define wxStyledTextCtrl_GetWrapMode 3183
-#define wxStyledTextCtrl_SetWrapVisualFlags 3184
-#define wxStyledTextCtrl_GetWrapVisualFlags 3185
-#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3186
-#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3187
-#define wxStyledTextCtrl_SetWrapStartIndent 3188
-#define wxStyledTextCtrl_GetWrapStartIndent 3189
-#define wxStyledTextCtrl_SetLayoutCache 3190
-#define wxStyledTextCtrl_GetLayoutCache 3191
-#define wxStyledTextCtrl_SetScrollWidth 3192
-#define wxStyledTextCtrl_GetScrollWidth 3193
-#define wxStyledTextCtrl_TextWidth 3194
-#define wxStyledTextCtrl_GetEndAtLastLine 3195
-#define wxStyledTextCtrl_TextHeight 3196
-#define wxStyledTextCtrl_SetUseVerticalScrollBar 3197
-#define wxStyledTextCtrl_GetUseVerticalScrollBar 3198
-#define wxStyledTextCtrl_AppendText 3199
-#define wxStyledTextCtrl_GetTwoPhaseDraw 3200
-#define wxStyledTextCtrl_SetTwoPhaseDraw 3201
-#define wxStyledTextCtrl_TargetFromSelection 3202
-#define wxStyledTextCtrl_LinesJoin 3203
-#define wxStyledTextCtrl_LinesSplit 3204
-#define wxStyledTextCtrl_SetFoldMarginColour 3205
-#define wxStyledTextCtrl_SetFoldMarginHiColour 3206
-#define wxStyledTextCtrl_LineDown 3207
-#define wxStyledTextCtrl_LineDownExtend 3208
-#define wxStyledTextCtrl_LineUp 3209
-#define wxStyledTextCtrl_LineUpExtend 3210
-#define wxStyledTextCtrl_CharLeft 3211
-#define wxStyledTextCtrl_CharLeftExtend 3212
-#define wxStyledTextCtrl_CharRight 3213
-#define wxStyledTextCtrl_CharRightExtend 3214
-#define wxStyledTextCtrl_WordLeft 3215
-#define wxStyledTextCtrl_WordLeftExtend 3216
-#define wxStyledTextCtrl_WordRight 3217
-#define wxStyledTextCtrl_WordRightExtend 3218
-#define wxStyledTextCtrl_Home 3219
-#define wxStyledTextCtrl_HomeExtend 3220
-#define wxStyledTextCtrl_LineEnd 3221
-#define wxStyledTextCtrl_LineEndExtend 3222
-#define wxStyledTextCtrl_DocumentStart 3223
-#define wxStyledTextCtrl_DocumentStartExtend 3224
-#define wxStyledTextCtrl_DocumentEnd 3225
-#define wxStyledTextCtrl_DocumentEndExtend 3226
-#define wxStyledTextCtrl_PageUp 3227
-#define wxStyledTextCtrl_PageUpExtend 3228
-#define wxStyledTextCtrl_PageDown 3229
-#define wxStyledTextCtrl_PageDownExtend 3230
-#define wxStyledTextCtrl_EditToggleOvertype 3231
-#define wxStyledTextCtrl_Cancel 3232
-#define wxStyledTextCtrl_DeleteBack 3233
-#define wxStyledTextCtrl_Tab 3234
-#define wxStyledTextCtrl_BackTab 3235
-#define wxStyledTextCtrl_NewLine 3236
-#define wxStyledTextCtrl_FormFeed 3237
-#define wxStyledTextCtrl_VCHome 3238
-#define wxStyledTextCtrl_VCHomeExtend 3239
-#define wxStyledTextCtrl_ZoomIn 3240
-#define wxStyledTextCtrl_ZoomOut 3241
-#define wxStyledTextCtrl_DelWordLeft 3242
-#define wxStyledTextCtrl_DelWordRight 3243
-#define wxStyledTextCtrl_LineCut 3244
-#define wxStyledTextCtrl_LineDelete 3245
-#define wxStyledTextCtrl_LineTranspose 3246
-#define wxStyledTextCtrl_LineDuplicate 3247
-#define wxStyledTextCtrl_LowerCase 3248
-#define wxStyledTextCtrl_UpperCase 3249
-#define wxStyledTextCtrl_LineScrollDown 3250
-#define wxStyledTextCtrl_LineScrollUp 3251
-#define wxStyledTextCtrl_DeleteBackNotLine 3252
-#define wxStyledTextCtrl_HomeDisplay 3253
-#define wxStyledTextCtrl_HomeDisplayExtend 3254
-#define wxStyledTextCtrl_LineEndDisplay 3255
-#define wxStyledTextCtrl_LineEndDisplayExtend 3256
-#define wxStyledTextCtrl_HomeWrapExtend 3257
-#define wxStyledTextCtrl_LineEndWrap 3258
-#define wxStyledTextCtrl_LineEndWrapExtend 3259
-#define wxStyledTextCtrl_VCHomeWrap 3260
-#define wxStyledTextCtrl_VCHomeWrapExtend 3261
-#define wxStyledTextCtrl_LineCopy 3262
-#define wxStyledTextCtrl_MoveCaretInsideView 3263
-#define wxStyledTextCtrl_LineLength 3264
-#define wxStyledTextCtrl_BraceHighlight 3265
-#define wxStyledTextCtrl_BraceBadLight 3266
-#define wxStyledTextCtrl_BraceMatch 3267
-#define wxStyledTextCtrl_GetViewEOL 3268
-#define wxStyledTextCtrl_SetViewEOL 3269
-#define wxStyledTextCtrl_SetModEventMask 3270
-#define wxStyledTextCtrl_GetEdgeColumn 3271
-#define wxStyledTextCtrl_SetEdgeColumn 3272
-#define wxStyledTextCtrl_SetEdgeMode 3273
-#define wxStyledTextCtrl_GetEdgeMode 3274
-#define wxStyledTextCtrl_GetEdgeColour 3275
-#define wxStyledTextCtrl_SetEdgeColour 3276
-#define wxStyledTextCtrl_SearchAnchor 3277
-#define wxStyledTextCtrl_SearchNext 3278
-#define wxStyledTextCtrl_SearchPrev 3279
-#define wxStyledTextCtrl_LinesOnScreen 3280
-#define wxStyledTextCtrl_UsePopUp 3281
-#define wxStyledTextCtrl_SelectionIsRectangle 3282
-#define wxStyledTextCtrl_SetZoom 3283
-#define wxStyledTextCtrl_GetZoom 3284
-#define wxStyledTextCtrl_GetModEventMask 3285
-#define wxStyledTextCtrl_SetSTCFocus 3286
-#define wxStyledTextCtrl_GetSTCFocus 3287
-#define wxStyledTextCtrl_SetStatus 3288
-#define wxStyledTextCtrl_GetStatus 3289
-#define wxStyledTextCtrl_SetMouseDownCaptures 3290
-#define wxStyledTextCtrl_GetMouseDownCaptures 3291
-#define wxStyledTextCtrl_SetSTCCursor 3292
-#define wxStyledTextCtrl_GetSTCCursor 3293
-#define wxStyledTextCtrl_SetControlCharSymbol 3294
-#define wxStyledTextCtrl_GetControlCharSymbol 3295
-#define wxStyledTextCtrl_WordPartLeft 3296
-#define wxStyledTextCtrl_WordPartLeftExtend 3297
-#define wxStyledTextCtrl_WordPartRight 3298
-#define wxStyledTextCtrl_WordPartRightExtend 3299
-#define wxStyledTextCtrl_SetVisiblePolicy 3300
-#define wxStyledTextCtrl_DelLineLeft 3301
-#define wxStyledTextCtrl_DelLineRight 3302
-#define wxStyledTextCtrl_GetXOffset 3303
-#define wxStyledTextCtrl_ChooseCaretX 3304
-#define wxStyledTextCtrl_SetXCaretPolicy 3305
-#define wxStyledTextCtrl_SetYCaretPolicy 3306
-#define wxStyledTextCtrl_GetPrintWrapMode 3307
-#define wxStyledTextCtrl_SetHotspotActiveForeground 3308
-#define wxStyledTextCtrl_SetHotspotActiveBackground 3309
-#define wxStyledTextCtrl_SetHotspotActiveUnderline 3310
-#define wxStyledTextCtrl_SetHotspotSingleLine 3311
-#define wxStyledTextCtrl_ParaDownExtend 3312
-#define wxStyledTextCtrl_ParaUp 3313
-#define wxStyledTextCtrl_ParaUpExtend 3314
-#define wxStyledTextCtrl_PositionBefore 3315
-#define wxStyledTextCtrl_PositionAfter 3316
-#define wxStyledTextCtrl_CopyRange 3317
-#define wxStyledTextCtrl_CopyText 3318
-#define wxStyledTextCtrl_SetSelectionMode 3319
-#define wxStyledTextCtrl_GetSelectionMode 3320
-#define wxStyledTextCtrl_LineDownRectExtend 3321
-#define wxStyledTextCtrl_LineUpRectExtend 3322
-#define wxStyledTextCtrl_CharLeftRectExtend 3323
-#define wxStyledTextCtrl_CharRightRectExtend 3324
-#define wxStyledTextCtrl_HomeRectExtend 3325
-#define wxStyledTextCtrl_VCHomeRectExtend 3326
-#define wxStyledTextCtrl_LineEndRectExtend 3327
-#define wxStyledTextCtrl_PageUpRectExtend 3328
-#define wxStyledTextCtrl_PageDownRectExtend 3329
-#define wxStyledTextCtrl_StutteredPageUp 3330
-#define wxStyledTextCtrl_StutteredPageUpExtend 3331
-#define wxStyledTextCtrl_StutteredPageDown 3332
-#define wxStyledTextCtrl_StutteredPageDownExtend 3333
-#define wxStyledTextCtrl_WordLeftEnd 3334
-#define wxStyledTextCtrl_WordLeftEndExtend 3335
-#define wxStyledTextCtrl_WordRightEnd 3336
-#define wxStyledTextCtrl_WordRightEndExtend 3337
-#define wxStyledTextCtrl_SetWhitespaceChars 3338
-#define wxStyledTextCtrl_SetCharsDefault 3339
-#define wxStyledTextCtrl_AutoCompGetCurrent 3340
-#define wxStyledTextCtrl_Allocate 3341
-#define wxStyledTextCtrl_FindColumn 3342
-#define wxStyledTextCtrl_GetCaretSticky 3343
-#define wxStyledTextCtrl_SetCaretSticky 3344
-#define wxStyledTextCtrl_ToggleCaretSticky 3345
-#define wxStyledTextCtrl_SetPasteConvertEndings 3346
-#define wxStyledTextCtrl_GetPasteConvertEndings 3347
-#define wxStyledTextCtrl_SelectionDuplicate 3348
-#define wxStyledTextCtrl_SetCaretLineBackAlpha 3349
-#define wxStyledTextCtrl_GetCaretLineBackAlpha 3350
-#define wxStyledTextCtrl_StartRecord 3351
-#define wxStyledTextCtrl_StopRecord 3352
-#define wxStyledTextCtrl_SetLexer 3353
-#define wxStyledTextCtrl_GetLexer 3354
-#define wxStyledTextCtrl_Colourise 3355
-#define wxStyledTextCtrl_SetProperty 3356
-#define wxStyledTextCtrl_SetKeyWords 3357
-#define wxStyledTextCtrl_SetLexerLanguage 3358
-#define wxStyledTextCtrl_GetProperty 3359
-#define wxStyledTextCtrl_GetStyleBitsNeeded 3360
-#define wxStyledTextCtrl_GetCurrentLine 3361
-#define wxStyledTextCtrl_StyleSetSpec 3362
-#define wxStyledTextCtrl_StyleSetFont 3363
-#define wxStyledTextCtrl_StyleSetFontAttr 3364
-#define wxStyledTextCtrl_StyleSetCharacterSet 3365
-#define wxStyledTextCtrl_StyleSetFontEncoding 3366
-#define wxStyledTextCtrl_CmdKeyExecute 3367
-#define wxStyledTextCtrl_SetMargins 3368
-#define wxStyledTextCtrl_GetSelection 3369
-#define wxStyledTextCtrl_PointFromPosition 3370
-#define wxStyledTextCtrl_ScrollToLine 3371
-#define wxStyledTextCtrl_ScrollToColumn 3372
-#define wxStyledTextCtrl_SendMsg 3373
+#define wxTreeCtrl_IsTreeItemIdOk 2061
+#define wxTreeCtrl_PrependItem 2062
+#define wxTreeCtrl_ScrollTo 2063
+#define wxTreeCtrl_SelectItem_1 2064
+#define wxTreeCtrl_SelectItem_2 2065
+#define wxTreeCtrl_SetIndent 2066
+#define wxTreeCtrl_SetImageList 2067
+#define wxTreeCtrl_SetItemBackgroundColour 2068
+#define wxTreeCtrl_SetItemBold 2069
+#define wxTreeCtrl_SetItemData 2070
+#define wxTreeCtrl_SetItemDropHighlight 2071
+#define wxTreeCtrl_SetItemFont 2072
+#define wxTreeCtrl_SetItemHasChildren 2073
+#define wxTreeCtrl_SetItemImage_2 2074
+#define wxTreeCtrl_SetItemImage_3 2075
+#define wxTreeCtrl_SetItemText 2076
+#define wxTreeCtrl_SetItemTextColour 2077
+#define wxTreeCtrl_SetStateImageList 2078
+#define wxTreeCtrl_SetWindowStyle 2079
+#define wxTreeCtrl_SortChildren 2080
+#define wxTreeCtrl_Toggle 2081
+#define wxTreeCtrl_ToggleItemSelection 2082
+#define wxTreeCtrl_Unselect 2083
+#define wxTreeCtrl_UnselectAll 2084
+#define wxTreeCtrl_UnselectItem 2085
+#define wxScrollBar_new_0 2086
+#define wxScrollBar_new_3 2087
+#define wxScrollBar_destruct 2088
+#define wxScrollBar_Create 2089
+#define wxScrollBar_GetRange 2090
+#define wxScrollBar_GetPageSize 2091
+#define wxScrollBar_GetThumbPosition 2092
+#define wxScrollBar_GetThumbSize 2093
+#define wxScrollBar_SetThumbPosition 2094
+#define wxScrollBar_SetScrollbar 2095
+#define wxSpinButton_new_2 2097
+#define wxSpinButton_new_0 2098
+#define wxSpinButton_Create 2099
+#define wxSpinButton_GetMax 2100
+#define wxSpinButton_GetMin 2101
+#define wxSpinButton_GetValue 2102
+#define wxSpinButton_SetRange 2103
+#define wxSpinButton_SetValue 2104
+#define wxSpinButton_destroy 2105
+#define wxSpinCtrl_new_0 2106
+#define wxSpinCtrl_new_2 2107
+#define wxSpinCtrl_Create 2109
+#define wxSpinCtrl_SetValue_1_1 2112
+#define wxSpinCtrl_SetValue_1_0 2113
+#define wxSpinCtrl_GetValue 2115
+#define wxSpinCtrl_SetRange 2117
+#define wxSpinCtrl_SetSelection 2118
+#define wxSpinCtrl_GetMin 2120
+#define wxSpinCtrl_GetMax 2122
+#define wxSpinCtrl_destroy 2123
+#define wxStaticText_new_0 2124
+#define wxStaticText_new_4 2125
+#define wxStaticText_Create 2126
+#define wxStaticText_GetLabel 2127
+#define wxStaticText_SetLabel 2128
+#define wxStaticText_Wrap 2129
+#define wxStaticText_destroy 2130
+#define wxStaticBitmap_new_0 2131
+#define wxStaticBitmap_new_4 2132
+#define wxStaticBitmap_Create 2133
+#define wxStaticBitmap_GetBitmap 2134
+#define wxStaticBitmap_SetBitmap 2135
+#define wxStaticBitmap_destroy 2136
+#define wxRadioBox_new 2137
+#define wxRadioBox_destruct 2139
+#define wxRadioBox_Create 2140
+#define wxRadioBox_Enable_2 2141
+#define wxRadioBox_Enable_1 2142
+#define wxRadioBox_GetSelection 2143
+#define wxRadioBox_GetString 2144
+#define wxRadioBox_SetSelection 2145
+#define wxRadioBox_Show_2 2146
+#define wxRadioBox_Show_1 2147
+#define wxRadioBox_GetColumnCount 2148
+#define wxRadioBox_GetItemHelpText 2149
+#define wxRadioBox_GetItemToolTip 2150
+#define wxRadioBox_GetItemFromPoint 2152
+#define wxRadioBox_GetRowCount 2153
+#define wxRadioBox_IsItemEnabled 2154
+#define wxRadioBox_IsItemShown 2155
+#define wxRadioBox_SetItemHelpText 2156
+#define wxRadioBox_SetItemToolTip 2157
+#define wxRadioButton_new_0 2158
+#define wxRadioButton_new_4 2159
+#define wxRadioButton_Create 2160
+#define wxRadioButton_GetValue 2161
+#define wxRadioButton_SetValue 2162
+#define wxRadioButton_destroy 2163
+#define wxSlider_new_6 2165
+#define wxSlider_new_0 2166
+#define wxSlider_Create 2167
+#define wxSlider_GetLineSize 2168
+#define wxSlider_GetMax 2169
+#define wxSlider_GetMin 2170
+#define wxSlider_GetPageSize 2171
+#define wxSlider_GetThumbLength 2172
+#define wxSlider_GetValue 2173
+#define wxSlider_SetLineSize 2174
+#define wxSlider_SetPageSize 2175
+#define wxSlider_SetRange 2176
+#define wxSlider_SetThumbLength 2177
+#define wxSlider_SetValue 2178
+#define wxSlider_destroy 2179
+#define wxDialog_new_4 2181
+#define wxDialog_new_0 2182
+#define wxDialog_destruct 2184
+#define wxDialog_Create 2185
+#define wxDialog_CreateButtonSizer 2186
+#define wxDialog_CreateStdDialogButtonSizer 2187
+#define wxDialog_EndModal 2188
+#define wxDialog_GetAffirmativeId 2189
+#define wxDialog_GetReturnCode 2190
+#define wxDialog_IsModal 2191
+#define wxDialog_SetAffirmativeId 2192
+#define wxDialog_SetReturnCode 2193
+#define wxDialog_Show 2194
+#define wxDialog_ShowModal 2195
+#define wxColourDialog_new_0 2196
+#define wxColourDialog_new_2 2197
+#define wxColourDialog_destruct 2198
+#define wxColourDialog_Create 2199
+#define wxColourDialog_GetColourData 2200
+#define wxColourData_new_0 2201
+#define wxColourData_new_1 2202
+#define wxColourData_destruct 2203
+#define wxColourData_GetChooseFull 2204
+#define wxColourData_GetColour 2205
+#define wxColourData_GetCustomColour 2207
+#define wxColourData_SetChooseFull 2208
+#define wxColourData_SetColour 2209
+#define wxColourData_SetCustomColour 2210
+#define wxPalette_new_0 2211
+#define wxPalette_new_4 2212
+#define wxPalette_destruct 2214
+#define wxPalette_Create 2215
+#define wxPalette_GetColoursCount 2216
+#define wxPalette_GetPixel 2217
+#define wxPalette_GetRGB 2218
+#define wxPalette_IsOk 2219
+#define wxDirDialog_new 2223
+#define wxDirDialog_destruct 2224
+#define wxDirDialog_GetPath 2225
+#define wxDirDialog_GetMessage 2226
+#define wxDirDialog_SetMessage 2227
+#define wxDirDialog_SetPath 2228
+#define wxFileDialog_new 2232
+#define wxFileDialog_destruct 2233
+#define wxFileDialog_GetDirectory 2234
+#define wxFileDialog_GetFilename 2235
+#define wxFileDialog_GetFilenames 2236
+#define wxFileDialog_GetFilterIndex 2237
+#define wxFileDialog_GetMessage 2238
+#define wxFileDialog_GetPath 2239
+#define wxFileDialog_GetPaths 2240
+#define wxFileDialog_GetWildcard 2241
+#define wxFileDialog_SetDirectory 2242
+#define wxFileDialog_SetFilename 2243
+#define wxFileDialog_SetFilterIndex 2244
+#define wxFileDialog_SetMessage 2245
+#define wxFileDialog_SetPath 2246
+#define wxFileDialog_SetWildcard 2247
+#define wxPickerBase_SetInternalMargin 2248
+#define wxPickerBase_GetInternalMargin 2249
+#define wxPickerBase_SetTextCtrlProportion 2250
+#define wxPickerBase_SetPickerCtrlProportion 2251
+#define wxPickerBase_GetTextCtrlProportion 2252
+#define wxPickerBase_GetPickerCtrlProportion 2253
+#define wxPickerBase_HasTextCtrl 2254
+#define wxPickerBase_GetTextCtrl 2255
+#define wxPickerBase_IsTextCtrlGrowable 2256
+#define wxPickerBase_SetPickerCtrlGrowable 2257
+#define wxPickerBase_SetTextCtrlGrowable 2258
+#define wxPickerBase_IsPickerCtrlGrowable 2259
+#define wxFilePickerCtrl_new_0 2260
+#define wxFilePickerCtrl_new_3 2261
+#define wxFilePickerCtrl_Create 2262
+#define wxFilePickerCtrl_GetPath 2263
+#define wxFilePickerCtrl_SetPath 2264
+#define wxFilePickerCtrl_destroy 2265
+#define wxDirPickerCtrl_new_0 2266
+#define wxDirPickerCtrl_new_3 2267
+#define wxDirPickerCtrl_Create 2268
+#define wxDirPickerCtrl_GetPath 2269
+#define wxDirPickerCtrl_SetPath 2270
+#define wxDirPickerCtrl_destroy 2271
+#define wxColourPickerCtrl_new_0 2272
+#define wxColourPickerCtrl_new_3 2273
+#define wxColourPickerCtrl_Create 2274
+#define wxColourPickerCtrl_GetColour 2275
+#define wxColourPickerCtrl_SetColour_1_1 2276
+#define wxColourPickerCtrl_SetColour_1_0 2277
+#define wxColourPickerCtrl_destroy 2278
+#define wxDatePickerCtrl_new_0 2279
+#define wxDatePickerCtrl_new_3 2280
+#define wxDatePickerCtrl_GetRange 2281
+#define wxDatePickerCtrl_GetValue 2282
+#define wxDatePickerCtrl_SetRange 2283
+#define wxDatePickerCtrl_SetValue 2284
+#define wxDatePickerCtrl_destroy 2285
+#define wxFontPickerCtrl_new_0 2286
+#define wxFontPickerCtrl_new_3 2287
+#define wxFontPickerCtrl_Create 2288
+#define wxFontPickerCtrl_GetSelectedFont 2289
+#define wxFontPickerCtrl_SetSelectedFont 2290
+#define wxFontPickerCtrl_GetMaxPointSize 2291
+#define wxFontPickerCtrl_SetMaxPointSize 2292
+#define wxFontPickerCtrl_destroy 2293
+#define wxFindReplaceDialog_new_0 2296
+#define wxFindReplaceDialog_new_4 2297
+#define wxFindReplaceDialog_destruct 2298
+#define wxFindReplaceDialog_Create 2299
+#define wxFindReplaceDialog_GetData 2300
+#define wxFindReplaceData_new_0 2301
+#define wxFindReplaceData_new_1 2302
+#define wxFindReplaceData_GetFindString 2303
+#define wxFindReplaceData_GetReplaceString 2304
+#define wxFindReplaceData_GetFlags 2305
+#define wxFindReplaceData_SetFlags 2306
+#define wxFindReplaceData_SetFindString 2307
+#define wxFindReplaceData_SetReplaceString 2308
+#define wxFindReplaceData_destroy 2309
+#define wxMultiChoiceDialog_new_0 2310
+#define wxMultiChoiceDialog_new_5 2312
+#define wxMultiChoiceDialog_GetSelections 2313
+#define wxMultiChoiceDialog_SetSelections 2314
+#define wxMultiChoiceDialog_destroy 2315
+#define wxSingleChoiceDialog_new_0 2316
+#define wxSingleChoiceDialog_new_5 2318
+#define wxSingleChoiceDialog_GetSelection 2319
+#define wxSingleChoiceDialog_GetStringSelection 2320
+#define wxSingleChoiceDialog_SetSelection 2321
+#define wxSingleChoiceDialog_destroy 2322
+#define wxTextEntryDialog_new 2323
+#define wxTextEntryDialog_GetValue 2324
+#define wxTextEntryDialog_SetValue 2325
+#define wxTextEntryDialog_destroy 2326
+#define wxPasswordEntryDialog_new 2327
+#define wxPasswordEntryDialog_destroy 2328
+#define wxFontData_new_0 2329
+#define wxFontData_new_1 2330
+#define wxFontData_destruct 2331
+#define wxFontData_EnableEffects 2332
+#define wxFontData_GetAllowSymbols 2333
+#define wxFontData_GetColour 2334
+#define wxFontData_GetChosenFont 2335
+#define wxFontData_GetEnableEffects 2336
+#define wxFontData_GetInitialFont 2337
+#define wxFontData_GetShowHelp 2338
+#define wxFontData_SetAllowSymbols 2339
+#define wxFontData_SetChosenFont 2340
+#define wxFontData_SetColour 2341
+#define wxFontData_SetInitialFont 2342
+#define wxFontData_SetRange 2343
+#define wxFontData_SetShowHelp 2344
+#define wxFontDialog_new_0 2348
+#define wxFontDialog_new_2 2350
+#define wxFontDialog_Create 2352
+#define wxFontDialog_GetFontData 2353
+#define wxFontDialog_destroy 2355
+#define wxProgressDialog_new 2356
+#define wxProgressDialog_destruct 2357
+#define wxProgressDialog_Resume 2358
+#define wxProgressDialog_Update_2 2359
+#define wxProgressDialog_Update_0 2360
+#define wxMessageDialog_new 2361
+#define wxMessageDialog_destruct 2362
+#define wxPageSetupDialog_new 2363
+#define wxPageSetupDialog_destruct 2364
+#define wxPageSetupDialog_GetPageSetupData 2365
+#define wxPageSetupDialog_ShowModal 2366
+#define wxPageSetupDialogData_new_0 2367
+#define wxPageSetupDialogData_new_1_0 2368
+#define wxPageSetupDialogData_new_1_1 2369
+#define wxPageSetupDialogData_destruct 2370
+#define wxPageSetupDialogData_EnableHelp 2371
+#define wxPageSetupDialogData_EnableMargins 2372
+#define wxPageSetupDialogData_EnableOrientation 2373
+#define wxPageSetupDialogData_EnablePaper 2374
+#define wxPageSetupDialogData_EnablePrinter 2375
+#define wxPageSetupDialogData_GetDefaultMinMargins 2376
+#define wxPageSetupDialogData_GetEnableMargins 2377
+#define wxPageSetupDialogData_GetEnableOrientation 2378
+#define wxPageSetupDialogData_GetEnablePaper 2379
+#define wxPageSetupDialogData_GetEnablePrinter 2380
+#define wxPageSetupDialogData_GetEnableHelp 2381
+#define wxPageSetupDialogData_GetDefaultInfo 2382
+#define wxPageSetupDialogData_GetMarginTopLeft 2383
+#define wxPageSetupDialogData_GetMarginBottomRight 2384
+#define wxPageSetupDialogData_GetMinMarginTopLeft 2385
+#define wxPageSetupDialogData_GetMinMarginBottomRight 2386
+#define wxPageSetupDialogData_GetPaperId 2387
+#define wxPageSetupDialogData_GetPaperSize 2388
+#define wxPageSetupDialogData_GetPrintData 2390
+#define wxPageSetupDialogData_IsOk 2391
+#define wxPageSetupDialogData_SetDefaultInfo 2392
+#define wxPageSetupDialogData_SetDefaultMinMargins 2393
+#define wxPageSetupDialogData_SetMarginTopLeft 2394
+#define wxPageSetupDialogData_SetMarginBottomRight 2395
+#define wxPageSetupDialogData_SetMinMarginTopLeft 2396
+#define wxPageSetupDialogData_SetMinMarginBottomRight 2397
+#define wxPageSetupDialogData_SetPaperId 2398
+#define wxPageSetupDialogData_SetPaperSize_1_1 2399
+#define wxPageSetupDialogData_SetPaperSize_1_0 2400
+#define wxPageSetupDialogData_SetPrintData 2401
+#define wxPrintDialog_new_2_0 2402
+#define wxPrintDialog_new_2_1 2403
+#define wxPrintDialog_destruct 2404
+#define wxPrintDialog_GetPrintDialogData 2405
+#define wxPrintDialog_GetPrintDC 2406
+#define wxPrintDialogData_new_0 2407
+#define wxPrintDialogData_new_1_1 2408
+#define wxPrintDialogData_new_1_0 2409
+#define wxPrintDialogData_destruct 2410
+#define wxPrintDialogData_EnableHelp 2411
+#define wxPrintDialogData_EnablePageNumbers 2412
+#define wxPrintDialogData_EnablePrintToFile 2413
+#define wxPrintDialogData_EnableSelection 2414
+#define wxPrintDialogData_GetAllPages 2415
+#define wxPrintDialogData_GetCollate 2416
+#define wxPrintDialogData_GetFromPage 2417
+#define wxPrintDialogData_GetMaxPage 2418
+#define wxPrintDialogData_GetMinPage 2419
+#define wxPrintDialogData_GetNoCopies 2420
+#define wxPrintDialogData_GetPrintData 2421
+#define wxPrintDialogData_GetPrintToFile 2422
+#define wxPrintDialogData_GetSelection 2423
+#define wxPrintDialogData_GetToPage 2424
+#define wxPrintDialogData_IsOk 2425
+#define wxPrintDialogData_SetCollate 2426
+#define wxPrintDialogData_SetFromPage 2427
+#define wxPrintDialogData_SetMaxPage 2428
+#define wxPrintDialogData_SetMinPage 2429
+#define wxPrintDialogData_SetNoCopies 2430
+#define wxPrintDialogData_SetPrintData 2431
+#define wxPrintDialogData_SetPrintToFile 2432
+#define wxPrintDialogData_SetSelection 2433
+#define wxPrintDialogData_SetToPage 2434
+#define wxPrintData_new_0 2435
+#define wxPrintData_new_1 2436
+#define wxPrintData_destruct 2437
+#define wxPrintData_GetCollate 2438
+#define wxPrintData_GetBin 2439
+#define wxPrintData_GetColour 2440
+#define wxPrintData_GetDuplex 2441
+#define wxPrintData_GetNoCopies 2442
+#define wxPrintData_GetOrientation 2443
+#define wxPrintData_GetPaperId 2444
+#define wxPrintData_GetPrinterName 2445
+#define wxPrintData_GetQuality 2446
+#define wxPrintData_IsOk 2447
+#define wxPrintData_SetBin 2448
+#define wxPrintData_SetCollate 2449
+#define wxPrintData_SetColour 2450
+#define wxPrintData_SetDuplex 2451
+#define wxPrintData_SetNoCopies 2452
+#define wxPrintData_SetOrientation 2453
+#define wxPrintData_SetPaperId 2454
+#define wxPrintData_SetPrinterName 2455
+#define wxPrintData_SetQuality 2456
+#define wxPrintPreview_new_2 2459
+#define wxPrintPreview_new_3 2460
+#define wxPrintPreview_destruct 2462
+#define wxPrintPreview_GetCanvas 2463
+#define wxPrintPreview_GetCurrentPage 2464
+#define wxPrintPreview_GetFrame 2465
+#define wxPrintPreview_GetMaxPage 2466
+#define wxPrintPreview_GetMinPage 2467
+#define wxPrintPreview_GetPrintout 2468
+#define wxPrintPreview_GetPrintoutForPrinting 2469
+#define wxPrintPreview_IsOk 2470
+#define wxPrintPreview_PaintPage 2471
+#define wxPrintPreview_Print 2472
+#define wxPrintPreview_RenderPage 2473
+#define wxPrintPreview_SetCanvas 2474
+#define wxPrintPreview_SetCurrentPage 2475
+#define wxPrintPreview_SetFrame 2476
+#define wxPrintPreview_SetPrintout 2477
+#define wxPrintPreview_SetZoom 2478
+#define wxPreviewFrame_new 2479
+#define wxPreviewFrame_destruct 2480
+#define wxPreviewFrame_CreateControlBar 2481
+#define wxPreviewFrame_CreateCanvas 2482
+#define wxPreviewFrame_Initialize 2483
+#define wxPreviewFrame_OnCloseWindow 2484
+#define wxPreviewControlBar_new 2485
+#define wxPreviewControlBar_destruct 2486
+#define wxPreviewControlBar_CreateButtons 2487
+#define wxPreviewControlBar_GetPrintPreview 2488
+#define wxPreviewControlBar_GetZoomControl 2489
+#define wxPreviewControlBar_SetZoomControl 2490
+#define wxPrinter_new 2492
+#define wxPrinter_CreateAbortWindow 2493
+#define wxPrinter_GetAbort 2494
+#define wxPrinter_GetLastError 2495
+#define wxPrinter_GetPrintDialogData 2496
+#define wxPrinter_Print 2497
+#define wxPrinter_PrintDialog 2498
+#define wxPrinter_ReportError 2499
+#define wxPrinter_Setup 2500
+#define wxPrinter_destroy 2501
+#define wxXmlResource_new_1 2502
+#define wxXmlResource_new_2 2503
+#define wxXmlResource_destruct 2504
+#define wxXmlResource_AttachUnknownControl 2505
+#define wxXmlResource_ClearHandlers 2506
+#define wxXmlResource_CompareVersion 2507
+#define wxXmlResource_Get 2508
+#define wxXmlResource_GetFlags 2509
+#define wxXmlResource_GetVersion 2510
+#define wxXmlResource_GetXRCID 2511
+#define wxXmlResource_InitAllHandlers 2512
+#define wxXmlResource_Load 2513
+#define wxXmlResource_LoadBitmap 2514
+#define wxXmlResource_LoadDialog_2 2515
+#define wxXmlResource_LoadDialog_3 2516
+#define wxXmlResource_LoadFrame_2 2517
+#define wxXmlResource_LoadFrame_3 2518
+#define wxXmlResource_LoadIcon 2519
+#define wxXmlResource_LoadMenu 2520
+#define wxXmlResource_LoadMenuBar_2 2521
+#define wxXmlResource_LoadMenuBar_1 2522
+#define wxXmlResource_LoadPanel_2 2523
+#define wxXmlResource_LoadPanel_3 2524
+#define wxXmlResource_LoadToolBar 2525
+#define wxXmlResource_Set 2526
+#define wxXmlResource_SetFlags 2527
+#define wxXmlResource_Unload 2528
+#define wxXmlResource_xrcctrl 2529
+#define wxHtmlEasyPrinting_new 2530
+#define wxHtmlEasyPrinting_destruct 2531
+#define wxHtmlEasyPrinting_GetPrintData 2532
+#define wxHtmlEasyPrinting_GetPageSetupData 2533
+#define wxHtmlEasyPrinting_PreviewFile 2534
+#define wxHtmlEasyPrinting_PreviewText 2535
+#define wxHtmlEasyPrinting_PrintFile 2536
+#define wxHtmlEasyPrinting_PrintText 2537
+#define wxHtmlEasyPrinting_PageSetup 2538
+#define wxHtmlEasyPrinting_SetFonts 2539
+#define wxHtmlEasyPrinting_SetHeader 2540
+#define wxHtmlEasyPrinting_SetFooter 2541
+#define wxGLCanvas_new_2 2543
+#define wxGLCanvas_new_3_1 2544
+#define wxGLCanvas_new_3_0 2545
+#define wxGLCanvas_GetContext 2546
+#define wxGLCanvas_SetCurrent 2548
+#define wxGLCanvas_SwapBuffers 2549
+#define wxGLCanvas_destroy 2550
+#define wxAuiManager_new 2551
+#define wxAuiManager_destruct 2552
+#define wxAuiManager_AddPane_2_1 2553
+#define wxAuiManager_AddPane_3 2554
+#define wxAuiManager_AddPane_2_0 2555
+#define wxAuiManager_DetachPane 2556
+#define wxAuiManager_GetAllPanes 2557
+#define wxAuiManager_GetArtProvider 2558
+#define wxAuiManager_GetDockSizeConstraint 2559
+#define wxAuiManager_GetFlags 2560
+#define wxAuiManager_GetManagedWindow 2561
+#define wxAuiManager_GetManager 2562
+#define wxAuiManager_GetPane_1_1 2563
+#define wxAuiManager_GetPane_1_0 2564
+#define wxAuiManager_HideHint 2565
+#define wxAuiManager_InsertPane 2566
+#define wxAuiManager_LoadPaneInfo 2567
+#define wxAuiManager_LoadPerspective 2568
+#define wxAuiManager_SavePaneInfo 2569
+#define wxAuiManager_SavePerspective 2570
+#define wxAuiManager_SetArtProvider 2571
+#define wxAuiManager_SetDockSizeConstraint 2572
+#define wxAuiManager_SetFlags 2573
+#define wxAuiManager_SetManagedWindow 2574
+#define wxAuiManager_ShowHint 2575
+#define wxAuiManager_UnInit 2576
+#define wxAuiManager_Update 2577
+#define wxAuiPaneInfo_new_0 2578
+#define wxAuiPaneInfo_new_1 2579
+#define wxAuiPaneInfo_destruct 2580
+#define wxAuiPaneInfo_BestSize_1 2581
+#define wxAuiPaneInfo_BestSize_2 2582
+#define wxAuiPaneInfo_Bottom 2583
+#define wxAuiPaneInfo_BottomDockable 2584
+#define wxAuiPaneInfo_Caption 2585
+#define wxAuiPaneInfo_CaptionVisible 2586
+#define wxAuiPaneInfo_Centre 2587
+#define wxAuiPaneInfo_CentrePane 2588
+#define wxAuiPaneInfo_CloseButton 2589
+#define wxAuiPaneInfo_DefaultPane 2590
+#define wxAuiPaneInfo_DestroyOnClose 2591
+#define wxAuiPaneInfo_Direction 2592
+#define wxAuiPaneInfo_Dock 2593
+#define wxAuiPaneInfo_Dockable 2594
+#define wxAuiPaneInfo_Fixed 2595
+#define wxAuiPaneInfo_Float 2596
+#define wxAuiPaneInfo_Floatable 2597
+#define wxAuiPaneInfo_FloatingPosition_1 2598
+#define wxAuiPaneInfo_FloatingPosition_2 2599
+#define wxAuiPaneInfo_FloatingSize_1 2600
+#define wxAuiPaneInfo_FloatingSize_2 2601
+#define wxAuiPaneInfo_Gripper 2602
+#define wxAuiPaneInfo_GripperTop 2603
+#define wxAuiPaneInfo_HasBorder 2604
+#define wxAuiPaneInfo_HasCaption 2605
+#define wxAuiPaneInfo_HasCloseButton 2606
+#define wxAuiPaneInfo_HasFlag 2607
+#define wxAuiPaneInfo_HasGripper 2608
+#define wxAuiPaneInfo_HasGripperTop 2609
+#define wxAuiPaneInfo_HasMaximizeButton 2610
+#define wxAuiPaneInfo_HasMinimizeButton 2611
+#define wxAuiPaneInfo_HasPinButton 2612
+#define wxAuiPaneInfo_Hide 2613
+#define wxAuiPaneInfo_IsBottomDockable 2614
+#define wxAuiPaneInfo_IsDocked 2615
+#define wxAuiPaneInfo_IsFixed 2616
+#define wxAuiPaneInfo_IsFloatable 2617
+#define wxAuiPaneInfo_IsFloating 2618
+#define wxAuiPaneInfo_IsLeftDockable 2619
+#define wxAuiPaneInfo_IsMovable 2620
+#define wxAuiPaneInfo_IsOk 2621
+#define wxAuiPaneInfo_IsResizable 2622
+#define wxAuiPaneInfo_IsRightDockable 2623
+#define wxAuiPaneInfo_IsShown 2624
+#define wxAuiPaneInfo_IsToolbar 2625
+#define wxAuiPaneInfo_IsTopDockable 2626
+#define wxAuiPaneInfo_Layer 2627
+#define wxAuiPaneInfo_Left 2628
+#define wxAuiPaneInfo_LeftDockable 2629
+#define wxAuiPaneInfo_MaxSize_1 2630
+#define wxAuiPaneInfo_MaxSize_2 2631
+#define wxAuiPaneInfo_MaximizeButton 2632
+#define wxAuiPaneInfo_MinSize_1 2633
+#define wxAuiPaneInfo_MinSize_2 2634
+#define wxAuiPaneInfo_MinimizeButton 2635
+#define wxAuiPaneInfo_Movable 2636
+#define wxAuiPaneInfo_Name 2637
+#define wxAuiPaneInfo_PaneBorder 2638
+#define wxAuiPaneInfo_PinButton 2639
+#define wxAuiPaneInfo_Position 2640
+#define wxAuiPaneInfo_Resizable 2641
+#define wxAuiPaneInfo_Right 2642
+#define wxAuiPaneInfo_RightDockable 2643
+#define wxAuiPaneInfo_Row 2644
+#define wxAuiPaneInfo_SafeSet 2645
+#define wxAuiPaneInfo_SetFlag 2646
+#define wxAuiPaneInfo_Show 2647
+#define wxAuiPaneInfo_ToolbarPane 2648
+#define wxAuiPaneInfo_Top 2649
+#define wxAuiPaneInfo_TopDockable 2650
+#define wxAuiPaneInfo_Window 2651
+#define wxAuiNotebook_new_0 2652
+#define wxAuiNotebook_new_2 2653
+#define wxAuiNotebook_AddPage 2654
+#define wxAuiNotebook_Create 2655
+#define wxAuiNotebook_DeletePage 2656
+#define wxAuiNotebook_GetArtProvider 2657
+#define wxAuiNotebook_GetPage 2658
+#define wxAuiNotebook_GetPageBitmap 2659
+#define wxAuiNotebook_GetPageCount 2660
+#define wxAuiNotebook_GetPageIndex 2661
+#define wxAuiNotebook_GetPageText 2662
+#define wxAuiNotebook_GetSelection 2663
+#define wxAuiNotebook_InsertPage 2664
+#define wxAuiNotebook_RemovePage 2665
+#define wxAuiNotebook_SetArtProvider 2666
+#define wxAuiNotebook_SetFont 2667
+#define wxAuiNotebook_SetPageBitmap 2668
+#define wxAuiNotebook_SetPageText 2669
+#define wxAuiNotebook_SetSelection 2670
+#define wxAuiNotebook_SetTabCtrlHeight 2671
+#define wxAuiNotebook_SetUniformBitmapSize 2672
+#define wxAuiNotebook_destroy 2673
+#define wxMDIParentFrame_new_0 2674
+#define wxMDIParentFrame_new_4 2675
+#define wxMDIParentFrame_destruct 2676
+#define wxMDIParentFrame_ActivateNext 2677
+#define wxMDIParentFrame_ActivatePrevious 2678
+#define wxMDIParentFrame_ArrangeIcons 2679
+#define wxMDIParentFrame_Cascade 2680
+#define wxMDIParentFrame_Create 2681
+#define wxMDIParentFrame_GetActiveChild 2682
+#define wxMDIParentFrame_GetClientWindow 2683
+#define wxMDIParentFrame_Tile 2684
+#define wxMDIChildFrame_new_0 2685
+#define wxMDIChildFrame_new_4 2686
+#define wxMDIChildFrame_destruct 2687
+#define wxMDIChildFrame_Activate 2688
+#define wxMDIChildFrame_Create 2689
+#define wxMDIChildFrame_Maximize 2690
+#define wxMDIChildFrame_Restore 2691
+#define wxMDIClientWindow_new_0 2692
+#define wxMDIClientWindow_new_2 2693
+#define wxMDIClientWindow_destruct 2694
+#define wxMDIClientWindow_CreateClient 2695
+#define wxLayoutAlgorithm_new 2696
+#define wxLayoutAlgorithm_LayoutFrame 2697
+#define wxLayoutAlgorithm_LayoutMDIFrame 2698
+#define wxLayoutAlgorithm_LayoutWindow 2699
+#define wxLayoutAlgorithm_destroy 2700
+#define wxEvent_GetId 2701
+#define wxEvent_GetSkipped 2702
+#define wxEvent_GetTimestamp 2703
+#define wxEvent_IsCommandEvent 2704
+#define wxEvent_ResumePropagation 2705
+#define wxEvent_ShouldPropagate 2706
+#define wxEvent_Skip 2707
+#define wxEvent_StopPropagation 2708
+#define wxCommandEvent_getClientData 2709
+#define wxCommandEvent_GetExtraLong 2710
+#define wxCommandEvent_GetInt 2711
+#define wxCommandEvent_GetSelection 2712
+#define wxCommandEvent_GetString 2713
+#define wxCommandEvent_IsChecked 2714
+#define wxCommandEvent_IsSelection 2715
+#define wxCommandEvent_SetInt 2716
+#define wxCommandEvent_SetString 2717
+#define wxScrollEvent_GetOrientation 2718
+#define wxScrollEvent_GetPosition 2719
+#define wxScrollWinEvent_GetOrientation 2720
+#define wxScrollWinEvent_GetPosition 2721
+#define wxMouseEvent_AltDown 2722
+#define wxMouseEvent_Button 2723
+#define wxMouseEvent_ButtonDClick 2724
+#define wxMouseEvent_ButtonDown 2725
+#define wxMouseEvent_ButtonUp 2726
+#define wxMouseEvent_CmdDown 2727
+#define wxMouseEvent_ControlDown 2728
+#define wxMouseEvent_Dragging 2729
+#define wxMouseEvent_Entering 2730
+#define wxMouseEvent_GetButton 2731
+#define wxMouseEvent_GetPosition 2734
+#define wxMouseEvent_GetLogicalPosition 2735
+#define wxMouseEvent_GetLinesPerAction 2736
+#define wxMouseEvent_GetWheelRotation 2737
+#define wxMouseEvent_GetWheelDelta 2738
+#define wxMouseEvent_GetX 2739
+#define wxMouseEvent_GetY 2740
+#define wxMouseEvent_IsButton 2741
+#define wxMouseEvent_IsPageScroll 2742
+#define wxMouseEvent_Leaving 2743
+#define wxMouseEvent_LeftDClick 2744
+#define wxMouseEvent_LeftDown 2745
+#define wxMouseEvent_LeftIsDown 2746
+#define wxMouseEvent_LeftUp 2747
+#define wxMouseEvent_MetaDown 2748
+#define wxMouseEvent_MiddleDClick 2749
+#define wxMouseEvent_MiddleDown 2750
+#define wxMouseEvent_MiddleIsDown 2751
+#define wxMouseEvent_MiddleUp 2752
+#define wxMouseEvent_Moving 2753
+#define wxMouseEvent_RightDClick 2754
+#define wxMouseEvent_RightDown 2755
+#define wxMouseEvent_RightIsDown 2756
+#define wxMouseEvent_RightUp 2757
+#define wxMouseEvent_ShiftDown 2758
+#define wxSetCursorEvent_GetCursor 2759
+#define wxSetCursorEvent_GetX 2760
+#define wxSetCursorEvent_GetY 2761
+#define wxSetCursorEvent_HasCursor 2762
+#define wxSetCursorEvent_SetCursor 2763
+#define wxKeyEvent_AltDown 2764
+#define wxKeyEvent_CmdDown 2765
+#define wxKeyEvent_ControlDown 2766
+#define wxKeyEvent_GetKeyCode 2767
+#define wxKeyEvent_GetModifiers 2768
+#define wxKeyEvent_GetPosition 2771
+#define wxKeyEvent_GetRawKeyCode 2772
+#define wxKeyEvent_GetRawKeyFlags 2773
+#define wxKeyEvent_GetUnicodeKey 2774
+#define wxKeyEvent_GetX 2775
+#define wxKeyEvent_GetY 2776
+#define wxKeyEvent_HasModifiers 2777
+#define wxKeyEvent_MetaDown 2778
+#define wxKeyEvent_ShiftDown 2779
+#define wxSizeEvent_GetSize 2780
+#define wxMoveEvent_GetPosition 2781
+#define wxEraseEvent_GetDC 2782
+#define wxFocusEvent_GetWindow 2783
+#define wxChildFocusEvent_GetWindow 2784
+#define wxMenuEvent_GetMenu 2785
+#define wxMenuEvent_GetMenuId 2786
+#define wxMenuEvent_IsPopup 2787
+#define wxCloseEvent_CanVeto 2788
+#define wxCloseEvent_GetLoggingOff 2789
+#define wxCloseEvent_SetCanVeto 2790
+#define wxCloseEvent_SetLoggingOff 2791
+#define wxCloseEvent_Veto 2792
+#define wxShowEvent_SetShow 2793
+#define wxShowEvent_GetShow 2794
+#define wxIconizeEvent_Iconized 2795
+#define wxJoystickEvent_ButtonDown 2796
+#define wxJoystickEvent_ButtonIsDown 2797
+#define wxJoystickEvent_ButtonUp 2798
+#define wxJoystickEvent_GetButtonChange 2799
+#define wxJoystickEvent_GetButtonState 2800
+#define wxJoystickEvent_GetJoystick 2801
+#define wxJoystickEvent_GetPosition 2802
+#define wxJoystickEvent_GetZPosition 2803
+#define wxJoystickEvent_IsButton 2804
+#define wxJoystickEvent_IsMove 2805
+#define wxJoystickEvent_IsZMove 2806
+#define wxUpdateUIEvent_CanUpdate 2807
+#define wxUpdateUIEvent_Check 2808
+#define wxUpdateUIEvent_Enable 2809
+#define wxUpdateUIEvent_Show 2810
+#define wxUpdateUIEvent_GetChecked 2811
+#define wxUpdateUIEvent_GetEnabled 2812
+#define wxUpdateUIEvent_GetShown 2813
+#define wxUpdateUIEvent_GetSetChecked 2814
+#define wxUpdateUIEvent_GetSetEnabled 2815
+#define wxUpdateUIEvent_GetSetShown 2816
+#define wxUpdateUIEvent_GetSetText 2817
+#define wxUpdateUIEvent_GetText 2818
+#define wxUpdateUIEvent_GetMode 2819
+#define wxUpdateUIEvent_GetUpdateInterval 2820
+#define wxUpdateUIEvent_ResetUpdateTime 2821
+#define wxUpdateUIEvent_SetMode 2822
+#define wxUpdateUIEvent_SetText 2823
+#define wxUpdateUIEvent_SetUpdateInterval 2824
+#define wxMouseCaptureChangedEvent_GetCapturedWindow 2825
+#define wxPaletteChangedEvent_SetChangedWindow 2826
+#define wxPaletteChangedEvent_GetChangedWindow 2827
+#define wxQueryNewPaletteEvent_SetPaletteRealized 2828
+#define wxQueryNewPaletteEvent_GetPaletteRealized 2829
+#define wxNavigationKeyEvent_GetDirection 2830
+#define wxNavigationKeyEvent_SetDirection 2831
+#define wxNavigationKeyEvent_IsWindowChange 2832
+#define wxNavigationKeyEvent_SetWindowChange 2833
+#define wxNavigationKeyEvent_IsFromTab 2834
+#define wxNavigationKeyEvent_SetFromTab 2835
+#define wxNavigationKeyEvent_GetCurrentFocus 2836
+#define wxNavigationKeyEvent_SetCurrentFocus 2837
+#define wxHelpEvent_GetOrigin 2838
+#define wxHelpEvent_GetPosition 2839
+#define wxHelpEvent_SetOrigin 2840
+#define wxHelpEvent_SetPosition 2841
+#define wxContextMenuEvent_GetPosition 2842
+#define wxContextMenuEvent_SetPosition 2843
+#define wxIdleEvent_CanSend 2844
+#define wxIdleEvent_GetMode 2845
+#define wxIdleEvent_RequestMore 2846
+#define wxIdleEvent_MoreRequested 2847
+#define wxIdleEvent_SetMode 2848
+#define wxGridEvent_AltDown 2849
+#define wxGridEvent_ControlDown 2850
+#define wxGridEvent_GetCol 2851
+#define wxGridEvent_GetPosition 2852
+#define wxGridEvent_GetRow 2853
+#define wxGridEvent_MetaDown 2854
+#define wxGridEvent_Selecting 2855
+#define wxGridEvent_ShiftDown 2856
+#define wxNotifyEvent_Allow 2857
+#define wxNotifyEvent_IsAllowed 2858
+#define wxNotifyEvent_Veto 2859
+#define wxSashEvent_GetEdge 2860
+#define wxSashEvent_GetDragRect 2861
+#define wxSashEvent_GetDragStatus 2862
+#define wxListEvent_GetCacheFrom 2863
+#define wxListEvent_GetCacheTo 2864
+#define wxListEvent_GetKeyCode 2865
+#define wxListEvent_GetIndex 2866
+#define wxListEvent_GetColumn 2867
+#define wxListEvent_GetPoint 2868
+#define wxListEvent_GetLabel 2869
+#define wxListEvent_GetText 2870
+#define wxListEvent_GetImage 2871
+#define wxListEvent_GetData 2872
+#define wxListEvent_GetMask 2873
+#define wxListEvent_GetItem 2874
+#define wxListEvent_IsEditCancelled 2875
+#define wxDateEvent_GetDate 2876
+#define wxCalendarEvent_GetWeekDay 2877
+#define wxFileDirPickerEvent_GetPath 2878
+#define wxColourPickerEvent_GetColour 2879
+#define wxFontPickerEvent_GetFont 2880
+#define wxStyledTextEvent_GetPosition 2881
+#define wxStyledTextEvent_GetKey 2882
+#define wxStyledTextEvent_GetModifiers 2883
+#define wxStyledTextEvent_GetModificationType 2884
+#define wxStyledTextEvent_GetText 2885
+#define wxStyledTextEvent_GetLength 2886
+#define wxStyledTextEvent_GetLinesAdded 2887
+#define wxStyledTextEvent_GetLine 2888
+#define wxStyledTextEvent_GetFoldLevelNow 2889
+#define wxStyledTextEvent_GetFoldLevelPrev 2890
+#define wxStyledTextEvent_GetMargin 2891
+#define wxStyledTextEvent_GetMessage 2892
+#define wxStyledTextEvent_GetWParam 2893
+#define wxStyledTextEvent_GetLParam 2894
+#define wxStyledTextEvent_GetListType 2895
+#define wxStyledTextEvent_GetX 2896
+#define wxStyledTextEvent_GetY 2897
+#define wxStyledTextEvent_GetDragText 2898
+#define wxStyledTextEvent_GetDragAllowMove 2899
+#define wxStyledTextEvent_GetDragResult 2900
+#define wxStyledTextEvent_GetShift 2901
+#define wxStyledTextEvent_GetControl 2902
+#define wxStyledTextEvent_GetAlt 2903
+#define utils_wxGetKeyState 2904
+#define utils_wxGetMousePosition 2905
+#define utils_wxGetMouseState 2906
+#define utils_wxSetDetectableAutoRepeat 2907
+#define utils_wxBell 2908
+#define utils_wxFindMenuItemId 2909
+#define utils_wxGenericFindWindowAtPoint 2910
+#define utils_wxFindWindowAtPoint 2911
+#define utils_wxBeginBusyCursor 2912
+#define utils_wxEndBusyCursor 2913
+#define utils_wxIsBusy 2914
+#define utils_wxShutdown 2915
+#define utils_wxShell 2916
+#define utils_wxLaunchDefaultBrowser 2917
+#define utils_wxGetEmailAddress 2918
+#define utils_wxGetUserId 2919
+#define utils_wxGetHomeDir 2920
+#define utils_wxNewId 2921
+#define utils_wxRegisterId 2922
+#define utils_wxGetCurrentId 2923
+#define utils_wxGetOsDescription 2924
+#define utils_wxIsPlatformLittleEndian 2925
+#define utils_wxIsPlatform64Bit 2926
+#define wxPrintout_new 2927
+#define wxPrintout_destruct 2928
+#define wxPrintout_GetDC 2929
+#define wxPrintout_GetPageSizeMM 2930
+#define wxPrintout_GetPageSizePixels 2931
+#define wxPrintout_GetPaperRectPixels 2932
+#define wxPrintout_GetPPIPrinter 2933
+#define wxPrintout_GetPPIScreen 2934
+#define wxPrintout_GetTitle 2935
+#define wxPrintout_IsPreview 2936
+#define wxPrintout_FitThisSizeToPaper 2937
+#define wxPrintout_FitThisSizeToPage 2938
+#define wxPrintout_FitThisSizeToPageMargins 2939
+#define wxPrintout_MapScreenSizeToPaper 2940
+#define wxPrintout_MapScreenSizeToPage 2941
+#define wxPrintout_MapScreenSizeToPageMargins 2942
+#define wxPrintout_MapScreenSizeToDevice 2943
+#define wxPrintout_GetLogicalPaperRect 2944
+#define wxPrintout_GetLogicalPageRect 2945
+#define wxPrintout_GetLogicalPageMarginsRect 2946
+#define wxPrintout_SetLogicalOrigin 2947
+#define wxPrintout_OffsetLogicalOrigin 2948
+#define wxStyledTextCtrl_new_2 2949
+#define wxStyledTextCtrl_new_0 2950
+#define wxStyledTextCtrl_destruct 2951
+#define wxStyledTextCtrl_Create 2952
+#define wxStyledTextCtrl_AddText 2953
+#define wxStyledTextCtrl_AddStyledText 2954
+#define wxStyledTextCtrl_InsertText 2955
+#define wxStyledTextCtrl_ClearAll 2956
+#define wxStyledTextCtrl_ClearDocumentStyle 2957
+#define wxStyledTextCtrl_GetLength 2958
+#define wxStyledTextCtrl_GetCharAt 2959
+#define wxStyledTextCtrl_GetCurrentPos 2960
+#define wxStyledTextCtrl_GetAnchor 2961
+#define wxStyledTextCtrl_GetStyleAt 2962
+#define wxStyledTextCtrl_Redo 2963
+#define wxStyledTextCtrl_SetUndoCollection 2964
+#define wxStyledTextCtrl_SelectAll 2965
+#define wxStyledTextCtrl_SetSavePoint 2966
+#define wxStyledTextCtrl_GetStyledText 2967
+#define wxStyledTextCtrl_CanRedo 2968
+#define wxStyledTextCtrl_MarkerLineFromHandle 2969
+#define wxStyledTextCtrl_MarkerDeleteHandle 2970
+#define wxStyledTextCtrl_GetUndoCollection 2971
+#define wxStyledTextCtrl_GetViewWhiteSpace 2972
+#define wxStyledTextCtrl_SetViewWhiteSpace 2973
+#define wxStyledTextCtrl_PositionFromPoint 2974
+#define wxStyledTextCtrl_PositionFromPointClose 2975
+#define wxStyledTextCtrl_GotoLine 2976
+#define wxStyledTextCtrl_GotoPos 2977
+#define wxStyledTextCtrl_SetAnchor 2978
+#define wxStyledTextCtrl_GetCurLine 2979
+#define wxStyledTextCtrl_GetEndStyled 2980
+#define wxStyledTextCtrl_ConvertEOLs 2981
+#define wxStyledTextCtrl_GetEOLMode 2982
+#define wxStyledTextCtrl_SetEOLMode 2983
+#define wxStyledTextCtrl_StartStyling 2984
+#define wxStyledTextCtrl_SetStyling 2985
+#define wxStyledTextCtrl_GetBufferedDraw 2986
+#define wxStyledTextCtrl_SetBufferedDraw 2987
+#define wxStyledTextCtrl_SetTabWidth 2988
+#define wxStyledTextCtrl_GetTabWidth 2989
+#define wxStyledTextCtrl_SetCodePage 2990
+#define wxStyledTextCtrl_MarkerDefine 2991
+#define wxStyledTextCtrl_MarkerSetForeground 2992
+#define wxStyledTextCtrl_MarkerSetBackground 2993
+#define wxStyledTextCtrl_MarkerAdd 2994
+#define wxStyledTextCtrl_MarkerDelete 2995
+#define wxStyledTextCtrl_MarkerDeleteAll 2996
+#define wxStyledTextCtrl_MarkerGet 2997
+#define wxStyledTextCtrl_MarkerNext 2998
+#define wxStyledTextCtrl_MarkerPrevious 2999
+#define wxStyledTextCtrl_MarkerDefineBitmap 3000
+#define wxStyledTextCtrl_MarkerAddSet 3001
+#define wxStyledTextCtrl_MarkerSetAlpha 3002
+#define wxStyledTextCtrl_SetMarginType 3003
+#define wxStyledTextCtrl_GetMarginType 3004
+#define wxStyledTextCtrl_SetMarginWidth 3005
+#define wxStyledTextCtrl_GetMarginWidth 3006
+#define wxStyledTextCtrl_SetMarginMask 3007
+#define wxStyledTextCtrl_GetMarginMask 3008
+#define wxStyledTextCtrl_SetMarginSensitive 3009
+#define wxStyledTextCtrl_GetMarginSensitive 3010
+#define wxStyledTextCtrl_StyleClearAll 3011
+#define wxStyledTextCtrl_StyleSetForeground 3012
+#define wxStyledTextCtrl_StyleSetBackground 3013
+#define wxStyledTextCtrl_StyleSetBold 3014
+#define wxStyledTextCtrl_StyleSetItalic 3015
+#define wxStyledTextCtrl_StyleSetSize 3016
+#define wxStyledTextCtrl_StyleSetFaceName 3017
+#define wxStyledTextCtrl_StyleSetEOLFilled 3018
+#define wxStyledTextCtrl_StyleResetDefault 3019
+#define wxStyledTextCtrl_StyleSetUnderline 3020
+#define wxStyledTextCtrl_StyleSetCase 3021
+#define wxStyledTextCtrl_StyleSetHotSpot 3022
+#define wxStyledTextCtrl_SetSelForeground 3023
+#define wxStyledTextCtrl_SetSelBackground 3024
+#define wxStyledTextCtrl_GetSelAlpha 3025
+#define wxStyledTextCtrl_SetSelAlpha 3026
+#define wxStyledTextCtrl_SetCaretForeground 3027
+#define wxStyledTextCtrl_CmdKeyAssign 3028
+#define wxStyledTextCtrl_CmdKeyClear 3029
+#define wxStyledTextCtrl_CmdKeyClearAll 3030
+#define wxStyledTextCtrl_SetStyleBytes 3031
+#define wxStyledTextCtrl_StyleSetVisible 3032
+#define wxStyledTextCtrl_GetCaretPeriod 3033
+#define wxStyledTextCtrl_SetCaretPeriod 3034
+#define wxStyledTextCtrl_SetWordChars 3035
+#define wxStyledTextCtrl_BeginUndoAction 3036
+#define wxStyledTextCtrl_EndUndoAction 3037
+#define wxStyledTextCtrl_IndicatorSetStyle 3038
+#define wxStyledTextCtrl_IndicatorGetStyle 3039
+#define wxStyledTextCtrl_IndicatorSetForeground 3040
+#define wxStyledTextCtrl_IndicatorGetForeground 3041
+#define wxStyledTextCtrl_SetWhitespaceForeground 3042
+#define wxStyledTextCtrl_SetWhitespaceBackground 3043
+#define wxStyledTextCtrl_GetStyleBits 3044
+#define wxStyledTextCtrl_SetLineState 3045
+#define wxStyledTextCtrl_GetLineState 3046
+#define wxStyledTextCtrl_GetMaxLineState 3047
+#define wxStyledTextCtrl_GetCaretLineVisible 3048
+#define wxStyledTextCtrl_SetCaretLineVisible 3049
+#define wxStyledTextCtrl_GetCaretLineBackground 3050
+#define wxStyledTextCtrl_SetCaretLineBackground 3051
+#define wxStyledTextCtrl_AutoCompShow 3052
+#define wxStyledTextCtrl_AutoCompCancel 3053
+#define wxStyledTextCtrl_AutoCompActive 3054
+#define wxStyledTextCtrl_AutoCompPosStart 3055
+#define wxStyledTextCtrl_AutoCompComplete 3056
+#define wxStyledTextCtrl_AutoCompStops 3057
+#define wxStyledTextCtrl_AutoCompSetSeparator 3058
+#define wxStyledTextCtrl_AutoCompGetSeparator 3059
+#define wxStyledTextCtrl_AutoCompSelect 3060
+#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3061
+#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3062
+#define wxStyledTextCtrl_AutoCompSetFillUps 3063
+#define wxStyledTextCtrl_AutoCompSetChooseSingle 3064
+#define wxStyledTextCtrl_AutoCompGetChooseSingle 3065
+#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3066
+#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3067
+#define wxStyledTextCtrl_UserListShow 3068
+#define wxStyledTextCtrl_AutoCompSetAutoHide 3069
+#define wxStyledTextCtrl_AutoCompGetAutoHide 3070
+#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3071
+#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3072
+#define wxStyledTextCtrl_RegisterImage 3073
+#define wxStyledTextCtrl_ClearRegisteredImages 3074
+#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3075
+#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3076
+#define wxStyledTextCtrl_AutoCompSetMaxWidth 3077
+#define wxStyledTextCtrl_AutoCompGetMaxWidth 3078
+#define wxStyledTextCtrl_AutoCompSetMaxHeight 3079
+#define wxStyledTextCtrl_AutoCompGetMaxHeight 3080
+#define wxStyledTextCtrl_SetIndent 3081
+#define wxStyledTextCtrl_GetIndent 3082
+#define wxStyledTextCtrl_SetUseTabs 3083
+#define wxStyledTextCtrl_GetUseTabs 3084
+#define wxStyledTextCtrl_SetLineIndentation 3085
+#define wxStyledTextCtrl_GetLineIndentation 3086
+#define wxStyledTextCtrl_GetLineIndentPosition 3087
+#define wxStyledTextCtrl_GetColumn 3088
+#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3089
+#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3090
+#define wxStyledTextCtrl_SetIndentationGuides 3091
+#define wxStyledTextCtrl_GetIndentationGuides 3092
+#define wxStyledTextCtrl_SetHighlightGuide 3093
+#define wxStyledTextCtrl_GetHighlightGuide 3094
+#define wxStyledTextCtrl_GetLineEndPosition 3095
+#define wxStyledTextCtrl_GetCodePage 3096
+#define wxStyledTextCtrl_GetCaretForeground 3097
+#define wxStyledTextCtrl_GetReadOnly 3098
+#define wxStyledTextCtrl_SetCurrentPos 3099
+#define wxStyledTextCtrl_SetSelectionStart 3100
+#define wxStyledTextCtrl_GetSelectionStart 3101
+#define wxStyledTextCtrl_SetSelectionEnd 3102
+#define wxStyledTextCtrl_GetSelectionEnd 3103
+#define wxStyledTextCtrl_SetPrintMagnification 3104
+#define wxStyledTextCtrl_GetPrintMagnification 3105
+#define wxStyledTextCtrl_SetPrintColourMode 3106
+#define wxStyledTextCtrl_GetPrintColourMode 3107
+#define wxStyledTextCtrl_FindText 3108
+#define wxStyledTextCtrl_FormatRange 3109
+#define wxStyledTextCtrl_GetFirstVisibleLine 3110
+#define wxStyledTextCtrl_GetLine 3111
+#define wxStyledTextCtrl_GetLineCount 3112
+#define wxStyledTextCtrl_SetMarginLeft 3113
+#define wxStyledTextCtrl_GetMarginLeft 3114
+#define wxStyledTextCtrl_SetMarginRight 3115
+#define wxStyledTextCtrl_GetMarginRight 3116
+#define wxStyledTextCtrl_GetModify 3117
+#define wxStyledTextCtrl_SetSelection 3118
+#define wxStyledTextCtrl_GetSelectedText 3119
+#define wxStyledTextCtrl_GetTextRange 3120
+#define wxStyledTextCtrl_HideSelection 3121
+#define wxStyledTextCtrl_LineFromPosition 3122
+#define wxStyledTextCtrl_PositionFromLine 3123
+#define wxStyledTextCtrl_LineScroll 3124
+#define wxStyledTextCtrl_EnsureCaretVisible 3125
+#define wxStyledTextCtrl_ReplaceSelection 3126
+#define wxStyledTextCtrl_SetReadOnly 3127
+#define wxStyledTextCtrl_CanPaste 3128
+#define wxStyledTextCtrl_CanUndo 3129
+#define wxStyledTextCtrl_EmptyUndoBuffer 3130
+#define wxStyledTextCtrl_Undo 3131
+#define wxStyledTextCtrl_Cut 3132
+#define wxStyledTextCtrl_Copy 3133
+#define wxStyledTextCtrl_Paste 3134
+#define wxStyledTextCtrl_Clear 3135
+#define wxStyledTextCtrl_SetText 3136
+#define wxStyledTextCtrl_GetText 3137
+#define wxStyledTextCtrl_GetTextLength 3138
+#define wxStyledTextCtrl_GetOvertype 3139
+#define wxStyledTextCtrl_SetCaretWidth 3140
+#define wxStyledTextCtrl_GetCaretWidth 3141
+#define wxStyledTextCtrl_SetTargetStart 3142
+#define wxStyledTextCtrl_GetTargetStart 3143
+#define wxStyledTextCtrl_SetTargetEnd 3144
+#define wxStyledTextCtrl_GetTargetEnd 3145
+#define wxStyledTextCtrl_ReplaceTarget 3146
+#define wxStyledTextCtrl_SearchInTarget 3147
+#define wxStyledTextCtrl_SetSearchFlags 3148
+#define wxStyledTextCtrl_GetSearchFlags 3149
+#define wxStyledTextCtrl_CallTipShow 3150
+#define wxStyledTextCtrl_CallTipCancel 3151
+#define wxStyledTextCtrl_CallTipActive 3152
+#define wxStyledTextCtrl_CallTipPosAtStart 3153
+#define wxStyledTextCtrl_CallTipSetHighlight 3154
+#define wxStyledTextCtrl_CallTipSetBackground 3155
+#define wxStyledTextCtrl_CallTipSetForeground 3156
+#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3157
+#define wxStyledTextCtrl_CallTipUseStyle 3158
+#define wxStyledTextCtrl_VisibleFromDocLine 3159
+#define wxStyledTextCtrl_DocLineFromVisible 3160
+#define wxStyledTextCtrl_WrapCount 3161
+#define wxStyledTextCtrl_SetFoldLevel 3162
+#define wxStyledTextCtrl_GetFoldLevel 3163
+#define wxStyledTextCtrl_GetLastChild 3164
+#define wxStyledTextCtrl_GetFoldParent 3165
+#define wxStyledTextCtrl_ShowLines 3166
+#define wxStyledTextCtrl_HideLines 3167
+#define wxStyledTextCtrl_GetLineVisible 3168
+#define wxStyledTextCtrl_SetFoldExpanded 3169
+#define wxStyledTextCtrl_GetFoldExpanded 3170
+#define wxStyledTextCtrl_ToggleFold 3171
+#define wxStyledTextCtrl_EnsureVisible 3172
+#define wxStyledTextCtrl_SetFoldFlags 3173
+#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3174
+#define wxStyledTextCtrl_SetTabIndents 3175
+#define wxStyledTextCtrl_GetTabIndents 3176
+#define wxStyledTextCtrl_SetBackSpaceUnIndents 3177
+#define wxStyledTextCtrl_GetBackSpaceUnIndents 3178
+#define wxStyledTextCtrl_SetMouseDwellTime 3179
+#define wxStyledTextCtrl_GetMouseDwellTime 3180
+#define wxStyledTextCtrl_WordStartPosition 3181
+#define wxStyledTextCtrl_WordEndPosition 3182
+#define wxStyledTextCtrl_SetWrapMode 3183
+#define wxStyledTextCtrl_GetWrapMode 3184
+#define wxStyledTextCtrl_SetWrapVisualFlags 3185
+#define wxStyledTextCtrl_GetWrapVisualFlags 3186
+#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3187
+#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3188
+#define wxStyledTextCtrl_SetWrapStartIndent 3189
+#define wxStyledTextCtrl_GetWrapStartIndent 3190
+#define wxStyledTextCtrl_SetLayoutCache 3191
+#define wxStyledTextCtrl_GetLayoutCache 3192
+#define wxStyledTextCtrl_SetScrollWidth 3193
+#define wxStyledTextCtrl_GetScrollWidth 3194
+#define wxStyledTextCtrl_TextWidth 3195
+#define wxStyledTextCtrl_GetEndAtLastLine 3196
+#define wxStyledTextCtrl_TextHeight 3197
+#define wxStyledTextCtrl_SetUseVerticalScrollBar 3198
+#define wxStyledTextCtrl_GetUseVerticalScrollBar 3199
+#define wxStyledTextCtrl_AppendText 3200
+#define wxStyledTextCtrl_GetTwoPhaseDraw 3201
+#define wxStyledTextCtrl_SetTwoPhaseDraw 3202
+#define wxStyledTextCtrl_TargetFromSelection 3203
+#define wxStyledTextCtrl_LinesJoin 3204
+#define wxStyledTextCtrl_LinesSplit 3205
+#define wxStyledTextCtrl_SetFoldMarginColour 3206
+#define wxStyledTextCtrl_SetFoldMarginHiColour 3207
+#define wxStyledTextCtrl_LineDown 3208
+#define wxStyledTextCtrl_LineDownExtend 3209
+#define wxStyledTextCtrl_LineUp 3210
+#define wxStyledTextCtrl_LineUpExtend 3211
+#define wxStyledTextCtrl_CharLeft 3212
+#define wxStyledTextCtrl_CharLeftExtend 3213
+#define wxStyledTextCtrl_CharRight 3214
+#define wxStyledTextCtrl_CharRightExtend 3215
+#define wxStyledTextCtrl_WordLeft 3216
+#define wxStyledTextCtrl_WordLeftExtend 3217
+#define wxStyledTextCtrl_WordRight 3218
+#define wxStyledTextCtrl_WordRightExtend 3219
+#define wxStyledTextCtrl_Home 3220
+#define wxStyledTextCtrl_HomeExtend 3221
+#define wxStyledTextCtrl_LineEnd 3222
+#define wxStyledTextCtrl_LineEndExtend 3223
+#define wxStyledTextCtrl_DocumentStart 3224
+#define wxStyledTextCtrl_DocumentStartExtend 3225
+#define wxStyledTextCtrl_DocumentEnd 3226
+#define wxStyledTextCtrl_DocumentEndExtend 3227
+#define wxStyledTextCtrl_PageUp 3228
+#define wxStyledTextCtrl_PageUpExtend 3229
+#define wxStyledTextCtrl_PageDown 3230
+#define wxStyledTextCtrl_PageDownExtend 3231
+#define wxStyledTextCtrl_EditToggleOvertype 3232
+#define wxStyledTextCtrl_Cancel 3233
+#define wxStyledTextCtrl_DeleteBack 3234
+#define wxStyledTextCtrl_Tab 3235
+#define wxStyledTextCtrl_BackTab 3236
+#define wxStyledTextCtrl_NewLine 3237
+#define wxStyledTextCtrl_FormFeed 3238
+#define wxStyledTextCtrl_VCHome 3239
+#define wxStyledTextCtrl_VCHomeExtend 3240
+#define wxStyledTextCtrl_ZoomIn 3241
+#define wxStyledTextCtrl_ZoomOut 3242
+#define wxStyledTextCtrl_DelWordLeft 3243
+#define wxStyledTextCtrl_DelWordRight 3244
+#define wxStyledTextCtrl_LineCut 3245
+#define wxStyledTextCtrl_LineDelete 3246
+#define wxStyledTextCtrl_LineTranspose 3247
+#define wxStyledTextCtrl_LineDuplicate 3248
+#define wxStyledTextCtrl_LowerCase 3249
+#define wxStyledTextCtrl_UpperCase 3250
+#define wxStyledTextCtrl_LineScrollDown 3251
+#define wxStyledTextCtrl_LineScrollUp 3252
+#define wxStyledTextCtrl_DeleteBackNotLine 3253
+#define wxStyledTextCtrl_HomeDisplay 3254
+#define wxStyledTextCtrl_HomeDisplayExtend 3255
+#define wxStyledTextCtrl_LineEndDisplay 3256
+#define wxStyledTextCtrl_LineEndDisplayExtend 3257
+#define wxStyledTextCtrl_HomeWrapExtend 3258
+#define wxStyledTextCtrl_LineEndWrap 3259
+#define wxStyledTextCtrl_LineEndWrapExtend 3260
+#define wxStyledTextCtrl_VCHomeWrap 3261
+#define wxStyledTextCtrl_VCHomeWrapExtend 3262
+#define wxStyledTextCtrl_LineCopy 3263
+#define wxStyledTextCtrl_MoveCaretInsideView 3264
+#define wxStyledTextCtrl_LineLength 3265
+#define wxStyledTextCtrl_BraceHighlight 3266
+#define wxStyledTextCtrl_BraceBadLight 3267
+#define wxStyledTextCtrl_BraceMatch 3268
+#define wxStyledTextCtrl_GetViewEOL 3269
+#define wxStyledTextCtrl_SetViewEOL 3270
+#define wxStyledTextCtrl_SetModEventMask 3271
+#define wxStyledTextCtrl_GetEdgeColumn 3272
+#define wxStyledTextCtrl_SetEdgeColumn 3273
+#define wxStyledTextCtrl_SetEdgeMode 3274
+#define wxStyledTextCtrl_GetEdgeMode 3275
+#define wxStyledTextCtrl_GetEdgeColour 3276
+#define wxStyledTextCtrl_SetEdgeColour 3277
+#define wxStyledTextCtrl_SearchAnchor 3278
+#define wxStyledTextCtrl_SearchNext 3279
+#define wxStyledTextCtrl_SearchPrev 3280
+#define wxStyledTextCtrl_LinesOnScreen 3281
+#define wxStyledTextCtrl_UsePopUp 3282
+#define wxStyledTextCtrl_SelectionIsRectangle 3283
+#define wxStyledTextCtrl_SetZoom 3284
+#define wxStyledTextCtrl_GetZoom 3285
+#define wxStyledTextCtrl_GetModEventMask 3286
+#define wxStyledTextCtrl_SetSTCFocus 3287
+#define wxStyledTextCtrl_GetSTCFocus 3288
+#define wxStyledTextCtrl_SetStatus 3289
+#define wxStyledTextCtrl_GetStatus 3290
+#define wxStyledTextCtrl_SetMouseDownCaptures 3291
+#define wxStyledTextCtrl_GetMouseDownCaptures 3292
+#define wxStyledTextCtrl_SetSTCCursor 3293
+#define wxStyledTextCtrl_GetSTCCursor 3294
+#define wxStyledTextCtrl_SetControlCharSymbol 3295
+#define wxStyledTextCtrl_GetControlCharSymbol 3296
+#define wxStyledTextCtrl_WordPartLeft 3297
+#define wxStyledTextCtrl_WordPartLeftExtend 3298
+#define wxStyledTextCtrl_WordPartRight 3299
+#define wxStyledTextCtrl_WordPartRightExtend 3300
+#define wxStyledTextCtrl_SetVisiblePolicy 3301
+#define wxStyledTextCtrl_DelLineLeft 3302
+#define wxStyledTextCtrl_DelLineRight 3303
+#define wxStyledTextCtrl_GetXOffset 3304
+#define wxStyledTextCtrl_ChooseCaretX 3305
+#define wxStyledTextCtrl_SetXCaretPolicy 3306
+#define wxStyledTextCtrl_SetYCaretPolicy 3307
+#define wxStyledTextCtrl_GetPrintWrapMode 3308
+#define wxStyledTextCtrl_SetHotspotActiveForeground 3309
+#define wxStyledTextCtrl_SetHotspotActiveBackground 3310
+#define wxStyledTextCtrl_SetHotspotActiveUnderline 3311
+#define wxStyledTextCtrl_SetHotspotSingleLine 3312
+#define wxStyledTextCtrl_ParaDownExtend 3313
+#define wxStyledTextCtrl_ParaUp 3314
+#define wxStyledTextCtrl_ParaUpExtend 3315
+#define wxStyledTextCtrl_PositionBefore 3316
+#define wxStyledTextCtrl_PositionAfter 3317
+#define wxStyledTextCtrl_CopyRange 3318
+#define wxStyledTextCtrl_CopyText 3319
+#define wxStyledTextCtrl_SetSelectionMode 3320
+#define wxStyledTextCtrl_GetSelectionMode 3321
+#define wxStyledTextCtrl_LineDownRectExtend 3322
+#define wxStyledTextCtrl_LineUpRectExtend 3323
+#define wxStyledTextCtrl_CharLeftRectExtend 3324
+#define wxStyledTextCtrl_CharRightRectExtend 3325
+#define wxStyledTextCtrl_HomeRectExtend 3326
+#define wxStyledTextCtrl_VCHomeRectExtend 3327
+#define wxStyledTextCtrl_LineEndRectExtend 3328
+#define wxStyledTextCtrl_PageUpRectExtend 3329
+#define wxStyledTextCtrl_PageDownRectExtend 3330
+#define wxStyledTextCtrl_StutteredPageUp 3331
+#define wxStyledTextCtrl_StutteredPageUpExtend 3332
+#define wxStyledTextCtrl_StutteredPageDown 3333
+#define wxStyledTextCtrl_StutteredPageDownExtend 3334
+#define wxStyledTextCtrl_WordLeftEnd 3335
+#define wxStyledTextCtrl_WordLeftEndExtend 3336
+#define wxStyledTextCtrl_WordRightEnd 3337
+#define wxStyledTextCtrl_WordRightEndExtend 3338
+#define wxStyledTextCtrl_SetWhitespaceChars 3339
+#define wxStyledTextCtrl_SetCharsDefault 3340
+#define wxStyledTextCtrl_AutoCompGetCurrent 3341
+#define wxStyledTextCtrl_Allocate 3342
+#define wxStyledTextCtrl_FindColumn 3343
+#define wxStyledTextCtrl_GetCaretSticky 3344
+#define wxStyledTextCtrl_SetCaretSticky 3345
+#define wxStyledTextCtrl_ToggleCaretSticky 3346
+#define wxStyledTextCtrl_SetPasteConvertEndings 3347
+#define wxStyledTextCtrl_GetPasteConvertEndings 3348
+#define wxStyledTextCtrl_SelectionDuplicate 3349
+#define wxStyledTextCtrl_SetCaretLineBackAlpha 3350
+#define wxStyledTextCtrl_GetCaretLineBackAlpha 3351
+#define wxStyledTextCtrl_StartRecord 3352
+#define wxStyledTextCtrl_StopRecord 3353
+#define wxStyledTextCtrl_SetLexer 3354
+#define wxStyledTextCtrl_GetLexer 3355
+#define wxStyledTextCtrl_Colourise 3356
+#define wxStyledTextCtrl_SetProperty 3357
+#define wxStyledTextCtrl_SetKeyWords 3358
+#define wxStyledTextCtrl_SetLexerLanguage 3359
+#define wxStyledTextCtrl_GetProperty 3360
+#define wxStyledTextCtrl_GetStyleBitsNeeded 3361
+#define wxStyledTextCtrl_GetCurrentLine 3362
+#define wxStyledTextCtrl_StyleSetSpec 3363
+#define wxStyledTextCtrl_StyleSetFont 3364
+#define wxStyledTextCtrl_StyleSetFontAttr 3365
+#define wxStyledTextCtrl_StyleSetCharacterSet 3366
+#define wxStyledTextCtrl_StyleSetFontEncoding 3367
+#define wxStyledTextCtrl_CmdKeyExecute 3368
+#define wxStyledTextCtrl_SetMargins 3369
+#define wxStyledTextCtrl_GetSelection 3370
+#define wxStyledTextCtrl_PointFromPosition 3371
+#define wxStyledTextCtrl_ScrollToLine 3372
+#define wxStyledTextCtrl_ScrollToColumn 3373
#define wxStyledTextCtrl_SetVScrollBar 3374
#define wxStyledTextCtrl_SetHScrollBar 3375
#define wxStyledTextCtrl_GetLastKeydownProcessed 3376
diff --git a/lib/wx/c_src/wxePrintout.cpp b/lib/wx/c_src/wxePrintout.cpp
index 90959df379..fc8782ba95 100644
--- a/lib/wx/c_src/wxePrintout.cpp
+++ b/lib/wx/c_src/wxePrintout.cpp
@@ -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
@@ -38,7 +38,7 @@
rt.addAtom("_wx_invoke_cb_"); \
rt.addTupleCount(3); \
rt.send(); \
- handle_event_callback(port, memenv->owner); \
+ handle_event_callback(WXE_DRV_PORT_HANDLE, memenv->owner); \
}
#define INVOKE_CALLBACK(port, callback, class_str) \
@@ -230,7 +230,7 @@ EwxListCtrl::~EwxListCtrl() {
}
// tools
-void clear_cb(ErlDrvPort port, int callback)
+void clear_cb(ErlDrvTermData port, int callback)
{
if(callback > 0) {
wxeMemEnv * memenv = ((WxeApp *) wxTheApp)->getMemEnv(port);
diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c
index d1ed252ec0..4d3aa577bf 100644
--- a/lib/wx/c_src/wxe_driver.c
+++ b/lib/wx/c_src/wxe_driver.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-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
@@ -100,7 +100,8 @@ int wxe_driver_load()
return -1;
}
-ErlDrvPort WXE_DRV_PORT = 0;
+ErlDrvPort WXE_DRV_PORT_HANDLE = 0;
+ErlDrvTermData WXE_DRV_PORT = 0;
static ErlDrvData
wxe_driver_start(ErlDrvPort port, char *buff)
@@ -114,17 +115,20 @@ wxe_driver_start(ErlDrvPort port, char *buff)
fprintf(stderr, " Couldn't alloc mem\r\n");
return(ERL_DRV_ERROR_GENERAL); /* ENOMEM */
} else {
+ ErlDrvTermData term_port = driver_mk_port(port);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
data->driver_data = NULL;
data->bin = NULL;
- data->port = port;
+ data->port_handle = port;
+ data->port = term_port;
data->pdl = driver_pdl_create(port);
- if(WXE_DRV_PORT == 0) {
+ if(WXE_DRV_PORT_HANDLE == 0) {
for(; *buff != 32; buff++);
buff++;
erl_wx_privdir = strdup(buff);
- WXE_DRV_PORT = port;
+ WXE_DRV_PORT_HANDLE = port;
+ WXE_DRV_PORT = term_port;
wxe_master = data;
if(!(start_native_gui(data) == 1))
return(ERL_DRV_ERROR_GENERAL); /* ENOMEM */
@@ -139,7 +143,7 @@ static void
wxe_driver_stop(ErlDrvData handle)
{
wxe_data *sd = ((wxe_data *)handle);
- if(sd->port != WXE_DRV_PORT) {
+ if(sd->port_handle != WXE_DRV_PORT_HANDLE) {
// fprintf(stderr, "%s:%d: STOP \r\n", __FILE__,__LINE__);
meta_command(DELETE_PORT,sd);
free(handle);
@@ -194,7 +198,7 @@ void wxe_process_died(ErlDrvData handle, ErlDrvMonitor *monitor)
push_command(WXE_CB_RETURN,NULL,0,sd);
/* ErlDrvTermData pid; */
-/* pid = driver_get_monitored_process(sd->port, monitor); */
+/* pid = driver_get_monitored_process(sd->port_handle, monitor); */
/* fprintf(stderr, "Process died %d \r\n", (int) pid); */
}
@@ -210,7 +214,7 @@ standard_outputv(ErlDrvData drv_data, ErlIOVec* ev)
binref = driver_alloc(sizeof(WXEBinRef));
binref->base = ev->iov[1].iov_base;
binref->size = ev->iov[1].iov_len;
- binref->from = driver_caller(sd->port);
+ binref->from = driver_caller(sd->port_handle);
bin = ev->binv[1];
driver_binary_inc_refc(bin); /* Otherwise it could get deallocated */
binref->bin = bin;
@@ -220,7 +224,7 @@ standard_outputv(ErlDrvData drv_data, ErlIOVec* ev)
binref = driver_alloc(sizeof(WXEBinRef));
binref->base = NULL;
binref->size = 0;
- binref->from = driver_caller(sd->port);
+ binref->from = driver_caller(sd->port_handle);
binref->bin = NULL;
binref->next = sd->bin;
sd->bin = binref;
diff --git a/lib/wx/c_src/wxe_driver.h b/lib/wx/c_src/wxe_driver.h
index 5c5b8614ed..c765c866c2 100644
--- a/lib/wx/c_src/wxe_driver.h
+++ b/lib/wx/c_src/wxe_driver.h
@@ -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
@@ -43,7 +43,8 @@ typedef struct wxe_bin_ref {
typedef struct wxe_data_def {
void * driver_data;
WXEBinRef * bin; /* Argument binaries */
- ErlDrvPort port;
+ ErlDrvPort port_handle;
+ ErlDrvTermData port;
int is_cbport;
ErlDrvPDL pdl;
} wxe_data;
@@ -67,8 +68,9 @@ void meta_command(int what, wxe_data *sd);
void * wxe_ps_init();
-extern ErlDrvPort WXE_DRV_PORT;
-extern int wxe_debug;
+extern ErlDrvPort WXE_DRV_PORT_HANDLE;
+extern ErlDrvTermData WXE_DRV_PORT;
+extern int wxe_debug;
extern char * erl_wx_privdir;
diff --git a/lib/wx/c_src/wxe_events.h b/lib/wx/c_src/wxe_events.h
index 43ea656a8d..6bbb0dfa13 100644
--- a/lib/wx/c_src/wxe_events.h
+++ b/lib/wx/c_src/wxe_events.h
@@ -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
@@ -54,7 +54,7 @@ public:
class wxeEvtListener : public wxEvtHandler
{
public:
- wxeEvtListener(ErlDrvPort Thisport) : port(Thisport)
+ wxeEvtListener(ErlDrvTermData Thisport) : port(Thisport)
{}
// {fprintf(stderr, "Creating %x\r\n", (unsigned int) this); fflush(stderr);}
void forward(wxEvent& event);
@@ -62,7 +62,7 @@ public:
((WxeApp *)wxTheApp)->clearPtr(this);
// fprintf(stderr, "Deleteing %x\r\n", (unsigned int) this); fflush(stderr);
};
- ErlDrvPort port;
+ ErlDrvTermData port;
};
#endif
diff --git a/lib/wx/c_src/wxe_gl.cpp b/lib/wx/c_src/wxe_gl.cpp
index e947a1bc6e..34904397d3 100644
--- a/lib/wx/c_src/wxe_gl.cpp
+++ b/lib/wx/c_src/wxe_gl.cpp
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-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
@@ -141,7 +141,7 @@ void gl_dispatch(int op, char *bp,ErlDrvTermData caller,WXEBinRef *bins[]){
ERL_DRV_INT, op,
ERL_DRV_ATOM, driver_mk_atom((char *) "no_gl_context"),
ERL_DRV_TUPLE,3};
- driver_send_term(WXE_DRV_PORT,caller,rt,8);
+ erl_drv_send_term(WXE_DRV_PORT,caller,rt,8);
return ;
}
};
@@ -155,6 +155,6 @@ void gl_dispatch(int op, char *bp,ErlDrvTermData caller,WXEBinRef *bins[]){
else
bs[i] = NULL;
}
- wxe_gl_dispatch(op, bp, WXE_DRV_PORT, caller, bs, bs_sz);
+ wxe_gl_dispatch(op, bp, WXE_DRV_PORT_HANDLE, caller, bs, bs_sz);
}
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index 69fcd4e362..adfee8da73 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-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,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"
@@ -112,7 +117,7 @@ int start_native_gui(wxe_data *sd)
wxe_batch_locker_m = erl_drv_mutex_create((char *)"wxe_batch_locker_m");
wxe_batch_locker_c = erl_drv_cond_create((char *)"wxe_batch_locker_c");
- init_caller = driver_connected(sd->port);
+ init_caller = driver_connected(sd->port_handle);
#ifdef __DARWIN__
res = erl_drv_steal_main_thread((char *)"wxwidgets",
@@ -164,7 +169,7 @@ void unload_native_gui()
void push_command(int op,char * buf,int len, wxe_data *sd)
{
- // fprintf(stderr, "Op %d %d\r\n", op, (int) driver_caller(sd->port)),fflush(stderr);
+ // fprintf(stderr, "Op %d %d\r\n", op, (int) driver_caller(sd->port_handle)),fflush(stderr);
wxeCommand *Cmd = new wxeCommand(op, buf, len, sd);
erl_drv_mutex_lock(wxe_batch_locker_m);
wxe_batch->Append(Cmd);
@@ -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();
}
@@ -510,18 +536,18 @@ void WxeApp::newMemEnv(wxeMetaCommand& Ecmd) {
memenv->ref2ptr[i] = global_me->ref2ptr[i];
}
memenv->next = global_me->next;
- refmap[(ErlDrvTermData) Ecmd.port] = memenv;
+ refmap[Ecmd.port] = memenv;
memenv->owner = Ecmd.caller;
ErlDrvTermData rt[] = {ERL_DRV_ATOM, driver_mk_atom((char *)"wx_port_initiated")};
- driver_send_term(WXE_DRV_PORT,Ecmd.caller,rt,2);
+ erl_drv_send_term(WXE_DRV_PORT,Ecmd.caller,rt,2);
}
void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd) {
// Clear incoming cmd queue first
// dispatch_cmds();
wxWindow *parent = NULL;
- wxeMemEnv * memenv = refmap[(ErlDrvTermData) Ecmd.port];
+ wxeMemEnv * memenv = refmap[Ecmd.port];
if(wxe_debug) {
wxString msg;
@@ -630,8 +656,8 @@ void WxeApp::destroyMemEnv(wxeMetaCommand& Ecmd) {
refmap.erase((ErlDrvTermData) Ecmd.port);
}
-wxeMemEnv * WxeApp::getMemEnv(ErlDrvPort port) {
- return refmap[(ErlDrvTermData) port];
+wxeMemEnv * WxeApp::getMemEnv(ErlDrvTermData port) {
+ return refmap[port];
}
int WxeApp::newPtr(void * ptr, int type, wxeMemEnv *memenv) {
@@ -811,7 +837,7 @@ wxeCommand::wxeCommand(int fc,char * cbuf,int buflen, wxe_data *sd)
{
WXEBinRef *temp, *start, *prev;
int n = 0;
- caller = driver_caller(sd->port);
+ caller = driver_caller(sd->port_handle);
port = sd->port;
op = fc;
len = buflen;
@@ -914,7 +940,7 @@ int wxCALLBACK wxEListCtrlCompare(long item1, long item2, long callbackInfoPtr)
rt.addAtom("_wx_invoke_cb_");
rt.addTupleCount(3);
rt.send();
- handle_event_callback(cb->port, memenv->owner);
+ handle_event_callback(WXE_DRV_PORT_HANDLE, memenv->owner);
if(((WxeApp *) wxTheApp)->cb_buff) {
int res = * (int*) ((WxeApp *) wxTheApp)->cb_buff;
diff --git a/lib/wx/c_src/wxe_impl.h b/lib/wx/c_src/wxe_impl.h
index ee31068d5d..a3c57e2598 100644
--- a/lib/wx/c_src/wxe_impl.h
+++ b/lib/wx/c_src/wxe_impl.h
@@ -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
@@ -34,7 +34,7 @@ class wxeMetaCommand : public wxEvent
public:
wxeMetaCommand(wxe_data *sd, int EvId)
: wxEvent(EvId, wxeEVT_META_COMMAND)
- { caller = driver_caller(sd->port); port = sd->port; pdl = sd->pdl; } ;
+ { caller = driver_caller(sd->port_handle); port = sd->port; pdl = sd->pdl; } ;
wxeMetaCommand(const wxeMetaCommand& event)
: wxEvent(event)
{ caller = event.caller; port = event.port; pdl = event.pdl; };
@@ -42,7 +42,7 @@ class wxeMetaCommand : public wxEvent
virtual wxEvent *Clone() const { return new wxeMetaCommand(*this); }
ErlDrvTermData caller;
- ErlDrvPort port;
+ ErlDrvTermData port;
ErlDrvPDL pdl;
};
@@ -51,9 +51,9 @@ class wxeCommand : public wxObject
public:
wxeCommand(int fc,char * cbuf,int buflen, wxe_data *);
virtual ~wxeCommand();
-
+
ErlDrvTermData caller;
- ErlDrvPort port;
+ ErlDrvTermData port;
WXEBinRef * bin[3];
char * buffer;
int len;
@@ -161,7 +161,7 @@ public:
// MemEnv handling
void newMemEnv(wxeMetaCommand& event);
void destroyMemEnv(wxeMetaCommand& event);
- wxeMemEnv * getMemEnv(ErlDrvPort port);
+ wxeMemEnv * getMemEnv(ErlDrvTermData port);
int newPtr(void * ptr, int type, wxeMemEnv *memenv);
int getRef(void * ptr, wxeMemEnv *memenv);
@@ -193,7 +193,7 @@ class wxETreeItemData : public wxTreeItemData
char * bin;
};
-bool sendevent(wxEvent * event, ErlDrvPort port);
+bool sendevent(wxEvent * event, ErlDrvTermData port);
void pre_callback();
void handle_event_callback(ErlDrvPort port, ErlDrvTermData process);
@@ -208,10 +208,10 @@ extern wxeGLC glc;
class wxEPrintout : public wxPrintout
{
public:
- wxEPrintout(wxString Title, int onPrintP, int onPrepareP,
+ wxEPrintout(wxString Title, int onPrintP, int onPrepareP,
int onBeginP, int onEndP,
int onBeginD, int onEndD,
- int hasP, int getPageI, ErlDrvPort Port) :
+ int hasP, int getPageI, ErlDrvTermData Port) :
wxPrintout(Title),
onPrintPage(onPrintP), onPreparePrinting(onPrepareP),
onBeginPrinting(onBeginP), onEndPrinting(onEndP),
@@ -241,15 +241,15 @@ class wxEPrintout : public wxPrintout
int hasPage;
int getPageInfo;
- ErlDrvPort port;
+ ErlDrvTermData port;
};
-void clear_cb(ErlDrvPort port, int callback);
+void clear_cb(ErlDrvTermData port, int callback);
// Implementation of wxListCtrlCompare
struct callbackInfo {
- ErlDrvPort port;
+ ErlDrvTermData port;
int callbackID;
};
diff --git a/lib/wx/c_src/wxe_ps_init.c b/lib/wx/c_src/wxe_ps_init.c
index a85f751024..541d188098 100644
--- a/lib/wx/c_src/wxe_ps_init.c
+++ b/lib/wx/c_src/wxe_ps_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-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
@@ -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/c_src/wxe_return.cpp b/lib/wx/c_src/wxe_return.cpp
index 0f3eb40efc..d50a74ac9d 100644
--- a/lib/wx/c_src/wxe_return.cpp
+++ b/lib/wx/c_src/wxe_return.cpp
@@ -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
@@ -26,9 +26,9 @@ WX_DEFINE_OBJARRAY(wxErlDrvTermDataArray);
#define INLINE
-wxeReturn::wxeReturn (ErlDrvPort _port,
- ErlDrvTermData _caller,
- bool _isResult) {
+wxeReturn::wxeReturn (ErlDrvTermData _port,
+ ErlDrvTermData _caller,
+ bool _isResult) {
port = _port;
caller = _caller;
@@ -61,7 +61,7 @@ int wxeReturn::send() {
rtData[i] = rt[i];
}
- int res = driver_send_term(port, caller, rtData, rtLength);
+ int res = erl_drv_send_term(port, caller, rtData, rtLength);
driver_free(rtData);
#ifdef DEBUG
diff --git a/lib/wx/c_src/wxe_return.h b/lib/wx/c_src/wxe_return.h
index b6bfa85e63..80946e2dc6 100644
--- a/lib/wx/c_src/wxe_return.h
+++ b/lib/wx/c_src/wxe_return.h
@@ -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
@@ -48,9 +48,9 @@ WX_DECLARE_OBJARRAY(ErlDrvTermData, wxErlDrvTermDataArray);
class wxeReturn {
public:
- wxeReturn (ErlDrvPort _port,
- ErlDrvTermData _caller,
- bool _isResult=false);
+ wxeReturn (ErlDrvTermData _port,
+ ErlDrvTermData _caller,
+ bool _isResult=false);
~wxeReturn();
@@ -130,7 +130,7 @@ private:
// WxeApp* wxe_app;
ErlDrvTermData caller;
- ErlDrvPort port;
+ ErlDrvTermData port;
// wxeMemEnv *memEnv;
wxErlDrvTermDataArray rt;
wxArrayDouble temp_float;
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 7b35fed672..6a0d649ccd 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-2012. All Rights Reserved.
+dnl Copyright Ericsson AB 2008-2013. 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,26 +80,26 @@ 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"
OBJC_CC=$CC
OBJC_CFLAGS=""
-
+CXXFLAGS=""
dnl NOTE: CPPFLAGS will be included in CFLAGS at the end
case $host_os in
darwin*)
@@ -111,12 +111,13 @@ case $host_os in
AC_MSG_RESULT([yes])
C_ONLY_FLAGS="-ObjC"
else
- dnl We are probebly trying to build with a non-Apple gcc,
+ dnl We are probably trying to build with a non-Apple gcc,
dnl which is good as long as we do not try to build Cocoa
dnl code. We need an Apple compiler for just that (Objective C)
AC_MSG_RESULT([no])
AC_MSG_CHECKING([for a Cocoa compliant Objective C compiler])
SEARCHFOR=""
+ SEARCHFORXX=""
save_IFS=$IFS
IFS=:
set $PATH
@@ -124,19 +125,29 @@ case $host_os in
while test X"$1" != X""; do
dnl Add all possible paths to a real apple gcc
SEARCHFOR="$1/gcc-apple-4.2 $SEARCHFOR"
+ SEARCHFORXX="$1/g++-apple-4.2 $SEARCHFORXX"
shift
done
dnl Add LLVM compilers, they will work in this case
SEARCHFOR="/usr/bin/clang /usr/bin/gcc $SEARCHFOR"
+ SEARCHFORXX="/usr/bin/clang /usr/bin/g++ $SEARCHFORXX"
APPLE_CC=""
+ APPLE_CXX=""
dnl SEARCHFOR is reversed, so we want to find the last existing
dnl executable in the list
+ IFS=" "
+ set $SEARCHFORXX
for x in $SEARCHFOR; do
if test -x $x; then
APPLE_CC=$x
fi
+ if test -x "$1"; then
+ APPLE_CXX="$1"
+ fi
+ shift
done
- if test X$APPLE_CC = X; then
+ IFS=$save_IFS
+ if test X$APPLE_CC = X -o X$APPLE_CXX = X; then
AC_MSG_RESULT([no])
dnl Complete failure, we cannot build Cocoa code
if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then
@@ -150,12 +161,17 @@ case $host_os in
else
dnl We think we found an Apple compiler and will add
dnl Apple specific options
- AC_MSG_RESULT($APPLE_CC)
+ AC_MSG_RESULT([$APPLE_CC ($APPLE_CXX)])
+ dnl We should use this compiler for all of wx - hack...
+ CC=$APPLE_CC
+ CXX=$APPLE_CXX
+ dnl Both clang and gcc accept these flags...
+ #CXXFLAGS="-x c++ $CXXFLAGS"
OBJC_CC=$APPLE_CC
OBJC_CFLAGS="-ObjC"
fi
fi
- CFLAGS=$saved_CFLAGS
+ CFLAGS="$saved_CFLAGS -Wno-deprecated-declarations"
CPPFLAGS="$CPPFLAGS -D_MACOSX $PTHR_CFLAGS"
;;
mingw32)
@@ -169,6 +185,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,12 +269,24 @@ 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"
+CXXFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS"
CFLAGS="$CFLAGS $CPPFLAGS $C_ONLY_FLAGS"
-DEBUG_CXXFLAGS="$DEBUG_CFLAGS $CPPFLAGS"
+DEBUG_CXXFLAGS="$CXXFLAGS $DEBUG_CFLAGS $CPPFLAGS"
DEBUG_CFLAGS="$DEBUG_CFLAGS $CPPFLAGS $C_ONLY_FLAGS"
AC_SUBST(DEBUG_CFLAGS)
AC_SUBST(DEBUG_CXXFLAGS)
@@ -308,7 +337,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 +432,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 +479,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 +659,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 95036bd7f0..a6e89d32a9 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>2012</year>
+ <year>2009</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,52 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add {silent_start, boolean()} option to wx:new/1 in order
+ to be able to suppress error messages during startup of
+ wx. (Thanks to H�kan Mattsson)</p>
+ <p>
+ Own Id: OTP-10585</p>
+ </item>
+ <item>
+ <p>
+ Fix wxTreeCtrl:getBoundingRect/2 and
+ wxTreeCtrl:hitTest/1. wxTreeCtrl:hitTest now returns a
+ tuple not bug compatible with previous releases but
+ needed.</p>
+ <p>
+ Own Id: OTP-10743</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The wx application now compiles and is usable with the
+ unstable development branch of wxWidgets-2.9. Some
+ functions are currently not available in wxWidgets-2.9
+ and their erlang counterparts are marked as deprecated.
+ They will generate an error if called when linked against
+ wxWidgets-2.9 libraries. This means that wx can now be
+ built on 64bit MacOsX, but keep in mind that
+ wxWidgets-2.9 is still a development branch and needs (a
+ lot) more work before it becomes stable.</p>
+ <p>
+ Own Id: OTP-10407 Aux Id: kunagi-262 [173] </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 0.99.2</title>
<section><title>Improvements and New Features</title>
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..7bb6736fdc 100644
--- a/lib/wx/examples/demo/demo_html_tagger.erl
+++ b/lib/wx/examples/demo/demo_html_tagger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..0f3296d0c0 100644
--- a/lib/wx/examples/demo/ex_button.erl
+++ b/lib/wx/examples/demo/ex_button.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..8e517ba852 100644
--- a/lib/wx/examples/demo/ex_canvas.erl
+++ b/lib/wx/examples/demo/ex_canvas.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..c8b27f8580 100644
--- a/lib/wx/examples/demo/ex_choices.erl
+++ b/lib/wx/examples/demo/ex_choices.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..f8a7cdd01b 100644
--- a/lib/wx/examples/demo/ex_cursor.erl
+++ b/lib/wx/examples/demo/ex_cursor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..2b9febfb64 100644
--- a/lib/wx/examples/demo/ex_dialogs.erl
+++ b/lib/wx/examples/demo/ex_dialogs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..c2273f80e0 100644
--- a/lib/wx/examples/demo/ex_frame_utils.erl
+++ b/lib/wx/examples/demo/ex_frame_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..5ec524fc37 100644
--- a/lib/wx/examples/demo/ex_gauge.erl
+++ b/lib/wx/examples/demo/ex_gauge.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..691eede3c4 100644
--- a/lib/wx/examples/demo/ex_grid.erl
+++ b/lib/wx/examples/demo/ex_grid.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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,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..f505dfcfe1 100644
--- a/lib/wx/examples/demo/ex_htmlWindow.erl
+++ b/lib/wx/examples/demo/ex_htmlWindow.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..a59b30b15c 100644
--- a/lib/wx/examples/demo/ex_listCtrl.erl
+++ b/lib/wx/examples/demo/ex_listCtrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..69b1e02f6b 100644
--- a/lib/wx/examples/demo/ex_notebook.erl
+++ b/lib/wx/examples/demo/ex_notebook.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..b12d1b3877 100644
--- a/lib/wx/examples/demo/ex_pickers.erl
+++ b/lib/wx/examples/demo/ex_pickers.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..c0d82444b6 100644
--- a/lib/wx/examples/demo/ex_popupMenu.erl
+++ b/lib/wx/examples/demo/ex_popupMenu.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..f615b81997 100644
--- a/lib/wx/examples/demo/ex_radioBox.erl
+++ b/lib/wx/examples/demo/ex_radioBox.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..8057d9b2c3 100644
--- a/lib/wx/examples/demo/ex_sashWindow.erl
+++ b/lib/wx/examples/demo/ex_sashWindow.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..5875198d38 100644
--- a/lib/wx/examples/demo/ex_sizers.erl
+++ b/lib/wx/examples/demo/ex_sizers.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..001e04a0f8 100644
--- a/lib/wx/examples/demo/ex_slider.erl
+++ b/lib/wx/examples/demo/ex_slider.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..06357d7d08 100644
--- a/lib/wx/examples/demo/ex_splitterWindow.erl
+++ b/lib/wx/examples/demo/ex_splitterWindow.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..1be033eac5 100644
--- a/lib/wx/examples/demo/ex_static.erl
+++ b/lib/wx/examples/demo/ex_static.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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..d2ce0cf95e 100644
--- a/lib/wx/examples/demo/ex_treeCtrl.erl
+++ b/lib/wx/examples/demo/ex_treeCtrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -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/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/wxNcPaintEvent.erl b/lib/wx/src/gen/wxNcPaintEvent.erl
deleted file mode 100644
index 4dd292a0be..0000000000
--- a/lib/wx/src/gen/wxNcPaintEvent.erl
+++ /dev/null
@@ -1,64 +0,0 @@
-%%
-%% %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%
-%% This file is generated DO NOT EDIT
-
-%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/stable/wx_wxncpaintevent.html">wxNcPaintEvent</a>.
-%% <dl><dt>Use {@link wxEvtHandler:connect/3.} with EventType:</dt>
-%% <dd><em>nc_paint</em></dd></dl>
-%% See also the message variant {@link wxEvtHandler:wxNcPaint(). #wxNcPaint{}} event record type.
-%%
-%% <p>This class is derived (and can use functions) from:
-%% <br />{@link wxEvent}
-%% </p>
-%% @type wxNcPaintEvent(). An object reference, The representation is internal
-%% and can be changed without notice. It can't be used for comparsion
-%% stored on disc or distributed for use on other nodes.
-
--module(wxNcPaintEvent).
--include("wxe.hrl").
--export([]).
-
-%% inherited exports
--export([getId/1,getSkipped/1,getTimestamp/1,isCommandEvent/1,parent_class/1,
- resumePropagation/2,shouldPropagate/1,skip/1,skip/2,stopPropagation/1]).
-
--export_type([wxNcPaintEvent/0]).
-%% @hidden
-parent_class(wxEvent) -> true;
-parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-
--type wxNcPaintEvent() :: wx:wx_object().
- %% From wxEvent
-%% @hidden
-stopPropagation(This) -> wxEvent:stopPropagation(This).
-%% @hidden
-skip(This, Options) -> wxEvent:skip(This, Options).
-%% @hidden
-skip(This) -> wxEvent:skip(This).
-%% @hidden
-shouldPropagate(This) -> wxEvent:shouldPropagate(This).
-%% @hidden
-resumePropagation(This,PropagationLevel) -> wxEvent:resumePropagation(This,PropagationLevel).
-%% @hidden
-isCommandEvent(This) -> wxEvent:isCommandEvent(This).
-%% @hidden
-getTimestamp(This) -> wxEvent:getTimestamp(This).
-%% @hidden
-getSkipped(This) -> wxEvent:getSkipped(This).
-%% @hidden
-getId(This) -> wxEvent:getId(This).
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/wxTreeCtrl.erl b/lib/wx/src/gen/wxTreeCtrl.erl
index dfa9e691ce..df2b9bed39 100644
--- a/lib/wx/src/gen/wxTreeCtrl.erl
+++ b/lib/wx/src/gen/wxTreeCtrl.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
@@ -35,21 +35,22 @@
-include("wxe.hrl").
-export([addRoot/2,addRoot/3,appendItem/3,appendItem/4,assignImageList/2,assignStateImageList/2,
collapse/2,collapseAndReset/2,create/2,create/3,delete/2,deleteAllItems/1,
- deleteChildren/2,destroy/1,editLabel/2,ensureVisible/2,expand/2,getBoundingRect/3,
- getBoundingRect/4,getChildrenCount/2,getChildrenCount/3,getCount/1,
+ deleteChildren/2,destroy/1,editLabel/2,ensureVisible/2,expand/2,getBoundingRect/2,
+ getBoundingRect/3,getChildrenCount/2,getChildrenCount/3,getCount/1,
getEditControl/1,getFirstChild/2,getFirstVisibleItem/1,getImageList/1,
getIndent/1,getItemBackgroundColour/2,getItemData/2,getItemFont/2,
getItemImage/2,getItemImage/3,getItemParent/2,getItemText/2,getItemTextColour/2,
getLastChild/2,getNextChild/3,getNextSibling/2,getNextVisible/2,getPrevSibling/2,
getPrevVisible/2,getRootItem/1,getSelection/1,getSelections/1,getStateImageList/1,
hitTest/2,insertItem/4,insertItem/5,isBold/2,isExpanded/2,isSelected/2,
- isVisible/2,itemHasChildren/2,new/0,new/1,new/2,prependItem/3,prependItem/4,
- scrollTo/2,selectItem/2,selectItem/3,setImageList/2,setIndent/2,setItemBackgroundColour/3,
- setItemBold/2,setItemBold/3,setItemData/3,setItemDropHighlight/2,
- setItemDropHighlight/3,setItemFont/3,setItemHasChildren/2,setItemHasChildren/3,
- setItemImage/3,setItemImage/4,setItemText/3,setItemTextColour/3,setStateImageList/2,
- setWindowStyle/2,sortChildren/2,toggle/2,toggleItemSelection/2,unselect/1,
- unselectAll/1,unselectItem/2]).
+ isTreeItemIdOk/1,isVisible/2,itemHasChildren/2,new/0,new/1,new/2,prependItem/3,
+ prependItem/4,scrollTo/2,selectItem/2,selectItem/3,setImageList/2,
+ setIndent/2,setItemBackgroundColour/3,setItemBold/2,setItemBold/3,
+ setItemData/3,setItemDropHighlight/2,setItemDropHighlight/3,setItemFont/3,
+ setItemHasChildren/2,setItemHasChildren/3,setItemImage/3,setItemImage/4,
+ setItemText/3,setItemTextColour/3,setStateImageList/2,setWindowStyle/2,
+ sortChildren/2,toggle/2,toggleItemSelection/2,unselect/1,unselectAll/1,
+ unselectItem/2]).
%% inherited exports
-export([cacheBestSize/2,captureMouse/1,center/1,center/2,centerOnParent/1,
@@ -303,26 +304,28 @@ expand(#wx_ref{type=ThisT,ref=ThisRef},Item)
wxe_util:cast(?wxTreeCtrl_Expand,
<<ThisRef:32/?UI,0:32,Item:64/?UI>>).
-%% @equiv getBoundingRect(This,Item,Rect, [])
--spec getBoundingRect(This, Item, Rect) -> boolean() when
- This::wxTreeCtrl(), Item::integer(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}.
+%% @equiv getBoundingRect(This,Item, [])
+-spec getBoundingRect(This, Item) -> Result when
+ Result ::{Res ::boolean(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}},
+ This::wxTreeCtrl(), Item::integer().
-getBoundingRect(This,Item,Rect={RectX,RectY,RectW,RectH})
- when is_record(This, wx_ref),is_integer(Item),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH) ->
- getBoundingRect(This,Item,Rect, []).
+getBoundingRect(This,Item)
+ when is_record(This, wx_ref),is_integer(Item) ->
+ getBoundingRect(This,Item, []).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtreectrl.html#wxtreectrlgetboundingrect">external documentation</a>.
--spec getBoundingRect(This, Item, Rect, [Option]) -> boolean() when
- This::wxTreeCtrl(), Item::integer(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()},
+-spec getBoundingRect(This, Item, [Option]) -> Result when
+ Result :: {Res ::boolean(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}},
+ This::wxTreeCtrl(), Item::integer(),
Option :: {textOnly, boolean()}.
-getBoundingRect(#wx_ref{type=ThisT,ref=ThisRef},Item,{RectX,RectY,RectW,RectH}, Options)
- when is_integer(Item),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH),is_list(Options) ->
+getBoundingRect(#wx_ref{type=ThisT,ref=ThisRef},Item, Options)
+ when is_integer(Item),is_list(Options) ->
?CLASS(ThisT,wxTreeCtrl),
MOpts = fun({textOnly, TextOnly}, Acc) -> [<<1:32/?UI,(wxe_util:from_bool(TextOnly)):32/?UI>>|Acc];
(BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
wxe_util:call(?wxTreeCtrl_GetBoundingRect,
- <<ThisRef:32/?UI,0:32,Item:64/?UI,RectX:32/?UI,RectY:32/?UI,RectW:32/?UI,RectH:32/?UI, BinOpt/binary>>).
+ <<ThisRef:32/?UI,0:32,Item:64/?UI, BinOpt/binary>>).
%% @equiv getChildrenCount(This,Item, [])
-spec getChildrenCount(This, Item) -> integer() when
@@ -561,7 +564,8 @@ getStateImageList(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtreectrl.html#wxtreectrlhittest">external documentation</a>.
--spec hitTest(This, Point) -> integer() when
+-spec hitTest(This, Point) -> Result when
+ Result ::{Res ::integer(), Flags::integer()},
This::wxTreeCtrl(), Point::{X::integer(), Y::integer()}.
hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY})
when is_integer(PointX),is_integer(PointY) ->
@@ -640,6 +644,14 @@ itemHasChildren(#wx_ref{type=ThisT,ref=ThisRef},Item)
wxe_util:call(?wxTreeCtrl_ItemHasChildren,
<<ThisRef:32/?UI,0:32,Item:64/?UI>>).
+%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtreectrl.html#wxtreectrlistreeitemidok">external documentation</a>.
+-spec isTreeItemIdOk(Id) -> boolean() when
+ Id::integer().
+isTreeItemIdOk(Id)
+ when is_integer(Id) ->
+ wxe_util:call(?wxTreeCtrl_IsTreeItemIdOk,
+ <<Id:64/?UI>>).
+
%% @equiv prependItem(This,Parent,Text, [])
-spec prependItem(This, Parent, Text) -> integer() when
This::wxTreeCtrl(), Parent::integer(), Text::unicode:chardata().
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..29cb2b05e6 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -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
@@ -1877,1284 +1877,1284 @@ wxdebug_table() ->
{2050, {wxTreeCtrl, getSelection, 0}},
{2051, {wxTreeCtrl, getSelections, 1}},
{2052, {wxTreeCtrl, getStateImageList, 0}},
- {2053, {wxTreeCtrl, hitTest, 1}},
+ {2053, {wxTreeCtrl, hitTest, 2}},
{2055, {wxTreeCtrl, insertItem, 4}},
{2056, {wxTreeCtrl, isBold, 1}},
{2057, {wxTreeCtrl, isExpanded, 1}},
{2058, {wxTreeCtrl, isSelected, 1}},
{2059, {wxTreeCtrl, isVisible, 1}},
{2060, {wxTreeCtrl, itemHasChildren, 1}},
- {2061, {wxTreeCtrl, prependItem, 3}},
- {2062, {wxTreeCtrl, scrollTo, 1}},
- {2063, {wxTreeCtrl, selectItem_1, 1}},
- {2064, {wxTreeCtrl, selectItem_2, 2}},
- {2065, {wxTreeCtrl, setIndent, 1}},
- {2066, {wxTreeCtrl, setImageList, 1}},
- {2067, {wxTreeCtrl, setItemBackgroundColour, 2}},
- {2068, {wxTreeCtrl, setItemBold, 2}},
- {2069, {wxTreeCtrl, setItemData, 2}},
- {2070, {wxTreeCtrl, setItemDropHighlight, 2}},
- {2071, {wxTreeCtrl, setItemFont, 2}},
- {2072, {wxTreeCtrl, setItemHasChildren, 2}},
- {2073, {wxTreeCtrl, setItemImage_2, 2}},
- {2074, {wxTreeCtrl, setItemImage_3, 3}},
- {2075, {wxTreeCtrl, setItemText, 2}},
- {2076, {wxTreeCtrl, setItemTextColour, 2}},
- {2077, {wxTreeCtrl, setStateImageList, 1}},
- {2078, {wxTreeCtrl, setWindowStyle, 1}},
- {2079, {wxTreeCtrl, sortChildren, 1}},
- {2080, {wxTreeCtrl, toggle, 1}},
- {2081, {wxTreeCtrl, toggleItemSelection, 1}},
- {2082, {wxTreeCtrl, unselect, 0}},
- {2083, {wxTreeCtrl, unselectAll, 0}},
- {2084, {wxTreeCtrl, unselectItem, 1}},
- {2085, {wxScrollBar, new_0, 0}},
- {2086, {wxScrollBar, new_3, 3}},
- {2087, {wxScrollBar, destruct, 0}},
- {2088, {wxScrollBar, create, 3}},
- {2089, {wxScrollBar, getRange, 0}},
- {2090, {wxScrollBar, getPageSize, 0}},
- {2091, {wxScrollBar, getThumbPosition, 0}},
- {2092, {wxScrollBar, getThumbSize, 0}},
- {2093, {wxScrollBar, setThumbPosition, 1}},
- {2094, {wxScrollBar, setScrollbar, 5}},
- {2096, {wxSpinButton, new_2, 2}},
- {2097, {wxSpinButton, new_0, 0}},
- {2098, {wxSpinButton, create, 2}},
- {2099, {wxSpinButton, getMax, 0}},
- {2100, {wxSpinButton, getMin, 0}},
- {2101, {wxSpinButton, getValue, 0}},
- {2102, {wxSpinButton, setRange, 2}},
- {2103, {wxSpinButton, setValue, 1}},
- {2104, {wxSpinButton, 'Destroy', undefined}},
- {2105, {wxSpinCtrl, new_0, 0}},
- {2106, {wxSpinCtrl, new_2, 2}},
- {2108, {wxSpinCtrl, create, 2}},
- {2111, {wxSpinCtrl, setValue_1_1, 1}},
- {2112, {wxSpinCtrl, setValue_1_0, 1}},
- {2114, {wxSpinCtrl, getValue, 0}},
- {2116, {wxSpinCtrl, setRange, 2}},
- {2117, {wxSpinCtrl, setSelection, 2}},
- {2119, {wxSpinCtrl, getMin, 0}},
- {2121, {wxSpinCtrl, getMax, 0}},
- {2122, {wxSpinCtrl, 'Destroy', undefined}},
- {2123, {wxStaticText, new_0, 0}},
- {2124, {wxStaticText, new_4, 4}},
- {2125, {wxStaticText, create, 4}},
- {2126, {wxStaticText, getLabel, 0}},
- {2127, {wxStaticText, setLabel, 1}},
- {2128, {wxStaticText, wrap, 1}},
- {2129, {wxStaticText, 'Destroy', undefined}},
- {2130, {wxStaticBitmap, new_0, 0}},
- {2131, {wxStaticBitmap, new_4, 4}},
- {2132, {wxStaticBitmap, create, 4}},
- {2133, {wxStaticBitmap, getBitmap, 0}},
- {2134, {wxStaticBitmap, setBitmap, 1}},
- {2135, {wxStaticBitmap, 'Destroy', undefined}},
- {2136, {wxRadioBox, new, 7}},
- {2138, {wxRadioBox, destruct, 0}},
- {2139, {wxRadioBox, create, 7}},
- {2140, {wxRadioBox, enable_2, 2}},
- {2141, {wxRadioBox, enable_1, 1}},
- {2142, {wxRadioBox, getSelection, 0}},
- {2143, {wxRadioBox, getString, 1}},
- {2144, {wxRadioBox, setSelection, 1}},
- {2145, {wxRadioBox, show_2, 2}},
- {2146, {wxRadioBox, show_1, 1}},
- {2147, {wxRadioBox, getColumnCount, 0}},
- {2148, {wxRadioBox, getItemHelpText, 1}},
- {2149, {wxRadioBox, getItemToolTip, 1}},
- {2151, {wxRadioBox, getItemFromPoint, 1}},
- {2152, {wxRadioBox, getRowCount, 0}},
- {2153, {wxRadioBox, isItemEnabled, 1}},
- {2154, {wxRadioBox, isItemShown, 1}},
- {2155, {wxRadioBox, setItemHelpText, 2}},
- {2156, {wxRadioBox, setItemToolTip, 2}},
- {2157, {wxRadioButton, new_0, 0}},
- {2158, {wxRadioButton, new_4, 4}},
- {2159, {wxRadioButton, create, 4}},
- {2160, {wxRadioButton, getValue, 0}},
- {2161, {wxRadioButton, setValue, 1}},
- {2162, {wxRadioButton, 'Destroy', undefined}},
- {2164, {wxSlider, new_6, 6}},
- {2165, {wxSlider, new_0, 0}},
- {2166, {wxSlider, create, 6}},
- {2167, {wxSlider, getLineSize, 0}},
- {2168, {wxSlider, getMax, 0}},
- {2169, {wxSlider, getMin, 0}},
- {2170, {wxSlider, getPageSize, 0}},
- {2171, {wxSlider, getThumbLength, 0}},
- {2172, {wxSlider, getValue, 0}},
- {2173, {wxSlider, setLineSize, 1}},
- {2174, {wxSlider, setPageSize, 1}},
- {2175, {wxSlider, setRange, 2}},
- {2176, {wxSlider, setThumbLength, 1}},
- {2177, {wxSlider, setValue, 1}},
- {2178, {wxSlider, 'Destroy', undefined}},
- {2180, {wxDialog, new_4, 4}},
- {2181, {wxDialog, new_0, 0}},
- {2183, {wxDialog, destruct, 0}},
- {2184, {wxDialog, create, 4}},
- {2185, {wxDialog, createButtonSizer, 1}},
- {2186, {wxDialog, createStdDialogButtonSizer, 1}},
- {2187, {wxDialog, endModal, 1}},
- {2188, {wxDialog, getAffirmativeId, 0}},
- {2189, {wxDialog, getReturnCode, 0}},
- {2190, {wxDialog, isModal, 0}},
- {2191, {wxDialog, setAffirmativeId, 1}},
- {2192, {wxDialog, setReturnCode, 1}},
- {2193, {wxDialog, show, 1}},
- {2194, {wxDialog, showModal, 0}},
- {2195, {wxColourDialog, new_0, 0}},
- {2196, {wxColourDialog, new_2, 2}},
- {2197, {wxColourDialog, destruct, 0}},
- {2198, {wxColourDialog, create, 2}},
- {2199, {wxColourDialog, getColourData, 0}},
- {2200, {wxColourData, new_0, 0}},
- {2201, {wxColourData, new_1, 1}},
- {2202, {wxColourData, destruct, 0}},
- {2203, {wxColourData, getChooseFull, 0}},
- {2204, {wxColourData, getColour, 0}},
- {2206, {wxColourData, getCustomColour, 1}},
- {2207, {wxColourData, setChooseFull, 1}},
- {2208, {wxColourData, setColour, 1}},
- {2209, {wxColourData, setCustomColour, 2}},
- {2210, {wxPalette, new_0, 0}},
- {2211, {wxPalette, new_4, 4}},
- {2213, {wxPalette, destruct, 0}},
- {2214, {wxPalette, create, 4}},
- {2215, {wxPalette, getColoursCount, 0}},
- {2216, {wxPalette, getPixel, 3}},
- {2217, {wxPalette, getRGB, 4}},
- {2218, {wxPalette, isOk, 0}},
- {2222, {wxDirDialog, new, 2}},
- {2223, {wxDirDialog, destruct, 0}},
- {2224, {wxDirDialog, getPath, 0}},
- {2225, {wxDirDialog, getMessage, 0}},
- {2226, {wxDirDialog, setMessage, 1}},
- {2227, {wxDirDialog, setPath, 1}},
- {2231, {wxFileDialog, new, 2}},
- {2232, {wxFileDialog, destruct, 0}},
- {2233, {wxFileDialog, getDirectory, 0}},
- {2234, {wxFileDialog, getFilename, 0}},
- {2235, {wxFileDialog, getFilenames, 1}},
- {2236, {wxFileDialog, getFilterIndex, 0}},
- {2237, {wxFileDialog, getMessage, 0}},
- {2238, {wxFileDialog, getPath, 0}},
- {2239, {wxFileDialog, getPaths, 1}},
- {2240, {wxFileDialog, getWildcard, 0}},
- {2241, {wxFileDialog, setDirectory, 1}},
- {2242, {wxFileDialog, setFilename, 1}},
- {2243, {wxFileDialog, setFilterIndex, 1}},
- {2244, {wxFileDialog, setMessage, 1}},
- {2245, {wxFileDialog, setPath, 1}},
- {2246, {wxFileDialog, setWildcard, 1}},
- {2247, {wxPickerBase, setInternalMargin, 1}},
- {2248, {wxPickerBase, getInternalMargin, 0}},
- {2249, {wxPickerBase, setTextCtrlProportion, 1}},
- {2250, {wxPickerBase, setPickerCtrlProportion, 1}},
- {2251, {wxPickerBase, getTextCtrlProportion, 0}},
- {2252, {wxPickerBase, getPickerCtrlProportion, 0}},
- {2253, {wxPickerBase, hasTextCtrl, 0}},
- {2254, {wxPickerBase, getTextCtrl, 0}},
- {2255, {wxPickerBase, isTextCtrlGrowable, 0}},
- {2256, {wxPickerBase, setPickerCtrlGrowable, 1}},
- {2257, {wxPickerBase, setTextCtrlGrowable, 1}},
- {2258, {wxPickerBase, isPickerCtrlGrowable, 0}},
- {2259, {wxFilePickerCtrl, new_0, 0}},
- {2260, {wxFilePickerCtrl, new_3, 3}},
- {2261, {wxFilePickerCtrl, create, 3}},
- {2262, {wxFilePickerCtrl, getPath, 0}},
- {2263, {wxFilePickerCtrl, setPath, 1}},
- {2264, {wxFilePickerCtrl, 'Destroy', undefined}},
- {2265, {wxDirPickerCtrl, new_0, 0}},
- {2266, {wxDirPickerCtrl, new_3, 3}},
- {2267, {wxDirPickerCtrl, create, 3}},
- {2268, {wxDirPickerCtrl, getPath, 0}},
- {2269, {wxDirPickerCtrl, setPath, 1}},
- {2270, {wxDirPickerCtrl, 'Destroy', undefined}},
- {2271, {wxColourPickerCtrl, new_0, 0}},
- {2272, {wxColourPickerCtrl, new_3, 3}},
- {2273, {wxColourPickerCtrl, create, 3}},
- {2274, {wxColourPickerCtrl, getColour, 0}},
- {2275, {wxColourPickerCtrl, setColour_1_1, 1}},
- {2276, {wxColourPickerCtrl, setColour_1_0, 1}},
- {2277, {wxColourPickerCtrl, 'Destroy', undefined}},
- {2278, {wxDatePickerCtrl, new_0, 0}},
- {2279, {wxDatePickerCtrl, new_3, 3}},
- {2280, {wxDatePickerCtrl, getRange, 2}},
- {2281, {wxDatePickerCtrl, getValue, 0}},
- {2282, {wxDatePickerCtrl, setRange, 2}},
- {2283, {wxDatePickerCtrl, setValue, 1}},
- {2284, {wxDatePickerCtrl, 'Destroy', undefined}},
- {2285, {wxFontPickerCtrl, new_0, 0}},
- {2286, {wxFontPickerCtrl, new_3, 3}},
- {2287, {wxFontPickerCtrl, create, 3}},
- {2288, {wxFontPickerCtrl, getSelectedFont, 0}},
- {2289, {wxFontPickerCtrl, setSelectedFont, 1}},
- {2290, {wxFontPickerCtrl, getMaxPointSize, 0}},
- {2291, {wxFontPickerCtrl, setMaxPointSize, 1}},
- {2292, {wxFontPickerCtrl, 'Destroy', undefined}},
- {2295, {wxFindReplaceDialog, new_0, 0}},
- {2296, {wxFindReplaceDialog, new_4, 4}},
- {2297, {wxFindReplaceDialog, destruct, 0}},
- {2298, {wxFindReplaceDialog, create, 4}},
- {2299, {wxFindReplaceDialog, getData, 0}},
- {2300, {wxFindReplaceData, new_0, 0}},
- {2301, {wxFindReplaceData, new_1, 1}},
- {2302, {wxFindReplaceData, getFindString, 0}},
- {2303, {wxFindReplaceData, getReplaceString, 0}},
- {2304, {wxFindReplaceData, getFlags, 0}},
- {2305, {wxFindReplaceData, setFlags, 1}},
- {2306, {wxFindReplaceData, setFindString, 1}},
- {2307, {wxFindReplaceData, setReplaceString, 1}},
- {2308, {wxFindReplaceData, 'Destroy', undefined}},
- {2309, {wxMultiChoiceDialog, new_0, 0}},
- {2311, {wxMultiChoiceDialog, new_5, 5}},
- {2312, {wxMultiChoiceDialog, getSelections, 0}},
- {2313, {wxMultiChoiceDialog, setSelections, 1}},
- {2314, {wxMultiChoiceDialog, 'Destroy', undefined}},
- {2315, {wxSingleChoiceDialog, new_0, 0}},
- {2317, {wxSingleChoiceDialog, new_5, 5}},
- {2318, {wxSingleChoiceDialog, getSelection, 0}},
- {2319, {wxSingleChoiceDialog, getStringSelection, 0}},
- {2320, {wxSingleChoiceDialog, setSelection, 1}},
- {2321, {wxSingleChoiceDialog, 'Destroy', undefined}},
- {2322, {wxTextEntryDialog, new, 3}},
- {2323, {wxTextEntryDialog, getValue, 0}},
- {2324, {wxTextEntryDialog, setValue, 1}},
- {2325, {wxTextEntryDialog, 'Destroy', undefined}},
- {2326, {wxPasswordEntryDialog, new, 3}},
- {2327, {wxPasswordEntryDialog, 'Destroy', undefined}},
- {2328, {wxFontData, new_0, 0}},
- {2329, {wxFontData, new_1, 1}},
- {2330, {wxFontData, destruct, 0}},
- {2331, {wxFontData, enableEffects, 1}},
- {2332, {wxFontData, getAllowSymbols, 0}},
- {2333, {wxFontData, getColour, 0}},
- {2334, {wxFontData, getChosenFont, 0}},
- {2335, {wxFontData, getEnableEffects, 0}},
- {2336, {wxFontData, getInitialFont, 0}},
- {2337, {wxFontData, getShowHelp, 0}},
- {2338, {wxFontData, setAllowSymbols, 1}},
- {2339, {wxFontData, setChosenFont, 1}},
- {2340, {wxFontData, setColour, 1}},
- {2341, {wxFontData, setInitialFont, 1}},
- {2342, {wxFontData, setRange, 2}},
- {2343, {wxFontData, setShowHelp, 1}},
- {2347, {wxFontDialog, new_0, 0}},
- {2349, {wxFontDialog, new_2, 2}},
- {2351, {wxFontDialog, create, 2}},
- {2352, {wxFontDialog, getFontData, 0}},
- {2354, {wxFontDialog, 'Destroy', undefined}},
- {2355, {wxProgressDialog, new, 3}},
- {2356, {wxProgressDialog, destruct, 0}},
- {2357, {wxProgressDialog, resume, 0}},
- {2358, {wxProgressDialog, update_2, 2}},
- {2359, {wxProgressDialog, update_0, 0}},
- {2360, {wxMessageDialog, new, 3}},
- {2361, {wxMessageDialog, destruct, 0}},
- {2362, {wxPageSetupDialog, new, 2}},
- {2363, {wxPageSetupDialog, destruct, 0}},
- {2364, {wxPageSetupDialog, getPageSetupData, 0}},
- {2365, {wxPageSetupDialog, showModal, 0}},
- {2366, {wxPageSetupDialogData, new_0, 0}},
- {2367, {wxPageSetupDialogData, new_1_0, 1}},
- {2368, {wxPageSetupDialogData, new_1_1, 1}},
- {2369, {wxPageSetupDialogData, destruct, 0}},
- {2370, {wxPageSetupDialogData, enableHelp, 1}},
- {2371, {wxPageSetupDialogData, enableMargins, 1}},
- {2372, {wxPageSetupDialogData, enableOrientation, 1}},
- {2373, {wxPageSetupDialogData, enablePaper, 1}},
- {2374, {wxPageSetupDialogData, enablePrinter, 1}},
- {2375, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
- {2376, {wxPageSetupDialogData, getEnableMargins, 0}},
- {2377, {wxPageSetupDialogData, getEnableOrientation, 0}},
- {2378, {wxPageSetupDialogData, getEnablePaper, 0}},
- {2379, {wxPageSetupDialogData, getEnablePrinter, 0}},
- {2380, {wxPageSetupDialogData, getEnableHelp, 0}},
- {2381, {wxPageSetupDialogData, getDefaultInfo, 0}},
- {2382, {wxPageSetupDialogData, getMarginTopLeft, 0}},
- {2383, {wxPageSetupDialogData, getMarginBottomRight, 0}},
- {2384, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
- {2385, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
- {2386, {wxPageSetupDialogData, getPaperId, 0}},
- {2387, {wxPageSetupDialogData, getPaperSize, 0}},
- {2389, {wxPageSetupDialogData, getPrintData, 0}},
- {2390, {wxPageSetupDialogData, isOk, 0}},
- {2391, {wxPageSetupDialogData, setDefaultInfo, 1}},
- {2392, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
- {2393, {wxPageSetupDialogData, setMarginTopLeft, 1}},
- {2394, {wxPageSetupDialogData, setMarginBottomRight, 1}},
- {2395, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
- {2396, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
- {2397, {wxPageSetupDialogData, setPaperId, 1}},
- {2398, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
- {2399, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
- {2400, {wxPageSetupDialogData, setPrintData, 1}},
- {2401, {wxPrintDialog, new_2_0, 2}},
- {2402, {wxPrintDialog, new_2_1, 2}},
- {2403, {wxPrintDialog, destruct, 0}},
- {2404, {wxPrintDialog, getPrintDialogData, 0}},
- {2405, {wxPrintDialog, getPrintDC, 0}},
- {2406, {wxPrintDialogData, new_0, 0}},
- {2407, {wxPrintDialogData, new_1_1, 1}},
- {2408, {wxPrintDialogData, new_1_0, 1}},
- {2409, {wxPrintDialogData, destruct, 0}},
- {2410, {wxPrintDialogData, enableHelp, 1}},
- {2411, {wxPrintDialogData, enablePageNumbers, 1}},
- {2412, {wxPrintDialogData, enablePrintToFile, 1}},
- {2413, {wxPrintDialogData, enableSelection, 1}},
- {2414, {wxPrintDialogData, getAllPages, 0}},
- {2415, {wxPrintDialogData, getCollate, 0}},
- {2416, {wxPrintDialogData, getFromPage, 0}},
- {2417, {wxPrintDialogData, getMaxPage, 0}},
- {2418, {wxPrintDialogData, getMinPage, 0}},
- {2419, {wxPrintDialogData, getNoCopies, 0}},
- {2420, {wxPrintDialogData, getPrintData, 0}},
- {2421, {wxPrintDialogData, getPrintToFile, 0}},
- {2422, {wxPrintDialogData, getSelection, 0}},
- {2423, {wxPrintDialogData, getToPage, 0}},
- {2424, {wxPrintDialogData, isOk, 0}},
- {2425, {wxPrintDialogData, setCollate, 1}},
- {2426, {wxPrintDialogData, setFromPage, 1}},
- {2427, {wxPrintDialogData, setMaxPage, 1}},
- {2428, {wxPrintDialogData, setMinPage, 1}},
- {2429, {wxPrintDialogData, setNoCopies, 1}},
- {2430, {wxPrintDialogData, setPrintData, 1}},
- {2431, {wxPrintDialogData, setPrintToFile, 1}},
- {2432, {wxPrintDialogData, setSelection, 1}},
- {2433, {wxPrintDialogData, setToPage, 1}},
- {2434, {wxPrintData, new_0, 0}},
- {2435, {wxPrintData, new_1, 1}},
- {2436, {wxPrintData, destruct, 0}},
- {2437, {wxPrintData, getCollate, 0}},
- {2438, {wxPrintData, getBin, 0}},
- {2439, {wxPrintData, getColour, 0}},
- {2440, {wxPrintData, getDuplex, 0}},
- {2441, {wxPrintData, getNoCopies, 0}},
- {2442, {wxPrintData, getOrientation, 0}},
- {2443, {wxPrintData, getPaperId, 0}},
- {2444, {wxPrintData, getPrinterName, 0}},
- {2445, {wxPrintData, getQuality, 0}},
- {2446, {wxPrintData, isOk, 0}},
- {2447, {wxPrintData, setBin, 1}},
- {2448, {wxPrintData, setCollate, 1}},
- {2449, {wxPrintData, setColour, 1}},
- {2450, {wxPrintData, setDuplex, 1}},
- {2451, {wxPrintData, setNoCopies, 1}},
- {2452, {wxPrintData, setOrientation, 1}},
- {2453, {wxPrintData, setPaperId, 1}},
- {2454, {wxPrintData, setPrinterName, 1}},
- {2455, {wxPrintData, setQuality, 1}},
- {2458, {wxPrintPreview, new_2, 2}},
- {2459, {wxPrintPreview, new_3, 3}},
- {2461, {wxPrintPreview, destruct, 0}},
- {2462, {wxPrintPreview, getCanvas, 0}},
- {2463, {wxPrintPreview, getCurrentPage, 0}},
- {2464, {wxPrintPreview, getFrame, 0}},
- {2465, {wxPrintPreview, getMaxPage, 0}},
- {2466, {wxPrintPreview, getMinPage, 0}},
- {2467, {wxPrintPreview, getPrintout, 0}},
- {2468, {wxPrintPreview, getPrintoutForPrinting, 0}},
- {2469, {wxPrintPreview, isOk, 0}},
- {2470, {wxPrintPreview, paintPage, 2}},
- {2471, {wxPrintPreview, print, 1}},
- {2472, {wxPrintPreview, renderPage, 1}},
- {2473, {wxPrintPreview, setCanvas, 1}},
- {2474, {wxPrintPreview, setCurrentPage, 1}},
- {2475, {wxPrintPreview, setFrame, 1}},
- {2476, {wxPrintPreview, setPrintout, 1}},
- {2477, {wxPrintPreview, setZoom, 1}},
- {2478, {wxPreviewFrame, new, 3}},
- {2479, {wxPreviewFrame, destruct, 0}},
- {2480, {wxPreviewFrame, createControlBar, 0}},
- {2481, {wxPreviewFrame, createCanvas, 0}},
- {2482, {wxPreviewFrame, initialize, 0}},
- {2483, {wxPreviewFrame, onCloseWindow, 1}},
- {2484, {wxPreviewControlBar, new, 4}},
- {2485, {wxPreviewControlBar, destruct, 0}},
- {2486, {wxPreviewControlBar, createButtons, 0}},
- {2487, {wxPreviewControlBar, getPrintPreview, 0}},
- {2488, {wxPreviewControlBar, getZoomControl, 0}},
- {2489, {wxPreviewControlBar, setZoomControl, 1}},
- {2491, {wxPrinter, new, 1}},
- {2492, {wxPrinter, createAbortWindow, 2}},
- {2493, {wxPrinter, getAbort, 0}},
- {2494, {wxPrinter, getLastError, 0}},
- {2495, {wxPrinter, getPrintDialogData, 0}},
- {2496, {wxPrinter, print, 3}},
- {2497, {wxPrinter, printDialog, 1}},
- {2498, {wxPrinter, reportError, 3}},
- {2499, {wxPrinter, setup, 1}},
- {2500, {wxPrinter, 'Destroy', undefined}},
- {2501, {wxXmlResource, new_1, 1}},
- {2502, {wxXmlResource, new_2, 2}},
- {2503, {wxXmlResource, destruct, 0}},
- {2504, {wxXmlResource, attachUnknownControl, 3}},
- {2505, {wxXmlResource, clearHandlers, 0}},
- {2506, {wxXmlResource, compareVersion, 4}},
- {2507, {wxXmlResource, get, 0}},
- {2508, {wxXmlResource, getFlags, 0}},
- {2509, {wxXmlResource, getVersion, 0}},
- {2510, {wxXmlResource, getXRCID, 2}},
- {2511, {wxXmlResource, initAllHandlers, 0}},
- {2512, {wxXmlResource, load, 1}},
- {2513, {wxXmlResource, loadBitmap, 1}},
- {2514, {wxXmlResource, loadDialog_2, 2}},
- {2515, {wxXmlResource, loadDialog_3, 3}},
- {2516, {wxXmlResource, loadFrame_2, 2}},
- {2517, {wxXmlResource, loadFrame_3, 3}},
- {2518, {wxXmlResource, loadIcon, 1}},
- {2519, {wxXmlResource, loadMenu, 1}},
- {2520, {wxXmlResource, loadMenuBar_2, 2}},
- {2521, {wxXmlResource, loadMenuBar_1, 1}},
- {2522, {wxXmlResource, loadPanel_2, 2}},
- {2523, {wxXmlResource, loadPanel_3, 3}},
- {2524, {wxXmlResource, loadToolBar, 2}},
- {2525, {wxXmlResource, set, 1}},
- {2526, {wxXmlResource, setFlags, 1}},
- {2527, {wxXmlResource, unload, 1}},
- {2528, {wxXmlResource, xrcctrl, 3}},
- {2529, {wxHtmlEasyPrinting, new, 1}},
- {2530, {wxHtmlEasyPrinting, destruct, 0}},
- {2531, {wxHtmlEasyPrinting, getPrintData, 0}},
- {2532, {wxHtmlEasyPrinting, getPageSetupData, 0}},
- {2533, {wxHtmlEasyPrinting, previewFile, 1}},
- {2534, {wxHtmlEasyPrinting, previewText, 2}},
- {2535, {wxHtmlEasyPrinting, printFile, 1}},
- {2536, {wxHtmlEasyPrinting, printText, 2}},
- {2537, {wxHtmlEasyPrinting, pageSetup, 0}},
- {2538, {wxHtmlEasyPrinting, setFonts, 3}},
- {2539, {wxHtmlEasyPrinting, setHeader, 2}},
- {2540, {wxHtmlEasyPrinting, setFooter, 2}},
- {2542, {wxGLCanvas, new_2, 2}},
- {2543, {wxGLCanvas, new_3_1, 3}},
- {2544, {wxGLCanvas, new_3_0, 3}},
- {2545, {wxGLCanvas, getContext, 0}},
- {2547, {wxGLCanvas, setCurrent, 0}},
- {2548, {wxGLCanvas, swapBuffers, 0}},
- {2549, {wxGLCanvas, 'Destroy', undefined}},
- {2550, {wxAuiManager, new, 1}},
- {2551, {wxAuiManager, destruct, 0}},
- {2552, {wxAuiManager, addPane_2_1, 2}},
- {2553, {wxAuiManager, addPane_3, 3}},
- {2554, {wxAuiManager, addPane_2_0, 2}},
- {2555, {wxAuiManager, detachPane, 1}},
- {2556, {wxAuiManager, getAllPanes, 0}},
- {2557, {wxAuiManager, getArtProvider, 0}},
- {2558, {wxAuiManager, getDockSizeConstraint, 2}},
- {2559, {wxAuiManager, getFlags, 0}},
- {2560, {wxAuiManager, getManagedWindow, 0}},
- {2561, {wxAuiManager, getManager, 1}},
- {2562, {wxAuiManager, getPane_1_1, 1}},
- {2563, {wxAuiManager, getPane_1_0, 1}},
- {2564, {wxAuiManager, hideHint, 0}},
- {2565, {wxAuiManager, insertPane, 3}},
- {2566, {wxAuiManager, loadPaneInfo, 2}},
- {2567, {wxAuiManager, loadPerspective, 2}},
- {2568, {wxAuiManager, savePaneInfo, 1}},
- {2569, {wxAuiManager, savePerspective, 0}},
- {2570, {wxAuiManager, setArtProvider, 1}},
- {2571, {wxAuiManager, setDockSizeConstraint, 2}},
- {2572, {wxAuiManager, setFlags, 1}},
- {2573, {wxAuiManager, setManagedWindow, 1}},
- {2574, {wxAuiManager, showHint, 1}},
- {2575, {wxAuiManager, unInit, 0}},
- {2576, {wxAuiManager, update, 0}},
- {2577, {wxAuiPaneInfo, new_0, 0}},
- {2578, {wxAuiPaneInfo, new_1, 1}},
- {2579, {wxAuiPaneInfo, destruct, 0}},
- {2580, {wxAuiPaneInfo, bestSize_1, 1}},
- {2581, {wxAuiPaneInfo, bestSize_2, 2}},
- {2582, {wxAuiPaneInfo, bottom, 0}},
- {2583, {wxAuiPaneInfo, bottomDockable, 1}},
- {2584, {wxAuiPaneInfo, caption, 1}},
- {2585, {wxAuiPaneInfo, captionVisible, 1}},
- {2586, {wxAuiPaneInfo, centre, 0}},
- {2587, {wxAuiPaneInfo, centrePane, 0}},
- {2588, {wxAuiPaneInfo, closeButton, 1}},
- {2589, {wxAuiPaneInfo, defaultPane, 0}},
- {2590, {wxAuiPaneInfo, destroyOnClose, 1}},
- {2591, {wxAuiPaneInfo, direction, 1}},
- {2592, {wxAuiPaneInfo, dock, 0}},
- {2593, {wxAuiPaneInfo, dockable, 1}},
- {2594, {wxAuiPaneInfo, fixed, 0}},
- {2595, {wxAuiPaneInfo, float, 0}},
- {2596, {wxAuiPaneInfo, floatable, 1}},
- {2597, {wxAuiPaneInfo, floatingPosition_1, 1}},
- {2598, {wxAuiPaneInfo, floatingPosition_2, 2}},
- {2599, {wxAuiPaneInfo, floatingSize_1, 1}},
- {2600, {wxAuiPaneInfo, floatingSize_2, 2}},
- {2601, {wxAuiPaneInfo, gripper, 1}},
- {2602, {wxAuiPaneInfo, gripperTop, 1}},
- {2603, {wxAuiPaneInfo, hasBorder, 0}},
- {2604, {wxAuiPaneInfo, hasCaption, 0}},
- {2605, {wxAuiPaneInfo, hasCloseButton, 0}},
- {2606, {wxAuiPaneInfo, hasFlag, 1}},
- {2607, {wxAuiPaneInfo, hasGripper, 0}},
- {2608, {wxAuiPaneInfo, hasGripperTop, 0}},
- {2609, {wxAuiPaneInfo, hasMaximizeButton, 0}},
- {2610, {wxAuiPaneInfo, hasMinimizeButton, 0}},
- {2611, {wxAuiPaneInfo, hasPinButton, 0}},
- {2612, {wxAuiPaneInfo, hide, 0}},
- {2613, {wxAuiPaneInfo, isBottomDockable, 0}},
- {2614, {wxAuiPaneInfo, isDocked, 0}},
- {2615, {wxAuiPaneInfo, isFixed, 0}},
- {2616, {wxAuiPaneInfo, isFloatable, 0}},
- {2617, {wxAuiPaneInfo, isFloating, 0}},
- {2618, {wxAuiPaneInfo, isLeftDockable, 0}},
- {2619, {wxAuiPaneInfo, isMovable, 0}},
- {2620, {wxAuiPaneInfo, isOk, 0}},
- {2621, {wxAuiPaneInfo, isResizable, 0}},
- {2622, {wxAuiPaneInfo, isRightDockable, 0}},
- {2623, {wxAuiPaneInfo, isShown, 0}},
- {2624, {wxAuiPaneInfo, isToolbar, 0}},
- {2625, {wxAuiPaneInfo, isTopDockable, 0}},
- {2626, {wxAuiPaneInfo, layer, 1}},
- {2627, {wxAuiPaneInfo, left, 0}},
- {2628, {wxAuiPaneInfo, leftDockable, 1}},
- {2629, {wxAuiPaneInfo, maxSize_1, 1}},
- {2630, {wxAuiPaneInfo, maxSize_2, 2}},
- {2631, {wxAuiPaneInfo, maximizeButton, 1}},
- {2632, {wxAuiPaneInfo, minSize_1, 1}},
- {2633, {wxAuiPaneInfo, minSize_2, 2}},
- {2634, {wxAuiPaneInfo, minimizeButton, 1}},
- {2635, {wxAuiPaneInfo, movable, 1}},
- {2636, {wxAuiPaneInfo, name, 1}},
- {2637, {wxAuiPaneInfo, paneBorder, 1}},
- {2638, {wxAuiPaneInfo, pinButton, 1}},
- {2639, {wxAuiPaneInfo, position, 1}},
- {2640, {wxAuiPaneInfo, resizable, 1}},
- {2641, {wxAuiPaneInfo, right, 0}},
- {2642, {wxAuiPaneInfo, rightDockable, 1}},
- {2643, {wxAuiPaneInfo, row, 1}},
- {2644, {wxAuiPaneInfo, safeSet, 1}},
- {2645, {wxAuiPaneInfo, setFlag, 2}},
- {2646, {wxAuiPaneInfo, show, 1}},
- {2647, {wxAuiPaneInfo, toolbarPane, 0}},
- {2648, {wxAuiPaneInfo, top, 0}},
- {2649, {wxAuiPaneInfo, topDockable, 1}},
- {2650, {wxAuiPaneInfo, window, 1}},
- {2651, {wxAuiNotebook, new_0, 0}},
- {2652, {wxAuiNotebook, new_2, 2}},
- {2653, {wxAuiNotebook, addPage, 3}},
- {2654, {wxAuiNotebook, create, 2}},
- {2655, {wxAuiNotebook, deletePage, 1}},
- {2656, {wxAuiNotebook, getArtProvider, 0}},
- {2657, {wxAuiNotebook, getPage, 1}},
- {2658, {wxAuiNotebook, getPageBitmap, 1}},
- {2659, {wxAuiNotebook, getPageCount, 0}},
- {2660, {wxAuiNotebook, getPageIndex, 1}},
- {2661, {wxAuiNotebook, getPageText, 1}},
- {2662, {wxAuiNotebook, getSelection, 0}},
- {2663, {wxAuiNotebook, insertPage, 4}},
- {2664, {wxAuiNotebook, removePage, 1}},
- {2665, {wxAuiNotebook, setArtProvider, 1}},
- {2666, {wxAuiNotebook, setFont, 1}},
- {2667, {wxAuiNotebook, setPageBitmap, 2}},
- {2668, {wxAuiNotebook, setPageText, 2}},
- {2669, {wxAuiNotebook, setSelection, 1}},
- {2670, {wxAuiNotebook, setTabCtrlHeight, 1}},
- {2671, {wxAuiNotebook, setUniformBitmapSize, 1}},
- {2672, {wxAuiNotebook, 'Destroy', undefined}},
- {2673, {wxMDIParentFrame, new_0, 0}},
- {2674, {wxMDIParentFrame, new_4, 4}},
- {2675, {wxMDIParentFrame, destruct, 0}},
- {2676, {wxMDIParentFrame, activateNext, 0}},
- {2677, {wxMDIParentFrame, activatePrevious, 0}},
- {2678, {wxMDIParentFrame, arrangeIcons, 0}},
- {2679, {wxMDIParentFrame, cascade, 0}},
- {2680, {wxMDIParentFrame, create, 4}},
- {2681, {wxMDIParentFrame, getActiveChild, 0}},
- {2682, {wxMDIParentFrame, getClientWindow, 0}},
- {2683, {wxMDIParentFrame, tile, 1}},
- {2684, {wxMDIChildFrame, new_0, 0}},
- {2685, {wxMDIChildFrame, new_4, 4}},
- {2686, {wxMDIChildFrame, destruct, 0}},
- {2687, {wxMDIChildFrame, activate, 0}},
- {2688, {wxMDIChildFrame, create, 4}},
- {2689, {wxMDIChildFrame, maximize, 1}},
- {2690, {wxMDIChildFrame, restore, 0}},
- {2691, {wxMDIClientWindow, new_0, 0}},
- {2692, {wxMDIClientWindow, new_2, 2}},
- {2693, {wxMDIClientWindow, destruct, 0}},
- {2694, {wxMDIClientWindow, createClient, 2}},
- {2695, {wxLayoutAlgorithm, new, 0}},
- {2696, {wxLayoutAlgorithm, layoutFrame, 2}},
- {2697, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
- {2698, {wxLayoutAlgorithm, layoutWindow, 2}},
- {2699, {wxLayoutAlgorithm, 'Destroy', undefined}},
- {2700, {wxEvent, getId, 0}},
- {2701, {wxEvent, getSkipped, 0}},
- {2702, {wxEvent, getTimestamp, 0}},
- {2703, {wxEvent, isCommandEvent, 0}},
- {2704, {wxEvent, resumePropagation, 1}},
- {2705, {wxEvent, shouldPropagate, 0}},
- {2706, {wxEvent, skip, 1}},
- {2707, {wxEvent, stopPropagation, 0}},
- {2708, {wxCommandEvent, getClientData, 0}},
- {2709, {wxCommandEvent, getExtraLong, 0}},
- {2710, {wxCommandEvent, getInt, 0}},
- {2711, {wxCommandEvent, getSelection, 0}},
- {2712, {wxCommandEvent, getString, 0}},
- {2713, {wxCommandEvent, isChecked, 0}},
- {2714, {wxCommandEvent, isSelection, 0}},
- {2715, {wxCommandEvent, setInt, 1}},
- {2716, {wxCommandEvent, setString, 1}},
- {2717, {wxScrollEvent, getOrientation, 0}},
- {2718, {wxScrollEvent, getPosition, 0}},
- {2719, {wxScrollWinEvent, getOrientation, 0}},
- {2720, {wxScrollWinEvent, getPosition, 0}},
- {2721, {wxMouseEvent, altDown, 0}},
- {2722, {wxMouseEvent, button, 1}},
- {2723, {wxMouseEvent, buttonDClick, 1}},
- {2724, {wxMouseEvent, buttonDown, 1}},
- {2725, {wxMouseEvent, buttonUp, 1}},
- {2726, {wxMouseEvent, cmdDown, 0}},
- {2727, {wxMouseEvent, controlDown, 0}},
- {2728, {wxMouseEvent, dragging, 0}},
- {2729, {wxMouseEvent, entering, 0}},
- {2730, {wxMouseEvent, getButton, 0}},
- {2733, {wxMouseEvent, getPosition, 0}},
- {2734, {wxMouseEvent, getLogicalPosition, 1}},
- {2735, {wxMouseEvent, getLinesPerAction, 0}},
- {2736, {wxMouseEvent, getWheelRotation, 0}},
- {2737, {wxMouseEvent, getWheelDelta, 0}},
- {2738, {wxMouseEvent, getX, 0}},
- {2739, {wxMouseEvent, getY, 0}},
- {2740, {wxMouseEvent, isButton, 0}},
- {2741, {wxMouseEvent, isPageScroll, 0}},
- {2742, {wxMouseEvent, leaving, 0}},
- {2743, {wxMouseEvent, leftDClick, 0}},
- {2744, {wxMouseEvent, leftDown, 0}},
- {2745, {wxMouseEvent, leftIsDown, 0}},
- {2746, {wxMouseEvent, leftUp, 0}},
- {2747, {wxMouseEvent, metaDown, 0}},
- {2748, {wxMouseEvent, middleDClick, 0}},
- {2749, {wxMouseEvent, middleDown, 0}},
- {2750, {wxMouseEvent, middleIsDown, 0}},
- {2751, {wxMouseEvent, middleUp, 0}},
- {2752, {wxMouseEvent, moving, 0}},
- {2753, {wxMouseEvent, rightDClick, 0}},
- {2754, {wxMouseEvent, rightDown, 0}},
- {2755, {wxMouseEvent, rightIsDown, 0}},
- {2756, {wxMouseEvent, rightUp, 0}},
- {2757, {wxMouseEvent, shiftDown, 0}},
- {2758, {wxSetCursorEvent, getCursor, 0}},
- {2759, {wxSetCursorEvent, getX, 0}},
- {2760, {wxSetCursorEvent, getY, 0}},
- {2761, {wxSetCursorEvent, hasCursor, 0}},
- {2762, {wxSetCursorEvent, setCursor, 1}},
- {2763, {wxKeyEvent, altDown, 0}},
- {2764, {wxKeyEvent, cmdDown, 0}},
- {2765, {wxKeyEvent, controlDown, 0}},
- {2766, {wxKeyEvent, getKeyCode, 0}},
- {2767, {wxKeyEvent, getModifiers, 0}},
- {2770, {wxKeyEvent, getPosition, 0}},
- {2771, {wxKeyEvent, getRawKeyCode, 0}},
- {2772, {wxKeyEvent, getRawKeyFlags, 0}},
- {2773, {wxKeyEvent, getUnicodeKey, 0}},
- {2774, {wxKeyEvent, getX, 0}},
- {2775, {wxKeyEvent, getY, 0}},
- {2776, {wxKeyEvent, hasModifiers, 0}},
- {2777, {wxKeyEvent, metaDown, 0}},
- {2778, {wxKeyEvent, shiftDown, 0}},
- {2779, {wxSizeEvent, getSize, 0}},
- {2780, {wxMoveEvent, getPosition, 0}},
- {2781, {wxEraseEvent, getDC, 0}},
- {2782, {wxFocusEvent, getWindow, 0}},
- {2783, {wxChildFocusEvent, getWindow, 0}},
- {2784, {wxMenuEvent, getMenu, 0}},
- {2785, {wxMenuEvent, getMenuId, 0}},
- {2786, {wxMenuEvent, isPopup, 0}},
- {2787, {wxCloseEvent, canVeto, 0}},
- {2788, {wxCloseEvent, getLoggingOff, 0}},
- {2789, {wxCloseEvent, setCanVeto, 1}},
- {2790, {wxCloseEvent, setLoggingOff, 1}},
- {2791, {wxCloseEvent, veto, 1}},
- {2792, {wxShowEvent, setShow, 1}},
- {2793, {wxShowEvent, getShow, 0}},
- {2794, {wxIconizeEvent, iconized, 0}},
- {2795, {wxJoystickEvent, buttonDown, 1}},
- {2796, {wxJoystickEvent, buttonIsDown, 1}},
- {2797, {wxJoystickEvent, buttonUp, 1}},
- {2798, {wxJoystickEvent, getButtonChange, 0}},
- {2799, {wxJoystickEvent, getButtonState, 0}},
- {2800, {wxJoystickEvent, getJoystick, 0}},
- {2801, {wxJoystickEvent, getPosition, 0}},
- {2802, {wxJoystickEvent, getZPosition, 0}},
- {2803, {wxJoystickEvent, isButton, 0}},
- {2804, {wxJoystickEvent, isMove, 0}},
- {2805, {wxJoystickEvent, isZMove, 0}},
- {2806, {wxUpdateUIEvent, canUpdate, 1}},
- {2807, {wxUpdateUIEvent, check, 1}},
- {2808, {wxUpdateUIEvent, enable, 1}},
- {2809, {wxUpdateUIEvent, show, 1}},
- {2810, {wxUpdateUIEvent, getChecked, 0}},
- {2811, {wxUpdateUIEvent, getEnabled, 0}},
- {2812, {wxUpdateUIEvent, getShown, 0}},
- {2813, {wxUpdateUIEvent, getSetChecked, 0}},
- {2814, {wxUpdateUIEvent, getSetEnabled, 0}},
- {2815, {wxUpdateUIEvent, getSetShown, 0}},
- {2816, {wxUpdateUIEvent, getSetText, 0}},
- {2817, {wxUpdateUIEvent, getText, 0}},
- {2818, {wxUpdateUIEvent, getMode, 0}},
- {2819, {wxUpdateUIEvent, getUpdateInterval, 0}},
- {2820, {wxUpdateUIEvent, resetUpdateTime, 0}},
- {2821, {wxUpdateUIEvent, setMode, 1}},
- {2822, {wxUpdateUIEvent, setText, 1}},
- {2823, {wxUpdateUIEvent, setUpdateInterval, 1}},
- {2824, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
- {2825, {wxPaletteChangedEvent, setChangedWindow, 1}},
- {2826, {wxPaletteChangedEvent, getChangedWindow, 0}},
- {2827, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
- {2828, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
- {2829, {wxNavigationKeyEvent, getDirection, 0}},
- {2830, {wxNavigationKeyEvent, setDirection, 1}},
- {2831, {wxNavigationKeyEvent, isWindowChange, 0}},
- {2832, {wxNavigationKeyEvent, setWindowChange, 1}},
- {2833, {wxNavigationKeyEvent, isFromTab, 0}},
- {2834, {wxNavigationKeyEvent, setFromTab, 1}},
- {2835, {wxNavigationKeyEvent, getCurrentFocus, 0}},
- {2836, {wxNavigationKeyEvent, setCurrentFocus, 1}},
- {2837, {wxHelpEvent, getOrigin, 0}},
- {2838, {wxHelpEvent, getPosition, 0}},
- {2839, {wxHelpEvent, setOrigin, 1}},
- {2840, {wxHelpEvent, setPosition, 1}},
- {2841, {wxContextMenuEvent, getPosition, 0}},
- {2842, {wxContextMenuEvent, setPosition, 1}},
- {2843, {wxIdleEvent, canSend, 1}},
- {2844, {wxIdleEvent, getMode, 0}},
- {2845, {wxIdleEvent, requestMore, 1}},
- {2846, {wxIdleEvent, moreRequested, 0}},
- {2847, {wxIdleEvent, setMode, 1}},
- {2848, {wxGridEvent, altDown, 0}},
- {2849, {wxGridEvent, controlDown, 0}},
- {2850, {wxGridEvent, getCol, 0}},
- {2851, {wxGridEvent, getPosition, 0}},
- {2852, {wxGridEvent, getRow, 0}},
- {2853, {wxGridEvent, metaDown, 0}},
- {2854, {wxGridEvent, selecting, 0}},
- {2855, {wxGridEvent, shiftDown, 0}},
- {2856, {wxNotifyEvent, allow, 0}},
- {2857, {wxNotifyEvent, isAllowed, 0}},
- {2858, {wxNotifyEvent, veto, 0}},
- {2859, {wxSashEvent, getEdge, 0}},
- {2860, {wxSashEvent, getDragRect, 0}},
- {2861, {wxSashEvent, getDragStatus, 0}},
- {2862, {wxListEvent, getCacheFrom, 0}},
- {2863, {wxListEvent, getCacheTo, 0}},
- {2864, {wxListEvent, getKeyCode, 0}},
- {2865, {wxListEvent, getIndex, 0}},
- {2866, {wxListEvent, getColumn, 0}},
- {2867, {wxListEvent, getPoint, 0}},
- {2868, {wxListEvent, getLabel, 0}},
- {2869, {wxListEvent, getText, 0}},
- {2870, {wxListEvent, getImage, 0}},
- {2871, {wxListEvent, getData, 0}},
- {2872, {wxListEvent, getMask, 0}},
- {2873, {wxListEvent, getItem, 0}},
- {2874, {wxListEvent, isEditCancelled, 0}},
- {2875, {wxDateEvent, getDate, 0}},
- {2876, {wxCalendarEvent, getWeekDay, 0}},
- {2877, {wxFileDirPickerEvent, getPath, 0}},
- {2878, {wxColourPickerEvent, getColour, 0}},
- {2879, {wxFontPickerEvent, getFont, 0}},
- {2880, {wxStyledTextEvent, getPosition, 0}},
- {2881, {wxStyledTextEvent, getKey, 0}},
- {2882, {wxStyledTextEvent, getModifiers, 0}},
- {2883, {wxStyledTextEvent, getModificationType, 0}},
- {2884, {wxStyledTextEvent, getText, 0}},
- {2885, {wxStyledTextEvent, getLength, 0}},
- {2886, {wxStyledTextEvent, getLinesAdded, 0}},
- {2887, {wxStyledTextEvent, getLine, 0}},
- {2888, {wxStyledTextEvent, getFoldLevelNow, 0}},
- {2889, {wxStyledTextEvent, getFoldLevelPrev, 0}},
- {2890, {wxStyledTextEvent, getMargin, 0}},
- {2891, {wxStyledTextEvent, getMessage, 0}},
- {2892, {wxStyledTextEvent, getWParam, 0}},
- {2893, {wxStyledTextEvent, getLParam, 0}},
- {2894, {wxStyledTextEvent, getListType, 0}},
- {2895, {wxStyledTextEvent, getX, 0}},
- {2896, {wxStyledTextEvent, getY, 0}},
- {2897, {wxStyledTextEvent, getDragText, 0}},
- {2898, {wxStyledTextEvent, getDragAllowMove, 0}},
- {2899, {wxStyledTextEvent, getDragResult, 0}},
- {2900, {wxStyledTextEvent, getShift, 0}},
- {2901, {wxStyledTextEvent, getControl, 0}},
- {2902, {wxStyledTextEvent, getAlt, 0}},
- {2903, {utils, getKeyState, 1}},
- {2904, {utils, getMousePosition, 2}},
- {2905, {utils, getMouseState, 0}},
- {2906, {utils, setDetectableAutoRepeat, 1}},
- {2907, {utils, bell, 0}},
- {2908, {utils, findMenuItemId, 3}},
- {2909, {utils, genericFindWindowAtPoint, 1}},
- {2910, {utils, findWindowAtPoint, 1}},
- {2911, {utils, beginBusyCursor, 1}},
- {2912, {utils, endBusyCursor, 0}},
- {2913, {utils, isBusy, 0}},
- {2914, {utils, shutdown, 1}},
- {2915, {utils, shell, 1}},
- {2916, {utils, launchDefaultBrowser, 2}},
- {2917, {utils, getEmailAddress, 0}},
- {2918, {utils, getUserId, 0}},
- {2919, {utils, getHomeDir, 0}},
- {2920, {utils, newId, 0}},
- {2921, {utils, registerId, 1}},
- {2922, {utils, getCurrentId, 0}},
- {2923, {utils, getOsDescription, 0}},
- {2924, {utils, isPlatformLittleEndian, 0}},
- {2925, {utils, isPlatform64Bit, 0}},
- {2926, {wxPrintout, new, 1}},
- {2927, {wxPrintout, destruct, 0}},
- {2928, {wxPrintout, getDC, 0}},
- {2929, {wxPrintout, getPageSizeMM, 2}},
- {2930, {wxPrintout, getPageSizePixels, 2}},
- {2931, {wxPrintout, getPaperRectPixels, 0}},
- {2932, {wxPrintout, getPPIPrinter, 2}},
- {2933, {wxPrintout, getPPIScreen, 2}},
- {2934, {wxPrintout, getTitle, 0}},
- {2935, {wxPrintout, isPreview, 0}},
- {2936, {wxPrintout, fitThisSizeToPaper, 1}},
- {2937, {wxPrintout, fitThisSizeToPage, 1}},
- {2938, {wxPrintout, fitThisSizeToPageMargins, 2}},
- {2939, {wxPrintout, mapScreenSizeToPaper, 0}},
- {2940, {wxPrintout, mapScreenSizeToPage, 0}},
- {2941, {wxPrintout, mapScreenSizeToPageMargins, 1}},
- {2942, {wxPrintout, mapScreenSizeToDevice, 0}},
- {2943, {wxPrintout, getLogicalPaperRect, 0}},
- {2944, {wxPrintout, getLogicalPageRect, 0}},
- {2945, {wxPrintout, getLogicalPageMarginsRect, 1}},
- {2946, {wxPrintout, setLogicalOrigin, 2}},
- {2947, {wxPrintout, offsetLogicalOrigin, 2}},
- {2948, {wxStyledTextCtrl, new_2, 2}},
- {2949, {wxStyledTextCtrl, new_0, 0}},
- {2950, {wxStyledTextCtrl, destruct, 0}},
- {2951, {wxStyledTextCtrl, create, 2}},
- {2952, {wxStyledTextCtrl, addText, 1}},
- {2953, {wxStyledTextCtrl, addStyledText, 1}},
- {2954, {wxStyledTextCtrl, insertText, 2}},
- {2955, {wxStyledTextCtrl, clearAll, 0}},
- {2956, {wxStyledTextCtrl, clearDocumentStyle, 0}},
- {2957, {wxStyledTextCtrl, getLength, 0}},
- {2958, {wxStyledTextCtrl, getCharAt, 1}},
- {2959, {wxStyledTextCtrl, getCurrentPos, 0}},
- {2960, {wxStyledTextCtrl, getAnchor, 0}},
- {2961, {wxStyledTextCtrl, getStyleAt, 1}},
- {2962, {wxStyledTextCtrl, redo, 0}},
- {2963, {wxStyledTextCtrl, setUndoCollection, 1}},
- {2964, {wxStyledTextCtrl, selectAll, 0}},
- {2965, {wxStyledTextCtrl, setSavePoint, 0}},
- {2966, {wxStyledTextCtrl, getStyledText, 2}},
- {2967, {wxStyledTextCtrl, canRedo, 0}},
- {2968, {wxStyledTextCtrl, markerLineFromHandle, 1}},
- {2969, {wxStyledTextCtrl, markerDeleteHandle, 1}},
- {2970, {wxStyledTextCtrl, getUndoCollection, 0}},
- {2971, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
- {2972, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
- {2973, {wxStyledTextCtrl, positionFromPoint, 1}},
- {2974, {wxStyledTextCtrl, positionFromPointClose, 2}},
- {2975, {wxStyledTextCtrl, gotoLine, 1}},
- {2976, {wxStyledTextCtrl, gotoPos, 1}},
- {2977, {wxStyledTextCtrl, setAnchor, 1}},
- {2978, {wxStyledTextCtrl, getCurLine, 1}},
- {2979, {wxStyledTextCtrl, getEndStyled, 0}},
- {2980, {wxStyledTextCtrl, convertEOLs, 1}},
- {2981, {wxStyledTextCtrl, getEOLMode, 0}},
- {2982, {wxStyledTextCtrl, setEOLMode, 1}},
- {2983, {wxStyledTextCtrl, startStyling, 2}},
- {2984, {wxStyledTextCtrl, setStyling, 2}},
- {2985, {wxStyledTextCtrl, getBufferedDraw, 0}},
- {2986, {wxStyledTextCtrl, setBufferedDraw, 1}},
- {2987, {wxStyledTextCtrl, setTabWidth, 1}},
- {2988, {wxStyledTextCtrl, getTabWidth, 0}},
- {2989, {wxStyledTextCtrl, setCodePage, 1}},
- {2990, {wxStyledTextCtrl, markerDefine, 3}},
- {2991, {wxStyledTextCtrl, markerSetForeground, 2}},
- {2992, {wxStyledTextCtrl, markerSetBackground, 2}},
- {2993, {wxStyledTextCtrl, markerAdd, 2}},
- {2994, {wxStyledTextCtrl, markerDelete, 2}},
- {2995, {wxStyledTextCtrl, markerDeleteAll, 1}},
- {2996, {wxStyledTextCtrl, markerGet, 1}},
- {2997, {wxStyledTextCtrl, markerNext, 2}},
- {2998, {wxStyledTextCtrl, markerPrevious, 2}},
- {2999, {wxStyledTextCtrl, markerDefineBitmap, 2}},
- {3000, {wxStyledTextCtrl, markerAddSet, 2}},
- {3001, {wxStyledTextCtrl, markerSetAlpha, 2}},
- {3002, {wxStyledTextCtrl, setMarginType, 2}},
- {3003, {wxStyledTextCtrl, getMarginType, 1}},
- {3004, {wxStyledTextCtrl, setMarginWidth, 2}},
- {3005, {wxStyledTextCtrl, getMarginWidth, 1}},
- {3006, {wxStyledTextCtrl, setMarginMask, 2}},
- {3007, {wxStyledTextCtrl, getMarginMask, 1}},
- {3008, {wxStyledTextCtrl, setMarginSensitive, 2}},
- {3009, {wxStyledTextCtrl, getMarginSensitive, 1}},
- {3010, {wxStyledTextCtrl, styleClearAll, 0}},
- {3011, {wxStyledTextCtrl, styleSetForeground, 2}},
- {3012, {wxStyledTextCtrl, styleSetBackground, 2}},
- {3013, {wxStyledTextCtrl, styleSetBold, 2}},
- {3014, {wxStyledTextCtrl, styleSetItalic, 2}},
- {3015, {wxStyledTextCtrl, styleSetSize, 2}},
- {3016, {wxStyledTextCtrl, styleSetFaceName, 2}},
- {3017, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
- {3018, {wxStyledTextCtrl, styleResetDefault, 0}},
- {3019, {wxStyledTextCtrl, styleSetUnderline, 2}},
- {3020, {wxStyledTextCtrl, styleSetCase, 2}},
- {3021, {wxStyledTextCtrl, styleSetHotSpot, 2}},
- {3022, {wxStyledTextCtrl, setSelForeground, 2}},
- {3023, {wxStyledTextCtrl, setSelBackground, 2}},
- {3024, {wxStyledTextCtrl, getSelAlpha, 0}},
- {3025, {wxStyledTextCtrl, setSelAlpha, 1}},
- {3026, {wxStyledTextCtrl, setCaretForeground, 1}},
- {3027, {wxStyledTextCtrl, cmdKeyAssign, 3}},
- {3028, {wxStyledTextCtrl, cmdKeyClear, 2}},
- {3029, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
- {3030, {wxStyledTextCtrl, setStyleBytes, 2}},
- {3031, {wxStyledTextCtrl, styleSetVisible, 2}},
- {3032, {wxStyledTextCtrl, getCaretPeriod, 0}},
- {3033, {wxStyledTextCtrl, setCaretPeriod, 1}},
- {3034, {wxStyledTextCtrl, setWordChars, 1}},
- {3035, {wxStyledTextCtrl, beginUndoAction, 0}},
- {3036, {wxStyledTextCtrl, endUndoAction, 0}},
- {3037, {wxStyledTextCtrl, indicatorSetStyle, 2}},
- {3038, {wxStyledTextCtrl, indicatorGetStyle, 1}},
- {3039, {wxStyledTextCtrl, indicatorSetForeground, 2}},
- {3040, {wxStyledTextCtrl, indicatorGetForeground, 1}},
- {3041, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
- {3042, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
- {3043, {wxStyledTextCtrl, getStyleBits, 0}},
- {3044, {wxStyledTextCtrl, setLineState, 2}},
- {3045, {wxStyledTextCtrl, getLineState, 1}},
- {3046, {wxStyledTextCtrl, getMaxLineState, 0}},
- {3047, {wxStyledTextCtrl, getCaretLineVisible, 0}},
- {3048, {wxStyledTextCtrl, setCaretLineVisible, 1}},
- {3049, {wxStyledTextCtrl, getCaretLineBackground, 0}},
- {3050, {wxStyledTextCtrl, setCaretLineBackground, 1}},
- {3051, {wxStyledTextCtrl, autoCompShow, 2}},
- {3052, {wxStyledTextCtrl, autoCompCancel, 0}},
- {3053, {wxStyledTextCtrl, autoCompActive, 0}},
- {3054, {wxStyledTextCtrl, autoCompPosStart, 0}},
- {3055, {wxStyledTextCtrl, autoCompComplete, 0}},
- {3056, {wxStyledTextCtrl, autoCompStops, 1}},
- {3057, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
- {3058, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
- {3059, {wxStyledTextCtrl, autoCompSelect, 1}},
- {3060, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
- {3061, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
- {3062, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
- {3063, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
- {3064, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
- {3065, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
- {3066, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
- {3067, {wxStyledTextCtrl, userListShow, 2}},
- {3068, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
- {3069, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
- {3070, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
- {3071, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
- {3072, {wxStyledTextCtrl, registerImage, 2}},
- {3073, {wxStyledTextCtrl, clearRegisteredImages, 0}},
- {3074, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
- {3075, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
- {3076, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
- {3077, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
- {3078, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
- {3079, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
- {3080, {wxStyledTextCtrl, setIndent, 1}},
- {3081, {wxStyledTextCtrl, getIndent, 0}},
- {3082, {wxStyledTextCtrl, setUseTabs, 1}},
- {3083, {wxStyledTextCtrl, getUseTabs, 0}},
- {3084, {wxStyledTextCtrl, setLineIndentation, 2}},
- {3085, {wxStyledTextCtrl, getLineIndentation, 1}},
- {3086, {wxStyledTextCtrl, getLineIndentPosition, 1}},
- {3087, {wxStyledTextCtrl, getColumn, 1}},
- {3088, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
- {3089, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
- {3090, {wxStyledTextCtrl, setIndentationGuides, 1}},
- {3091, {wxStyledTextCtrl, getIndentationGuides, 0}},
- {3092, {wxStyledTextCtrl, setHighlightGuide, 1}},
- {3093, {wxStyledTextCtrl, getHighlightGuide, 0}},
- {3094, {wxStyledTextCtrl, getLineEndPosition, 1}},
- {3095, {wxStyledTextCtrl, getCodePage, 0}},
- {3096, {wxStyledTextCtrl, getCaretForeground, 0}},
- {3097, {wxStyledTextCtrl, getReadOnly, 0}},
- {3098, {wxStyledTextCtrl, setCurrentPos, 1}},
- {3099, {wxStyledTextCtrl, setSelectionStart, 1}},
- {3100, {wxStyledTextCtrl, getSelectionStart, 0}},
- {3101, {wxStyledTextCtrl, setSelectionEnd, 1}},
- {3102, {wxStyledTextCtrl, getSelectionEnd, 0}},
- {3103, {wxStyledTextCtrl, setPrintMagnification, 1}},
- {3104, {wxStyledTextCtrl, getPrintMagnification, 0}},
- {3105, {wxStyledTextCtrl, setPrintColourMode, 1}},
- {3106, {wxStyledTextCtrl, getPrintColourMode, 0}},
- {3107, {wxStyledTextCtrl, findText, 4}},
- {3108, {wxStyledTextCtrl, formatRange, 7}},
- {3109, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
- {3110, {wxStyledTextCtrl, getLine, 1}},
- {3111, {wxStyledTextCtrl, getLineCount, 0}},
- {3112, {wxStyledTextCtrl, setMarginLeft, 1}},
- {3113, {wxStyledTextCtrl, getMarginLeft, 0}},
- {3114, {wxStyledTextCtrl, setMarginRight, 1}},
- {3115, {wxStyledTextCtrl, getMarginRight, 0}},
- {3116, {wxStyledTextCtrl, getModify, 0}},
- {3117, {wxStyledTextCtrl, setSelection, 2}},
- {3118, {wxStyledTextCtrl, getSelectedText, 0}},
- {3119, {wxStyledTextCtrl, getTextRange, 2}},
- {3120, {wxStyledTextCtrl, hideSelection, 1}},
- {3121, {wxStyledTextCtrl, lineFromPosition, 1}},
- {3122, {wxStyledTextCtrl, positionFromLine, 1}},
- {3123, {wxStyledTextCtrl, lineScroll, 2}},
- {3124, {wxStyledTextCtrl, ensureCaretVisible, 0}},
- {3125, {wxStyledTextCtrl, replaceSelection, 1}},
- {3126, {wxStyledTextCtrl, setReadOnly, 1}},
- {3127, {wxStyledTextCtrl, canPaste, 0}},
- {3128, {wxStyledTextCtrl, canUndo, 0}},
- {3129, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
- {3130, {wxStyledTextCtrl, undo, 0}},
- {3131, {wxStyledTextCtrl, cut, 0}},
- {3132, {wxStyledTextCtrl, copy, 0}},
- {3133, {wxStyledTextCtrl, paste, 0}},
- {3134, {wxStyledTextCtrl, clear, 0}},
- {3135, {wxStyledTextCtrl, setText, 1}},
- {3136, {wxStyledTextCtrl, getText, 0}},
- {3137, {wxStyledTextCtrl, getTextLength, 0}},
- {3138, {wxStyledTextCtrl, getOvertype, 0}},
- {3139, {wxStyledTextCtrl, setCaretWidth, 1}},
- {3140, {wxStyledTextCtrl, getCaretWidth, 0}},
- {3141, {wxStyledTextCtrl, setTargetStart, 1}},
- {3142, {wxStyledTextCtrl, getTargetStart, 0}},
- {3143, {wxStyledTextCtrl, setTargetEnd, 1}},
- {3144, {wxStyledTextCtrl, getTargetEnd, 0}},
- {3145, {wxStyledTextCtrl, replaceTarget, 1}},
- {3146, {wxStyledTextCtrl, searchInTarget, 1}},
- {3147, {wxStyledTextCtrl, setSearchFlags, 1}},
- {3148, {wxStyledTextCtrl, getSearchFlags, 0}},
- {3149, {wxStyledTextCtrl, callTipShow, 2}},
- {3150, {wxStyledTextCtrl, callTipCancel, 0}},
- {3151, {wxStyledTextCtrl, callTipActive, 0}},
- {3152, {wxStyledTextCtrl, callTipPosAtStart, 0}},
- {3153, {wxStyledTextCtrl, callTipSetHighlight, 2}},
- {3154, {wxStyledTextCtrl, callTipSetBackground, 1}},
- {3155, {wxStyledTextCtrl, callTipSetForeground, 1}},
- {3156, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
- {3157, {wxStyledTextCtrl, callTipUseStyle, 1}},
- {3158, {wxStyledTextCtrl, visibleFromDocLine, 1}},
- {3159, {wxStyledTextCtrl, docLineFromVisible, 1}},
- {3160, {wxStyledTextCtrl, wrapCount, 1}},
- {3161, {wxStyledTextCtrl, setFoldLevel, 2}},
- {3162, {wxStyledTextCtrl, getFoldLevel, 1}},
- {3163, {wxStyledTextCtrl, getLastChild, 2}},
- {3164, {wxStyledTextCtrl, getFoldParent, 1}},
- {3165, {wxStyledTextCtrl, showLines, 2}},
- {3166, {wxStyledTextCtrl, hideLines, 2}},
- {3167, {wxStyledTextCtrl, getLineVisible, 1}},
- {3168, {wxStyledTextCtrl, setFoldExpanded, 2}},
- {3169, {wxStyledTextCtrl, getFoldExpanded, 1}},
- {3170, {wxStyledTextCtrl, toggleFold, 1}},
- {3171, {wxStyledTextCtrl, ensureVisible, 1}},
- {3172, {wxStyledTextCtrl, setFoldFlags, 1}},
- {3173, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
- {3174, {wxStyledTextCtrl, setTabIndents, 1}},
- {3175, {wxStyledTextCtrl, getTabIndents, 0}},
- {3176, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
- {3177, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
- {3178, {wxStyledTextCtrl, setMouseDwellTime, 1}},
- {3179, {wxStyledTextCtrl, getMouseDwellTime, 0}},
- {3180, {wxStyledTextCtrl, wordStartPosition, 2}},
- {3181, {wxStyledTextCtrl, wordEndPosition, 2}},
- {3182, {wxStyledTextCtrl, setWrapMode, 1}},
- {3183, {wxStyledTextCtrl, getWrapMode, 0}},
- {3184, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
- {3185, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
- {3186, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
- {3187, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
- {3188, {wxStyledTextCtrl, setWrapStartIndent, 1}},
- {3189, {wxStyledTextCtrl, getWrapStartIndent, 0}},
- {3190, {wxStyledTextCtrl, setLayoutCache, 1}},
- {3191, {wxStyledTextCtrl, getLayoutCache, 0}},
- {3192, {wxStyledTextCtrl, setScrollWidth, 1}},
- {3193, {wxStyledTextCtrl, getScrollWidth, 0}},
- {3194, {wxStyledTextCtrl, textWidth, 2}},
- {3195, {wxStyledTextCtrl, getEndAtLastLine, 0}},
- {3196, {wxStyledTextCtrl, textHeight, 1}},
- {3197, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
- {3198, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
- {3199, {wxStyledTextCtrl, appendText, 1}},
- {3200, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
- {3201, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
- {3202, {wxStyledTextCtrl, targetFromSelection, 0}},
- {3203, {wxStyledTextCtrl, linesJoin, 0}},
- {3204, {wxStyledTextCtrl, linesSplit, 1}},
- {3205, {wxStyledTextCtrl, setFoldMarginColour, 2}},
- {3206, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
- {3207, {wxStyledTextCtrl, lineDown, 0}},
- {3208, {wxStyledTextCtrl, lineDownExtend, 0}},
- {3209, {wxStyledTextCtrl, lineUp, 0}},
- {3210, {wxStyledTextCtrl, lineUpExtend, 0}},
- {3211, {wxStyledTextCtrl, charLeft, 0}},
- {3212, {wxStyledTextCtrl, charLeftExtend, 0}},
- {3213, {wxStyledTextCtrl, charRight, 0}},
- {3214, {wxStyledTextCtrl, charRightExtend, 0}},
- {3215, {wxStyledTextCtrl, wordLeft, 0}},
- {3216, {wxStyledTextCtrl, wordLeftExtend, 0}},
- {3217, {wxStyledTextCtrl, wordRight, 0}},
- {3218, {wxStyledTextCtrl, wordRightExtend, 0}},
- {3219, {wxStyledTextCtrl, home, 0}},
- {3220, {wxStyledTextCtrl, homeExtend, 0}},
- {3221, {wxStyledTextCtrl, lineEnd, 0}},
- {3222, {wxStyledTextCtrl, lineEndExtend, 0}},
- {3223, {wxStyledTextCtrl, documentStart, 0}},
- {3224, {wxStyledTextCtrl, documentStartExtend, 0}},
- {3225, {wxStyledTextCtrl, documentEnd, 0}},
- {3226, {wxStyledTextCtrl, documentEndExtend, 0}},
- {3227, {wxStyledTextCtrl, pageUp, 0}},
- {3228, {wxStyledTextCtrl, pageUpExtend, 0}},
- {3229, {wxStyledTextCtrl, pageDown, 0}},
- {3230, {wxStyledTextCtrl, pageDownExtend, 0}},
- {3231, {wxStyledTextCtrl, editToggleOvertype, 0}},
- {3232, {wxStyledTextCtrl, cancel, 0}},
- {3233, {wxStyledTextCtrl, deleteBack, 0}},
- {3234, {wxStyledTextCtrl, tab, 0}},
- {3235, {wxStyledTextCtrl, backTab, 0}},
- {3236, {wxStyledTextCtrl, newLine, 0}},
- {3237, {wxStyledTextCtrl, formFeed, 0}},
- {3238, {wxStyledTextCtrl, vCHome, 0}},
- {3239, {wxStyledTextCtrl, vCHomeExtend, 0}},
- {3240, {wxStyledTextCtrl, zoomIn, 0}},
- {3241, {wxStyledTextCtrl, zoomOut, 0}},
- {3242, {wxStyledTextCtrl, delWordLeft, 0}},
- {3243, {wxStyledTextCtrl, delWordRight, 0}},
- {3244, {wxStyledTextCtrl, lineCut, 0}},
- {3245, {wxStyledTextCtrl, lineDelete, 0}},
- {3246, {wxStyledTextCtrl, lineTranspose, 0}},
- {3247, {wxStyledTextCtrl, lineDuplicate, 0}},
- {3248, {wxStyledTextCtrl, lowerCase, 0}},
- {3249, {wxStyledTextCtrl, upperCase, 0}},
- {3250, {wxStyledTextCtrl, lineScrollDown, 0}},
- {3251, {wxStyledTextCtrl, lineScrollUp, 0}},
- {3252, {wxStyledTextCtrl, deleteBackNotLine, 0}},
- {3253, {wxStyledTextCtrl, homeDisplay, 0}},
- {3254, {wxStyledTextCtrl, homeDisplayExtend, 0}},
- {3255, {wxStyledTextCtrl, lineEndDisplay, 0}},
- {3256, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
- {3257, {wxStyledTextCtrl, homeWrapExtend, 0}},
- {3258, {wxStyledTextCtrl, lineEndWrap, 0}},
- {3259, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
- {3260, {wxStyledTextCtrl, vCHomeWrap, 0}},
- {3261, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
- {3262, {wxStyledTextCtrl, lineCopy, 0}},
- {3263, {wxStyledTextCtrl, moveCaretInsideView, 0}},
- {3264, {wxStyledTextCtrl, lineLength, 1}},
- {3265, {wxStyledTextCtrl, braceHighlight, 2}},
- {3266, {wxStyledTextCtrl, braceBadLight, 1}},
- {3267, {wxStyledTextCtrl, braceMatch, 1}},
- {3268, {wxStyledTextCtrl, getViewEOL, 0}},
- {3269, {wxStyledTextCtrl, setViewEOL, 1}},
- {3270, {wxStyledTextCtrl, setModEventMask, 1}},
- {3271, {wxStyledTextCtrl, getEdgeColumn, 0}},
- {3272, {wxStyledTextCtrl, setEdgeColumn, 1}},
- {3273, {wxStyledTextCtrl, setEdgeMode, 1}},
- {3274, {wxStyledTextCtrl, getEdgeMode, 0}},
- {3275, {wxStyledTextCtrl, getEdgeColour, 0}},
- {3276, {wxStyledTextCtrl, setEdgeColour, 1}},
- {3277, {wxStyledTextCtrl, searchAnchor, 0}},
- {3278, {wxStyledTextCtrl, searchNext, 2}},
- {3279, {wxStyledTextCtrl, searchPrev, 2}},
- {3280, {wxStyledTextCtrl, linesOnScreen, 0}},
- {3281, {wxStyledTextCtrl, usePopUp, 1}},
- {3282, {wxStyledTextCtrl, selectionIsRectangle, 0}},
- {3283, {wxStyledTextCtrl, setZoom, 1}},
- {3284, {wxStyledTextCtrl, getZoom, 0}},
- {3285, {wxStyledTextCtrl, getModEventMask, 0}},
- {3286, {wxStyledTextCtrl, setSTCFocus, 1}},
- {3287, {wxStyledTextCtrl, getSTCFocus, 0}},
- {3288, {wxStyledTextCtrl, setStatus, 1}},
- {3289, {wxStyledTextCtrl, getStatus, 0}},
- {3290, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
- {3291, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
- {3292, {wxStyledTextCtrl, setSTCCursor, 1}},
- {3293, {wxStyledTextCtrl, getSTCCursor, 0}},
- {3294, {wxStyledTextCtrl, setControlCharSymbol, 1}},
- {3295, {wxStyledTextCtrl, getControlCharSymbol, 0}},
- {3296, {wxStyledTextCtrl, wordPartLeft, 0}},
- {3297, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
- {3298, {wxStyledTextCtrl, wordPartRight, 0}},
- {3299, {wxStyledTextCtrl, wordPartRightExtend, 0}},
- {3300, {wxStyledTextCtrl, setVisiblePolicy, 2}},
- {3301, {wxStyledTextCtrl, delLineLeft, 0}},
- {3302, {wxStyledTextCtrl, delLineRight, 0}},
- {3303, {wxStyledTextCtrl, getXOffset, 0}},
- {3304, {wxStyledTextCtrl, chooseCaretX, 0}},
- {3305, {wxStyledTextCtrl, setXCaretPolicy, 2}},
- {3306, {wxStyledTextCtrl, setYCaretPolicy, 2}},
- {3307, {wxStyledTextCtrl, getPrintWrapMode, 0}},
- {3308, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
- {3309, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
- {3310, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
- {3311, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
- {3312, {wxStyledTextCtrl, paraDownExtend, 0}},
- {3313, {wxStyledTextCtrl, paraUp, 0}},
- {3314, {wxStyledTextCtrl, paraUpExtend, 0}},
- {3315, {wxStyledTextCtrl, positionBefore, 1}},
- {3316, {wxStyledTextCtrl, positionAfter, 1}},
- {3317, {wxStyledTextCtrl, copyRange, 2}},
- {3318, {wxStyledTextCtrl, copyText, 2}},
- {3319, {wxStyledTextCtrl, setSelectionMode, 1}},
- {3320, {wxStyledTextCtrl, getSelectionMode, 0}},
- {3321, {wxStyledTextCtrl, lineDownRectExtend, 0}},
- {3322, {wxStyledTextCtrl, lineUpRectExtend, 0}},
- {3323, {wxStyledTextCtrl, charLeftRectExtend, 0}},
- {3324, {wxStyledTextCtrl, charRightRectExtend, 0}},
- {3325, {wxStyledTextCtrl, homeRectExtend, 0}},
- {3326, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
- {3327, {wxStyledTextCtrl, lineEndRectExtend, 0}},
- {3328, {wxStyledTextCtrl, pageUpRectExtend, 0}},
- {3329, {wxStyledTextCtrl, pageDownRectExtend, 0}},
- {3330, {wxStyledTextCtrl, stutteredPageUp, 0}},
- {3331, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
- {3332, {wxStyledTextCtrl, stutteredPageDown, 0}},
- {3333, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
- {3334, {wxStyledTextCtrl, wordLeftEnd, 0}},
- {3335, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
- {3336, {wxStyledTextCtrl, wordRightEnd, 0}},
- {3337, {wxStyledTextCtrl, wordRightEndExtend, 0}},
- {3338, {wxStyledTextCtrl, setWhitespaceChars, 1}},
- {3339, {wxStyledTextCtrl, setCharsDefault, 0}},
- {3340, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
- {3341, {wxStyledTextCtrl, allocate, 1}},
- {3342, {wxStyledTextCtrl, findColumn, 2}},
- {3343, {wxStyledTextCtrl, getCaretSticky, 0}},
- {3344, {wxStyledTextCtrl, setCaretSticky, 1}},
- {3345, {wxStyledTextCtrl, toggleCaretSticky, 0}},
- {3346, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
- {3347, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
- {3348, {wxStyledTextCtrl, selectionDuplicate, 0}},
- {3349, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
- {3350, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
- {3351, {wxStyledTextCtrl, startRecord, 0}},
- {3352, {wxStyledTextCtrl, stopRecord, 0}},
- {3353, {wxStyledTextCtrl, setLexer, 1}},
- {3354, {wxStyledTextCtrl, getLexer, 0}},
- {3355, {wxStyledTextCtrl, colourise, 2}},
- {3356, {wxStyledTextCtrl, setProperty, 2}},
- {3357, {wxStyledTextCtrl, setKeyWords, 2}},
- {3358, {wxStyledTextCtrl, setLexerLanguage, 1}},
- {3359, {wxStyledTextCtrl, getProperty, 1}},
- {3360, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
- {3361, {wxStyledTextCtrl, getCurrentLine, 0}},
- {3362, {wxStyledTextCtrl, styleSetSpec, 2}},
- {3363, {wxStyledTextCtrl, styleSetFont, 2}},
- {3364, {wxStyledTextCtrl, styleSetFontAttr, 7}},
- {3365, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
- {3366, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
- {3367, {wxStyledTextCtrl, cmdKeyExecute, 1}},
- {3368, {wxStyledTextCtrl, setMargins, 2}},
- {3369, {wxStyledTextCtrl, getSelection, 2}},
- {3370, {wxStyledTextCtrl, pointFromPosition, 1}},
- {3371, {wxStyledTextCtrl, scrollToLine, 1}},
- {3372, {wxStyledTextCtrl, scrollToColumn, 1}},
- {3373, {wxStyledTextCtrl, sendMsg, 2}},
+ {2061, {wxTreeCtrl, isTreeItemIdOk, 1}},
+ {2062, {wxTreeCtrl, prependItem, 3}},
+ {2063, {wxTreeCtrl, scrollTo, 1}},
+ {2064, {wxTreeCtrl, selectItem_1, 1}},
+ {2065, {wxTreeCtrl, selectItem_2, 2}},
+ {2066, {wxTreeCtrl, setIndent, 1}},
+ {2067, {wxTreeCtrl, setImageList, 1}},
+ {2068, {wxTreeCtrl, setItemBackgroundColour, 2}},
+ {2069, {wxTreeCtrl, setItemBold, 2}},
+ {2070, {wxTreeCtrl, setItemData, 2}},
+ {2071, {wxTreeCtrl, setItemDropHighlight, 2}},
+ {2072, {wxTreeCtrl, setItemFont, 2}},
+ {2073, {wxTreeCtrl, setItemHasChildren, 2}},
+ {2074, {wxTreeCtrl, setItemImage_2, 2}},
+ {2075, {wxTreeCtrl, setItemImage_3, 3}},
+ {2076, {wxTreeCtrl, setItemText, 2}},
+ {2077, {wxTreeCtrl, setItemTextColour, 2}},
+ {2078, {wxTreeCtrl, setStateImageList, 1}},
+ {2079, {wxTreeCtrl, setWindowStyle, 1}},
+ {2080, {wxTreeCtrl, sortChildren, 1}},
+ {2081, {wxTreeCtrl, toggle, 1}},
+ {2082, {wxTreeCtrl, toggleItemSelection, 1}},
+ {2083, {wxTreeCtrl, unselect, 0}},
+ {2084, {wxTreeCtrl, unselectAll, 0}},
+ {2085, {wxTreeCtrl, unselectItem, 1}},
+ {2086, {wxScrollBar, new_0, 0}},
+ {2087, {wxScrollBar, new_3, 3}},
+ {2088, {wxScrollBar, destruct, 0}},
+ {2089, {wxScrollBar, create, 3}},
+ {2090, {wxScrollBar, getRange, 0}},
+ {2091, {wxScrollBar, getPageSize, 0}},
+ {2092, {wxScrollBar, getThumbPosition, 0}},
+ {2093, {wxScrollBar, getThumbSize, 0}},
+ {2094, {wxScrollBar, setThumbPosition, 1}},
+ {2095, {wxScrollBar, setScrollbar, 5}},
+ {2097, {wxSpinButton, new_2, 2}},
+ {2098, {wxSpinButton, new_0, 0}},
+ {2099, {wxSpinButton, create, 2}},
+ {2100, {wxSpinButton, getMax, 0}},
+ {2101, {wxSpinButton, getMin, 0}},
+ {2102, {wxSpinButton, getValue, 0}},
+ {2103, {wxSpinButton, setRange, 2}},
+ {2104, {wxSpinButton, setValue, 1}},
+ {2105, {wxSpinButton, 'Destroy', undefined}},
+ {2106, {wxSpinCtrl, new_0, 0}},
+ {2107, {wxSpinCtrl, new_2, 2}},
+ {2109, {wxSpinCtrl, create, 2}},
+ {2112, {wxSpinCtrl, setValue_1_1, 1}},
+ {2113, {wxSpinCtrl, setValue_1_0, 1}},
+ {2115, {wxSpinCtrl, getValue, 0}},
+ {2117, {wxSpinCtrl, setRange, 2}},
+ {2118, {wxSpinCtrl, setSelection, 2}},
+ {2120, {wxSpinCtrl, getMin, 0}},
+ {2122, {wxSpinCtrl, getMax, 0}},
+ {2123, {wxSpinCtrl, 'Destroy', undefined}},
+ {2124, {wxStaticText, new_0, 0}},
+ {2125, {wxStaticText, new_4, 4}},
+ {2126, {wxStaticText, create, 4}},
+ {2127, {wxStaticText, getLabel, 0}},
+ {2128, {wxStaticText, setLabel, 1}},
+ {2129, {wxStaticText, wrap, 1}},
+ {2130, {wxStaticText, 'Destroy', undefined}},
+ {2131, {wxStaticBitmap, new_0, 0}},
+ {2132, {wxStaticBitmap, new_4, 4}},
+ {2133, {wxStaticBitmap, create, 4}},
+ {2134, {wxStaticBitmap, getBitmap, 0}},
+ {2135, {wxStaticBitmap, setBitmap, 1}},
+ {2136, {wxStaticBitmap, 'Destroy', undefined}},
+ {2137, {wxRadioBox, new, 7}},
+ {2139, {wxRadioBox, destruct, 0}},
+ {2140, {wxRadioBox, create, 7}},
+ {2141, {wxRadioBox, enable_2, 2}},
+ {2142, {wxRadioBox, enable_1, 1}},
+ {2143, {wxRadioBox, getSelection, 0}},
+ {2144, {wxRadioBox, getString, 1}},
+ {2145, {wxRadioBox, setSelection, 1}},
+ {2146, {wxRadioBox, show_2, 2}},
+ {2147, {wxRadioBox, show_1, 1}},
+ {2148, {wxRadioBox, getColumnCount, 0}},
+ {2149, {wxRadioBox, getItemHelpText, 1}},
+ {2150, {wxRadioBox, getItemToolTip, 1}},
+ {2152, {wxRadioBox, getItemFromPoint, 1}},
+ {2153, {wxRadioBox, getRowCount, 0}},
+ {2154, {wxRadioBox, isItemEnabled, 1}},
+ {2155, {wxRadioBox, isItemShown, 1}},
+ {2156, {wxRadioBox, setItemHelpText, 2}},
+ {2157, {wxRadioBox, setItemToolTip, 2}},
+ {2158, {wxRadioButton, new_0, 0}},
+ {2159, {wxRadioButton, new_4, 4}},
+ {2160, {wxRadioButton, create, 4}},
+ {2161, {wxRadioButton, getValue, 0}},
+ {2162, {wxRadioButton, setValue, 1}},
+ {2163, {wxRadioButton, 'Destroy', undefined}},
+ {2165, {wxSlider, new_6, 6}},
+ {2166, {wxSlider, new_0, 0}},
+ {2167, {wxSlider, create, 6}},
+ {2168, {wxSlider, getLineSize, 0}},
+ {2169, {wxSlider, getMax, 0}},
+ {2170, {wxSlider, getMin, 0}},
+ {2171, {wxSlider, getPageSize, 0}},
+ {2172, {wxSlider, getThumbLength, 0}},
+ {2173, {wxSlider, getValue, 0}},
+ {2174, {wxSlider, setLineSize, 1}},
+ {2175, {wxSlider, setPageSize, 1}},
+ {2176, {wxSlider, setRange, 2}},
+ {2177, {wxSlider, setThumbLength, 1}},
+ {2178, {wxSlider, setValue, 1}},
+ {2179, {wxSlider, 'Destroy', undefined}},
+ {2181, {wxDialog, new_4, 4}},
+ {2182, {wxDialog, new_0, 0}},
+ {2184, {wxDialog, destruct, 0}},
+ {2185, {wxDialog, create, 4}},
+ {2186, {wxDialog, createButtonSizer, 1}},
+ {2187, {wxDialog, createStdDialogButtonSizer, 1}},
+ {2188, {wxDialog, endModal, 1}},
+ {2189, {wxDialog, getAffirmativeId, 0}},
+ {2190, {wxDialog, getReturnCode, 0}},
+ {2191, {wxDialog, isModal, 0}},
+ {2192, {wxDialog, setAffirmativeId, 1}},
+ {2193, {wxDialog, setReturnCode, 1}},
+ {2194, {wxDialog, show, 1}},
+ {2195, {wxDialog, showModal, 0}},
+ {2196, {wxColourDialog, new_0, 0}},
+ {2197, {wxColourDialog, new_2, 2}},
+ {2198, {wxColourDialog, destruct, 0}},
+ {2199, {wxColourDialog, create, 2}},
+ {2200, {wxColourDialog, getColourData, 0}},
+ {2201, {wxColourData, new_0, 0}},
+ {2202, {wxColourData, new_1, 1}},
+ {2203, {wxColourData, destruct, 0}},
+ {2204, {wxColourData, getChooseFull, 0}},
+ {2205, {wxColourData, getColour, 0}},
+ {2207, {wxColourData, getCustomColour, 1}},
+ {2208, {wxColourData, setChooseFull, 1}},
+ {2209, {wxColourData, setColour, 1}},
+ {2210, {wxColourData, setCustomColour, 2}},
+ {2211, {wxPalette, new_0, 0}},
+ {2212, {wxPalette, new_4, 4}},
+ {2214, {wxPalette, destruct, 0}},
+ {2215, {wxPalette, create, 4}},
+ {2216, {wxPalette, getColoursCount, 0}},
+ {2217, {wxPalette, getPixel, 3}},
+ {2218, {wxPalette, getRGB, 4}},
+ {2219, {wxPalette, isOk, 0}},
+ {2223, {wxDirDialog, new, 2}},
+ {2224, {wxDirDialog, destruct, 0}},
+ {2225, {wxDirDialog, getPath, 0}},
+ {2226, {wxDirDialog, getMessage, 0}},
+ {2227, {wxDirDialog, setMessage, 1}},
+ {2228, {wxDirDialog, setPath, 1}},
+ {2232, {wxFileDialog, new, 2}},
+ {2233, {wxFileDialog, destruct, 0}},
+ {2234, {wxFileDialog, getDirectory, 0}},
+ {2235, {wxFileDialog, getFilename, 0}},
+ {2236, {wxFileDialog, getFilenames, 1}},
+ {2237, {wxFileDialog, getFilterIndex, 0}},
+ {2238, {wxFileDialog, getMessage, 0}},
+ {2239, {wxFileDialog, getPath, 0}},
+ {2240, {wxFileDialog, getPaths, 1}},
+ {2241, {wxFileDialog, getWildcard, 0}},
+ {2242, {wxFileDialog, setDirectory, 1}},
+ {2243, {wxFileDialog, setFilename, 1}},
+ {2244, {wxFileDialog, setFilterIndex, 1}},
+ {2245, {wxFileDialog, setMessage, 1}},
+ {2246, {wxFileDialog, setPath, 1}},
+ {2247, {wxFileDialog, setWildcard, 1}},
+ {2248, {wxPickerBase, setInternalMargin, 1}},
+ {2249, {wxPickerBase, getInternalMargin, 0}},
+ {2250, {wxPickerBase, setTextCtrlProportion, 1}},
+ {2251, {wxPickerBase, setPickerCtrlProportion, 1}},
+ {2252, {wxPickerBase, getTextCtrlProportion, 0}},
+ {2253, {wxPickerBase, getPickerCtrlProportion, 0}},
+ {2254, {wxPickerBase, hasTextCtrl, 0}},
+ {2255, {wxPickerBase, getTextCtrl, 0}},
+ {2256, {wxPickerBase, isTextCtrlGrowable, 0}},
+ {2257, {wxPickerBase, setPickerCtrlGrowable, 1}},
+ {2258, {wxPickerBase, setTextCtrlGrowable, 1}},
+ {2259, {wxPickerBase, isPickerCtrlGrowable, 0}},
+ {2260, {wxFilePickerCtrl, new_0, 0}},
+ {2261, {wxFilePickerCtrl, new_3, 3}},
+ {2262, {wxFilePickerCtrl, create, 3}},
+ {2263, {wxFilePickerCtrl, getPath, 0}},
+ {2264, {wxFilePickerCtrl, setPath, 1}},
+ {2265, {wxFilePickerCtrl, 'Destroy', undefined}},
+ {2266, {wxDirPickerCtrl, new_0, 0}},
+ {2267, {wxDirPickerCtrl, new_3, 3}},
+ {2268, {wxDirPickerCtrl, create, 3}},
+ {2269, {wxDirPickerCtrl, getPath, 0}},
+ {2270, {wxDirPickerCtrl, setPath, 1}},
+ {2271, {wxDirPickerCtrl, 'Destroy', undefined}},
+ {2272, {wxColourPickerCtrl, new_0, 0}},
+ {2273, {wxColourPickerCtrl, new_3, 3}},
+ {2274, {wxColourPickerCtrl, create, 3}},
+ {2275, {wxColourPickerCtrl, getColour, 0}},
+ {2276, {wxColourPickerCtrl, setColour_1_1, 1}},
+ {2277, {wxColourPickerCtrl, setColour_1_0, 1}},
+ {2278, {wxColourPickerCtrl, 'Destroy', undefined}},
+ {2279, {wxDatePickerCtrl, new_0, 0}},
+ {2280, {wxDatePickerCtrl, new_3, 3}},
+ {2281, {wxDatePickerCtrl, getRange, 2}},
+ {2282, {wxDatePickerCtrl, getValue, 0}},
+ {2283, {wxDatePickerCtrl, setRange, 2}},
+ {2284, {wxDatePickerCtrl, setValue, 1}},
+ {2285, {wxDatePickerCtrl, 'Destroy', undefined}},
+ {2286, {wxFontPickerCtrl, new_0, 0}},
+ {2287, {wxFontPickerCtrl, new_3, 3}},
+ {2288, {wxFontPickerCtrl, create, 3}},
+ {2289, {wxFontPickerCtrl, getSelectedFont, 0}},
+ {2290, {wxFontPickerCtrl, setSelectedFont, 1}},
+ {2291, {wxFontPickerCtrl, getMaxPointSize, 0}},
+ {2292, {wxFontPickerCtrl, setMaxPointSize, 1}},
+ {2293, {wxFontPickerCtrl, 'Destroy', undefined}},
+ {2296, {wxFindReplaceDialog, new_0, 0}},
+ {2297, {wxFindReplaceDialog, new_4, 4}},
+ {2298, {wxFindReplaceDialog, destruct, 0}},
+ {2299, {wxFindReplaceDialog, create, 4}},
+ {2300, {wxFindReplaceDialog, getData, 0}},
+ {2301, {wxFindReplaceData, new_0, 0}},
+ {2302, {wxFindReplaceData, new_1, 1}},
+ {2303, {wxFindReplaceData, getFindString, 0}},
+ {2304, {wxFindReplaceData, getReplaceString, 0}},
+ {2305, {wxFindReplaceData, getFlags, 0}},
+ {2306, {wxFindReplaceData, setFlags, 1}},
+ {2307, {wxFindReplaceData, setFindString, 1}},
+ {2308, {wxFindReplaceData, setReplaceString, 1}},
+ {2309, {wxFindReplaceData, 'Destroy', undefined}},
+ {2310, {wxMultiChoiceDialog, new_0, 0}},
+ {2312, {wxMultiChoiceDialog, new_5, 5}},
+ {2313, {wxMultiChoiceDialog, getSelections, 0}},
+ {2314, {wxMultiChoiceDialog, setSelections, 1}},
+ {2315, {wxMultiChoiceDialog, 'Destroy', undefined}},
+ {2316, {wxSingleChoiceDialog, new_0, 0}},
+ {2318, {wxSingleChoiceDialog, new_5, 5}},
+ {2319, {wxSingleChoiceDialog, getSelection, 0}},
+ {2320, {wxSingleChoiceDialog, getStringSelection, 0}},
+ {2321, {wxSingleChoiceDialog, setSelection, 1}},
+ {2322, {wxSingleChoiceDialog, 'Destroy', undefined}},
+ {2323, {wxTextEntryDialog, new, 3}},
+ {2324, {wxTextEntryDialog, getValue, 0}},
+ {2325, {wxTextEntryDialog, setValue, 1}},
+ {2326, {wxTextEntryDialog, 'Destroy', undefined}},
+ {2327, {wxPasswordEntryDialog, new, 3}},
+ {2328, {wxPasswordEntryDialog, 'Destroy', undefined}},
+ {2329, {wxFontData, new_0, 0}},
+ {2330, {wxFontData, new_1, 1}},
+ {2331, {wxFontData, destruct, 0}},
+ {2332, {wxFontData, enableEffects, 1}},
+ {2333, {wxFontData, getAllowSymbols, 0}},
+ {2334, {wxFontData, getColour, 0}},
+ {2335, {wxFontData, getChosenFont, 0}},
+ {2336, {wxFontData, getEnableEffects, 0}},
+ {2337, {wxFontData, getInitialFont, 0}},
+ {2338, {wxFontData, getShowHelp, 0}},
+ {2339, {wxFontData, setAllowSymbols, 1}},
+ {2340, {wxFontData, setChosenFont, 1}},
+ {2341, {wxFontData, setColour, 1}},
+ {2342, {wxFontData, setInitialFont, 1}},
+ {2343, {wxFontData, setRange, 2}},
+ {2344, {wxFontData, setShowHelp, 1}},
+ {2348, {wxFontDialog, new_0, 0}},
+ {2350, {wxFontDialog, new_2, 2}},
+ {2352, {wxFontDialog, create, 2}},
+ {2353, {wxFontDialog, getFontData, 0}},
+ {2355, {wxFontDialog, 'Destroy', undefined}},
+ {2356, {wxProgressDialog, new, 3}},
+ {2357, {wxProgressDialog, destruct, 0}},
+ {2358, {wxProgressDialog, resume, 0}},
+ {2359, {wxProgressDialog, update_2, 2}},
+ {2360, {wxProgressDialog, update_0, 0}},
+ {2361, {wxMessageDialog, new, 3}},
+ {2362, {wxMessageDialog, destruct, 0}},
+ {2363, {wxPageSetupDialog, new, 2}},
+ {2364, {wxPageSetupDialog, destruct, 0}},
+ {2365, {wxPageSetupDialog, getPageSetupData, 0}},
+ {2366, {wxPageSetupDialog, showModal, 0}},
+ {2367, {wxPageSetupDialogData, new_0, 0}},
+ {2368, {wxPageSetupDialogData, new_1_0, 1}},
+ {2369, {wxPageSetupDialogData, new_1_1, 1}},
+ {2370, {wxPageSetupDialogData, destruct, 0}},
+ {2371, {wxPageSetupDialogData, enableHelp, 1}},
+ {2372, {wxPageSetupDialogData, enableMargins, 1}},
+ {2373, {wxPageSetupDialogData, enableOrientation, 1}},
+ {2374, {wxPageSetupDialogData, enablePaper, 1}},
+ {2375, {wxPageSetupDialogData, enablePrinter, 1}},
+ {2376, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
+ {2377, {wxPageSetupDialogData, getEnableMargins, 0}},
+ {2378, {wxPageSetupDialogData, getEnableOrientation, 0}},
+ {2379, {wxPageSetupDialogData, getEnablePaper, 0}},
+ {2380, {wxPageSetupDialogData, getEnablePrinter, 0}},
+ {2381, {wxPageSetupDialogData, getEnableHelp, 0}},
+ {2382, {wxPageSetupDialogData, getDefaultInfo, 0}},
+ {2383, {wxPageSetupDialogData, getMarginTopLeft, 0}},
+ {2384, {wxPageSetupDialogData, getMarginBottomRight, 0}},
+ {2385, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
+ {2386, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
+ {2387, {wxPageSetupDialogData, getPaperId, 0}},
+ {2388, {wxPageSetupDialogData, getPaperSize, 0}},
+ {2390, {wxPageSetupDialogData, getPrintData, 0}},
+ {2391, {wxPageSetupDialogData, isOk, 0}},
+ {2392, {wxPageSetupDialogData, setDefaultInfo, 1}},
+ {2393, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
+ {2394, {wxPageSetupDialogData, setMarginTopLeft, 1}},
+ {2395, {wxPageSetupDialogData, setMarginBottomRight, 1}},
+ {2396, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
+ {2397, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
+ {2398, {wxPageSetupDialogData, setPaperId, 1}},
+ {2399, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
+ {2400, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
+ {2401, {wxPageSetupDialogData, setPrintData, 1}},
+ {2402, {wxPrintDialog, new_2_0, 2}},
+ {2403, {wxPrintDialog, new_2_1, 2}},
+ {2404, {wxPrintDialog, destruct, 0}},
+ {2405, {wxPrintDialog, getPrintDialogData, 0}},
+ {2406, {wxPrintDialog, getPrintDC, 0}},
+ {2407, {wxPrintDialogData, new_0, 0}},
+ {2408, {wxPrintDialogData, new_1_1, 1}},
+ {2409, {wxPrintDialogData, new_1_0, 1}},
+ {2410, {wxPrintDialogData, destruct, 0}},
+ {2411, {wxPrintDialogData, enableHelp, 1}},
+ {2412, {wxPrintDialogData, enablePageNumbers, 1}},
+ {2413, {wxPrintDialogData, enablePrintToFile, 1}},
+ {2414, {wxPrintDialogData, enableSelection, 1}},
+ {2415, {wxPrintDialogData, getAllPages, 0}},
+ {2416, {wxPrintDialogData, getCollate, 0}},
+ {2417, {wxPrintDialogData, getFromPage, 0}},
+ {2418, {wxPrintDialogData, getMaxPage, 0}},
+ {2419, {wxPrintDialogData, getMinPage, 0}},
+ {2420, {wxPrintDialogData, getNoCopies, 0}},
+ {2421, {wxPrintDialogData, getPrintData, 0}},
+ {2422, {wxPrintDialogData, getPrintToFile, 0}},
+ {2423, {wxPrintDialogData, getSelection, 0}},
+ {2424, {wxPrintDialogData, getToPage, 0}},
+ {2425, {wxPrintDialogData, isOk, 0}},
+ {2426, {wxPrintDialogData, setCollate, 1}},
+ {2427, {wxPrintDialogData, setFromPage, 1}},
+ {2428, {wxPrintDialogData, setMaxPage, 1}},
+ {2429, {wxPrintDialogData, setMinPage, 1}},
+ {2430, {wxPrintDialogData, setNoCopies, 1}},
+ {2431, {wxPrintDialogData, setPrintData, 1}},
+ {2432, {wxPrintDialogData, setPrintToFile, 1}},
+ {2433, {wxPrintDialogData, setSelection, 1}},
+ {2434, {wxPrintDialogData, setToPage, 1}},
+ {2435, {wxPrintData, new_0, 0}},
+ {2436, {wxPrintData, new_1, 1}},
+ {2437, {wxPrintData, destruct, 0}},
+ {2438, {wxPrintData, getCollate, 0}},
+ {2439, {wxPrintData, getBin, 0}},
+ {2440, {wxPrintData, getColour, 0}},
+ {2441, {wxPrintData, getDuplex, 0}},
+ {2442, {wxPrintData, getNoCopies, 0}},
+ {2443, {wxPrintData, getOrientation, 0}},
+ {2444, {wxPrintData, getPaperId, 0}},
+ {2445, {wxPrintData, getPrinterName, 0}},
+ {2446, {wxPrintData, getQuality, 0}},
+ {2447, {wxPrintData, isOk, 0}},
+ {2448, {wxPrintData, setBin, 1}},
+ {2449, {wxPrintData, setCollate, 1}},
+ {2450, {wxPrintData, setColour, 1}},
+ {2451, {wxPrintData, setDuplex, 1}},
+ {2452, {wxPrintData, setNoCopies, 1}},
+ {2453, {wxPrintData, setOrientation, 1}},
+ {2454, {wxPrintData, setPaperId, 1}},
+ {2455, {wxPrintData, setPrinterName, 1}},
+ {2456, {wxPrintData, setQuality, 1}},
+ {2459, {wxPrintPreview, new_2, 2}},
+ {2460, {wxPrintPreview, new_3, 3}},
+ {2462, {wxPrintPreview, destruct, 0}},
+ {2463, {wxPrintPreview, getCanvas, 0}},
+ {2464, {wxPrintPreview, getCurrentPage, 0}},
+ {2465, {wxPrintPreview, getFrame, 0}},
+ {2466, {wxPrintPreview, getMaxPage, 0}},
+ {2467, {wxPrintPreview, getMinPage, 0}},
+ {2468, {wxPrintPreview, getPrintout, 0}},
+ {2469, {wxPrintPreview, getPrintoutForPrinting, 0}},
+ {2470, {wxPrintPreview, isOk, 0}},
+ {2471, {wxPrintPreview, paintPage, 2}},
+ {2472, {wxPrintPreview, print, 1}},
+ {2473, {wxPrintPreview, renderPage, 1}},
+ {2474, {wxPrintPreview, setCanvas, 1}},
+ {2475, {wxPrintPreview, setCurrentPage, 1}},
+ {2476, {wxPrintPreview, setFrame, 1}},
+ {2477, {wxPrintPreview, setPrintout, 1}},
+ {2478, {wxPrintPreview, setZoom, 1}},
+ {2479, {wxPreviewFrame, new, 3}},
+ {2480, {wxPreviewFrame, destruct, 0}},
+ {2481, {wxPreviewFrame, createControlBar, 0}},
+ {2482, {wxPreviewFrame, createCanvas, 0}},
+ {2483, {wxPreviewFrame, initialize, 0}},
+ {2484, {wxPreviewFrame, onCloseWindow, 1}},
+ {2485, {wxPreviewControlBar, new, 4}},
+ {2486, {wxPreviewControlBar, destruct, 0}},
+ {2487, {wxPreviewControlBar, createButtons, 0}},
+ {2488, {wxPreviewControlBar, getPrintPreview, 0}},
+ {2489, {wxPreviewControlBar, getZoomControl, 0}},
+ {2490, {wxPreviewControlBar, setZoomControl, 1}},
+ {2492, {wxPrinter, new, 1}},
+ {2493, {wxPrinter, createAbortWindow, 2}},
+ {2494, {wxPrinter, getAbort, 0}},
+ {2495, {wxPrinter, getLastError, 0}},
+ {2496, {wxPrinter, getPrintDialogData, 0}},
+ {2497, {wxPrinter, print, 3}},
+ {2498, {wxPrinter, printDialog, 1}},
+ {2499, {wxPrinter, reportError, 3}},
+ {2500, {wxPrinter, setup, 1}},
+ {2501, {wxPrinter, 'Destroy', undefined}},
+ {2502, {wxXmlResource, new_1, 1}},
+ {2503, {wxXmlResource, new_2, 2}},
+ {2504, {wxXmlResource, destruct, 0}},
+ {2505, {wxXmlResource, attachUnknownControl, 3}},
+ {2506, {wxXmlResource, clearHandlers, 0}},
+ {2507, {wxXmlResource, compareVersion, 4}},
+ {2508, {wxXmlResource, get, 0}},
+ {2509, {wxXmlResource, getFlags, 0}},
+ {2510, {wxXmlResource, getVersion, 0}},
+ {2511, {wxXmlResource, getXRCID, 2}},
+ {2512, {wxXmlResource, initAllHandlers, 0}},
+ {2513, {wxXmlResource, load, 1}},
+ {2514, {wxXmlResource, loadBitmap, 1}},
+ {2515, {wxXmlResource, loadDialog_2, 2}},
+ {2516, {wxXmlResource, loadDialog_3, 3}},
+ {2517, {wxXmlResource, loadFrame_2, 2}},
+ {2518, {wxXmlResource, loadFrame_3, 3}},
+ {2519, {wxXmlResource, loadIcon, 1}},
+ {2520, {wxXmlResource, loadMenu, 1}},
+ {2521, {wxXmlResource, loadMenuBar_2, 2}},
+ {2522, {wxXmlResource, loadMenuBar_1, 1}},
+ {2523, {wxXmlResource, loadPanel_2, 2}},
+ {2524, {wxXmlResource, loadPanel_3, 3}},
+ {2525, {wxXmlResource, loadToolBar, 2}},
+ {2526, {wxXmlResource, set, 1}},
+ {2527, {wxXmlResource, setFlags, 1}},
+ {2528, {wxXmlResource, unload, 1}},
+ {2529, {wxXmlResource, xrcctrl, 3}},
+ {2530, {wxHtmlEasyPrinting, new, 1}},
+ {2531, {wxHtmlEasyPrinting, destruct, 0}},
+ {2532, {wxHtmlEasyPrinting, getPrintData, 0}},
+ {2533, {wxHtmlEasyPrinting, getPageSetupData, 0}},
+ {2534, {wxHtmlEasyPrinting, previewFile, 1}},
+ {2535, {wxHtmlEasyPrinting, previewText, 2}},
+ {2536, {wxHtmlEasyPrinting, printFile, 1}},
+ {2537, {wxHtmlEasyPrinting, printText, 2}},
+ {2538, {wxHtmlEasyPrinting, pageSetup, 0}},
+ {2539, {wxHtmlEasyPrinting, setFonts, 3}},
+ {2540, {wxHtmlEasyPrinting, setHeader, 2}},
+ {2541, {wxHtmlEasyPrinting, setFooter, 2}},
+ {2543, {wxGLCanvas, new_2, 2}},
+ {2544, {wxGLCanvas, new_3_1, 3}},
+ {2545, {wxGLCanvas, new_3_0, 3}},
+ {2546, {wxGLCanvas, getContext, 0}},
+ {2548, {wxGLCanvas, setCurrent, 0}},
+ {2549, {wxGLCanvas, swapBuffers, 0}},
+ {2550, {wxGLCanvas, 'Destroy', undefined}},
+ {2551, {wxAuiManager, new, 1}},
+ {2552, {wxAuiManager, destruct, 0}},
+ {2553, {wxAuiManager, addPane_2_1, 2}},
+ {2554, {wxAuiManager, addPane_3, 3}},
+ {2555, {wxAuiManager, addPane_2_0, 2}},
+ {2556, {wxAuiManager, detachPane, 1}},
+ {2557, {wxAuiManager, getAllPanes, 0}},
+ {2558, {wxAuiManager, getArtProvider, 0}},
+ {2559, {wxAuiManager, getDockSizeConstraint, 2}},
+ {2560, {wxAuiManager, getFlags, 0}},
+ {2561, {wxAuiManager, getManagedWindow, 0}},
+ {2562, {wxAuiManager, getManager, 1}},
+ {2563, {wxAuiManager, getPane_1_1, 1}},
+ {2564, {wxAuiManager, getPane_1_0, 1}},
+ {2565, {wxAuiManager, hideHint, 0}},
+ {2566, {wxAuiManager, insertPane, 3}},
+ {2567, {wxAuiManager, loadPaneInfo, 2}},
+ {2568, {wxAuiManager, loadPerspective, 2}},
+ {2569, {wxAuiManager, savePaneInfo, 1}},
+ {2570, {wxAuiManager, savePerspective, 0}},
+ {2571, {wxAuiManager, setArtProvider, 1}},
+ {2572, {wxAuiManager, setDockSizeConstraint, 2}},
+ {2573, {wxAuiManager, setFlags, 1}},
+ {2574, {wxAuiManager, setManagedWindow, 1}},
+ {2575, {wxAuiManager, showHint, 1}},
+ {2576, {wxAuiManager, unInit, 0}},
+ {2577, {wxAuiManager, update, 0}},
+ {2578, {wxAuiPaneInfo, new_0, 0}},
+ {2579, {wxAuiPaneInfo, new_1, 1}},
+ {2580, {wxAuiPaneInfo, destruct, 0}},
+ {2581, {wxAuiPaneInfo, bestSize_1, 1}},
+ {2582, {wxAuiPaneInfo, bestSize_2, 2}},
+ {2583, {wxAuiPaneInfo, bottom, 0}},
+ {2584, {wxAuiPaneInfo, bottomDockable, 1}},
+ {2585, {wxAuiPaneInfo, caption, 1}},
+ {2586, {wxAuiPaneInfo, captionVisible, 1}},
+ {2587, {wxAuiPaneInfo, centre, 0}},
+ {2588, {wxAuiPaneInfo, centrePane, 0}},
+ {2589, {wxAuiPaneInfo, closeButton, 1}},
+ {2590, {wxAuiPaneInfo, defaultPane, 0}},
+ {2591, {wxAuiPaneInfo, destroyOnClose, 1}},
+ {2592, {wxAuiPaneInfo, direction, 1}},
+ {2593, {wxAuiPaneInfo, dock, 0}},
+ {2594, {wxAuiPaneInfo, dockable, 1}},
+ {2595, {wxAuiPaneInfo, fixed, 0}},
+ {2596, {wxAuiPaneInfo, float, 0}},
+ {2597, {wxAuiPaneInfo, floatable, 1}},
+ {2598, {wxAuiPaneInfo, floatingPosition_1, 1}},
+ {2599, {wxAuiPaneInfo, floatingPosition_2, 2}},
+ {2600, {wxAuiPaneInfo, floatingSize_1, 1}},
+ {2601, {wxAuiPaneInfo, floatingSize_2, 2}},
+ {2602, {wxAuiPaneInfo, gripper, 1}},
+ {2603, {wxAuiPaneInfo, gripperTop, 1}},
+ {2604, {wxAuiPaneInfo, hasBorder, 0}},
+ {2605, {wxAuiPaneInfo, hasCaption, 0}},
+ {2606, {wxAuiPaneInfo, hasCloseButton, 0}},
+ {2607, {wxAuiPaneInfo, hasFlag, 1}},
+ {2608, {wxAuiPaneInfo, hasGripper, 0}},
+ {2609, {wxAuiPaneInfo, hasGripperTop, 0}},
+ {2610, {wxAuiPaneInfo, hasMaximizeButton, 0}},
+ {2611, {wxAuiPaneInfo, hasMinimizeButton, 0}},
+ {2612, {wxAuiPaneInfo, hasPinButton, 0}},
+ {2613, {wxAuiPaneInfo, hide, 0}},
+ {2614, {wxAuiPaneInfo, isBottomDockable, 0}},
+ {2615, {wxAuiPaneInfo, isDocked, 0}},
+ {2616, {wxAuiPaneInfo, isFixed, 0}},
+ {2617, {wxAuiPaneInfo, isFloatable, 0}},
+ {2618, {wxAuiPaneInfo, isFloating, 0}},
+ {2619, {wxAuiPaneInfo, isLeftDockable, 0}},
+ {2620, {wxAuiPaneInfo, isMovable, 0}},
+ {2621, {wxAuiPaneInfo, isOk, 0}},
+ {2622, {wxAuiPaneInfo, isResizable, 0}},
+ {2623, {wxAuiPaneInfo, isRightDockable, 0}},
+ {2624, {wxAuiPaneInfo, isShown, 0}},
+ {2625, {wxAuiPaneInfo, isToolbar, 0}},
+ {2626, {wxAuiPaneInfo, isTopDockable, 0}},
+ {2627, {wxAuiPaneInfo, layer, 1}},
+ {2628, {wxAuiPaneInfo, left, 0}},
+ {2629, {wxAuiPaneInfo, leftDockable, 1}},
+ {2630, {wxAuiPaneInfo, maxSize_1, 1}},
+ {2631, {wxAuiPaneInfo, maxSize_2, 2}},
+ {2632, {wxAuiPaneInfo, maximizeButton, 1}},
+ {2633, {wxAuiPaneInfo, minSize_1, 1}},
+ {2634, {wxAuiPaneInfo, minSize_2, 2}},
+ {2635, {wxAuiPaneInfo, minimizeButton, 1}},
+ {2636, {wxAuiPaneInfo, movable, 1}},
+ {2637, {wxAuiPaneInfo, name, 1}},
+ {2638, {wxAuiPaneInfo, paneBorder, 1}},
+ {2639, {wxAuiPaneInfo, pinButton, 1}},
+ {2640, {wxAuiPaneInfo, position, 1}},
+ {2641, {wxAuiPaneInfo, resizable, 1}},
+ {2642, {wxAuiPaneInfo, right, 0}},
+ {2643, {wxAuiPaneInfo, rightDockable, 1}},
+ {2644, {wxAuiPaneInfo, row, 1}},
+ {2645, {wxAuiPaneInfo, safeSet, 1}},
+ {2646, {wxAuiPaneInfo, setFlag, 2}},
+ {2647, {wxAuiPaneInfo, show, 1}},
+ {2648, {wxAuiPaneInfo, toolbarPane, 0}},
+ {2649, {wxAuiPaneInfo, top, 0}},
+ {2650, {wxAuiPaneInfo, topDockable, 1}},
+ {2651, {wxAuiPaneInfo, window, 1}},
+ {2652, {wxAuiNotebook, new_0, 0}},
+ {2653, {wxAuiNotebook, new_2, 2}},
+ {2654, {wxAuiNotebook, addPage, 3}},
+ {2655, {wxAuiNotebook, create, 2}},
+ {2656, {wxAuiNotebook, deletePage, 1}},
+ {2657, {wxAuiNotebook, getArtProvider, 0}},
+ {2658, {wxAuiNotebook, getPage, 1}},
+ {2659, {wxAuiNotebook, getPageBitmap, 1}},
+ {2660, {wxAuiNotebook, getPageCount, 0}},
+ {2661, {wxAuiNotebook, getPageIndex, 1}},
+ {2662, {wxAuiNotebook, getPageText, 1}},
+ {2663, {wxAuiNotebook, getSelection, 0}},
+ {2664, {wxAuiNotebook, insertPage, 4}},
+ {2665, {wxAuiNotebook, removePage, 1}},
+ {2666, {wxAuiNotebook, setArtProvider, 1}},
+ {2667, {wxAuiNotebook, setFont, 1}},
+ {2668, {wxAuiNotebook, setPageBitmap, 2}},
+ {2669, {wxAuiNotebook, setPageText, 2}},
+ {2670, {wxAuiNotebook, setSelection, 1}},
+ {2671, {wxAuiNotebook, setTabCtrlHeight, 1}},
+ {2672, {wxAuiNotebook, setUniformBitmapSize, 1}},
+ {2673, {wxAuiNotebook, 'Destroy', undefined}},
+ {2674, {wxMDIParentFrame, new_0, 0}},
+ {2675, {wxMDIParentFrame, new_4, 4}},
+ {2676, {wxMDIParentFrame, destruct, 0}},
+ {2677, {wxMDIParentFrame, activateNext, 0}},
+ {2678, {wxMDIParentFrame, activatePrevious, 0}},
+ {2679, {wxMDIParentFrame, arrangeIcons, 0}},
+ {2680, {wxMDIParentFrame, cascade, 0}},
+ {2681, {wxMDIParentFrame, create, 4}},
+ {2682, {wxMDIParentFrame, getActiveChild, 0}},
+ {2683, {wxMDIParentFrame, getClientWindow, 0}},
+ {2684, {wxMDIParentFrame, tile, 1}},
+ {2685, {wxMDIChildFrame, new_0, 0}},
+ {2686, {wxMDIChildFrame, new_4, 4}},
+ {2687, {wxMDIChildFrame, destruct, 0}},
+ {2688, {wxMDIChildFrame, activate, 0}},
+ {2689, {wxMDIChildFrame, create, 4}},
+ {2690, {wxMDIChildFrame, maximize, 1}},
+ {2691, {wxMDIChildFrame, restore, 0}},
+ {2692, {wxMDIClientWindow, new_0, 0}},
+ {2693, {wxMDIClientWindow, new_2, 2}},
+ {2694, {wxMDIClientWindow, destruct, 0}},
+ {2695, {wxMDIClientWindow, createClient, 2}},
+ {2696, {wxLayoutAlgorithm, new, 0}},
+ {2697, {wxLayoutAlgorithm, layoutFrame, 2}},
+ {2698, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
+ {2699, {wxLayoutAlgorithm, layoutWindow, 2}},
+ {2700, {wxLayoutAlgorithm, 'Destroy', undefined}},
+ {2701, {wxEvent, getId, 0}},
+ {2702, {wxEvent, getSkipped, 0}},
+ {2703, {wxEvent, getTimestamp, 0}},
+ {2704, {wxEvent, isCommandEvent, 0}},
+ {2705, {wxEvent, resumePropagation, 1}},
+ {2706, {wxEvent, shouldPropagate, 0}},
+ {2707, {wxEvent, skip, 1}},
+ {2708, {wxEvent, stopPropagation, 0}},
+ {2709, {wxCommandEvent, getClientData, 0}},
+ {2710, {wxCommandEvent, getExtraLong, 0}},
+ {2711, {wxCommandEvent, getInt, 0}},
+ {2712, {wxCommandEvent, getSelection, 0}},
+ {2713, {wxCommandEvent, getString, 0}},
+ {2714, {wxCommandEvent, isChecked, 0}},
+ {2715, {wxCommandEvent, isSelection, 0}},
+ {2716, {wxCommandEvent, setInt, 1}},
+ {2717, {wxCommandEvent, setString, 1}},
+ {2718, {wxScrollEvent, getOrientation, 0}},
+ {2719, {wxScrollEvent, getPosition, 0}},
+ {2720, {wxScrollWinEvent, getOrientation, 0}},
+ {2721, {wxScrollWinEvent, getPosition, 0}},
+ {2722, {wxMouseEvent, altDown, 0}},
+ {2723, {wxMouseEvent, button, 1}},
+ {2724, {wxMouseEvent, buttonDClick, 1}},
+ {2725, {wxMouseEvent, buttonDown, 1}},
+ {2726, {wxMouseEvent, buttonUp, 1}},
+ {2727, {wxMouseEvent, cmdDown, 0}},
+ {2728, {wxMouseEvent, controlDown, 0}},
+ {2729, {wxMouseEvent, dragging, 0}},
+ {2730, {wxMouseEvent, entering, 0}},
+ {2731, {wxMouseEvent, getButton, 0}},
+ {2734, {wxMouseEvent, getPosition, 0}},
+ {2735, {wxMouseEvent, getLogicalPosition, 1}},
+ {2736, {wxMouseEvent, getLinesPerAction, 0}},
+ {2737, {wxMouseEvent, getWheelRotation, 0}},
+ {2738, {wxMouseEvent, getWheelDelta, 0}},
+ {2739, {wxMouseEvent, getX, 0}},
+ {2740, {wxMouseEvent, getY, 0}},
+ {2741, {wxMouseEvent, isButton, 0}},
+ {2742, {wxMouseEvent, isPageScroll, 0}},
+ {2743, {wxMouseEvent, leaving, 0}},
+ {2744, {wxMouseEvent, leftDClick, 0}},
+ {2745, {wxMouseEvent, leftDown, 0}},
+ {2746, {wxMouseEvent, leftIsDown, 0}},
+ {2747, {wxMouseEvent, leftUp, 0}},
+ {2748, {wxMouseEvent, metaDown, 0}},
+ {2749, {wxMouseEvent, middleDClick, 0}},
+ {2750, {wxMouseEvent, middleDown, 0}},
+ {2751, {wxMouseEvent, middleIsDown, 0}},
+ {2752, {wxMouseEvent, middleUp, 0}},
+ {2753, {wxMouseEvent, moving, 0}},
+ {2754, {wxMouseEvent, rightDClick, 0}},
+ {2755, {wxMouseEvent, rightDown, 0}},
+ {2756, {wxMouseEvent, rightIsDown, 0}},
+ {2757, {wxMouseEvent, rightUp, 0}},
+ {2758, {wxMouseEvent, shiftDown, 0}},
+ {2759, {wxSetCursorEvent, getCursor, 0}},
+ {2760, {wxSetCursorEvent, getX, 0}},
+ {2761, {wxSetCursorEvent, getY, 0}},
+ {2762, {wxSetCursorEvent, hasCursor, 0}},
+ {2763, {wxSetCursorEvent, setCursor, 1}},
+ {2764, {wxKeyEvent, altDown, 0}},
+ {2765, {wxKeyEvent, cmdDown, 0}},
+ {2766, {wxKeyEvent, controlDown, 0}},
+ {2767, {wxKeyEvent, getKeyCode, 0}},
+ {2768, {wxKeyEvent, getModifiers, 0}},
+ {2771, {wxKeyEvent, getPosition, 0}},
+ {2772, {wxKeyEvent, getRawKeyCode, 0}},
+ {2773, {wxKeyEvent, getRawKeyFlags, 0}},
+ {2774, {wxKeyEvent, getUnicodeKey, 0}},
+ {2775, {wxKeyEvent, getX, 0}},
+ {2776, {wxKeyEvent, getY, 0}},
+ {2777, {wxKeyEvent, hasModifiers, 0}},
+ {2778, {wxKeyEvent, metaDown, 0}},
+ {2779, {wxKeyEvent, shiftDown, 0}},
+ {2780, {wxSizeEvent, getSize, 0}},
+ {2781, {wxMoveEvent, getPosition, 0}},
+ {2782, {wxEraseEvent, getDC, 0}},
+ {2783, {wxFocusEvent, getWindow, 0}},
+ {2784, {wxChildFocusEvent, getWindow, 0}},
+ {2785, {wxMenuEvent, getMenu, 0}},
+ {2786, {wxMenuEvent, getMenuId, 0}},
+ {2787, {wxMenuEvent, isPopup, 0}},
+ {2788, {wxCloseEvent, canVeto, 0}},
+ {2789, {wxCloseEvent, getLoggingOff, 0}},
+ {2790, {wxCloseEvent, setCanVeto, 1}},
+ {2791, {wxCloseEvent, setLoggingOff, 1}},
+ {2792, {wxCloseEvent, veto, 1}},
+ {2793, {wxShowEvent, setShow, 1}},
+ {2794, {wxShowEvent, getShow, 0}},
+ {2795, {wxIconizeEvent, iconized, 0}},
+ {2796, {wxJoystickEvent, buttonDown, 1}},
+ {2797, {wxJoystickEvent, buttonIsDown, 1}},
+ {2798, {wxJoystickEvent, buttonUp, 1}},
+ {2799, {wxJoystickEvent, getButtonChange, 0}},
+ {2800, {wxJoystickEvent, getButtonState, 0}},
+ {2801, {wxJoystickEvent, getJoystick, 0}},
+ {2802, {wxJoystickEvent, getPosition, 0}},
+ {2803, {wxJoystickEvent, getZPosition, 0}},
+ {2804, {wxJoystickEvent, isButton, 0}},
+ {2805, {wxJoystickEvent, isMove, 0}},
+ {2806, {wxJoystickEvent, isZMove, 0}},
+ {2807, {wxUpdateUIEvent, canUpdate, 1}},
+ {2808, {wxUpdateUIEvent, check, 1}},
+ {2809, {wxUpdateUIEvent, enable, 1}},
+ {2810, {wxUpdateUIEvent, show, 1}},
+ {2811, {wxUpdateUIEvent, getChecked, 0}},
+ {2812, {wxUpdateUIEvent, getEnabled, 0}},
+ {2813, {wxUpdateUIEvent, getShown, 0}},
+ {2814, {wxUpdateUIEvent, getSetChecked, 0}},
+ {2815, {wxUpdateUIEvent, getSetEnabled, 0}},
+ {2816, {wxUpdateUIEvent, getSetShown, 0}},
+ {2817, {wxUpdateUIEvent, getSetText, 0}},
+ {2818, {wxUpdateUIEvent, getText, 0}},
+ {2819, {wxUpdateUIEvent, getMode, 0}},
+ {2820, {wxUpdateUIEvent, getUpdateInterval, 0}},
+ {2821, {wxUpdateUIEvent, resetUpdateTime, 0}},
+ {2822, {wxUpdateUIEvent, setMode, 1}},
+ {2823, {wxUpdateUIEvent, setText, 1}},
+ {2824, {wxUpdateUIEvent, setUpdateInterval, 1}},
+ {2825, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
+ {2826, {wxPaletteChangedEvent, setChangedWindow, 1}},
+ {2827, {wxPaletteChangedEvent, getChangedWindow, 0}},
+ {2828, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
+ {2829, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
+ {2830, {wxNavigationKeyEvent, getDirection, 0}},
+ {2831, {wxNavigationKeyEvent, setDirection, 1}},
+ {2832, {wxNavigationKeyEvent, isWindowChange, 0}},
+ {2833, {wxNavigationKeyEvent, setWindowChange, 1}},
+ {2834, {wxNavigationKeyEvent, isFromTab, 0}},
+ {2835, {wxNavigationKeyEvent, setFromTab, 1}},
+ {2836, {wxNavigationKeyEvent, getCurrentFocus, 0}},
+ {2837, {wxNavigationKeyEvent, setCurrentFocus, 1}},
+ {2838, {wxHelpEvent, getOrigin, 0}},
+ {2839, {wxHelpEvent, getPosition, 0}},
+ {2840, {wxHelpEvent, setOrigin, 1}},
+ {2841, {wxHelpEvent, setPosition, 1}},
+ {2842, {wxContextMenuEvent, getPosition, 0}},
+ {2843, {wxContextMenuEvent, setPosition, 1}},
+ {2844, {wxIdleEvent, canSend, 1}},
+ {2845, {wxIdleEvent, getMode, 0}},
+ {2846, {wxIdleEvent, requestMore, 1}},
+ {2847, {wxIdleEvent, moreRequested, 0}},
+ {2848, {wxIdleEvent, setMode, 1}},
+ {2849, {wxGridEvent, altDown, 0}},
+ {2850, {wxGridEvent, controlDown, 0}},
+ {2851, {wxGridEvent, getCol, 0}},
+ {2852, {wxGridEvent, getPosition, 0}},
+ {2853, {wxGridEvent, getRow, 0}},
+ {2854, {wxGridEvent, metaDown, 0}},
+ {2855, {wxGridEvent, selecting, 0}},
+ {2856, {wxGridEvent, shiftDown, 0}},
+ {2857, {wxNotifyEvent, allow, 0}},
+ {2858, {wxNotifyEvent, isAllowed, 0}},
+ {2859, {wxNotifyEvent, veto, 0}},
+ {2860, {wxSashEvent, getEdge, 0}},
+ {2861, {wxSashEvent, getDragRect, 0}},
+ {2862, {wxSashEvent, getDragStatus, 0}},
+ {2863, {wxListEvent, getCacheFrom, 0}},
+ {2864, {wxListEvent, getCacheTo, 0}},
+ {2865, {wxListEvent, getKeyCode, 0}},
+ {2866, {wxListEvent, getIndex, 0}},
+ {2867, {wxListEvent, getColumn, 0}},
+ {2868, {wxListEvent, getPoint, 0}},
+ {2869, {wxListEvent, getLabel, 0}},
+ {2870, {wxListEvent, getText, 0}},
+ {2871, {wxListEvent, getImage, 0}},
+ {2872, {wxListEvent, getData, 0}},
+ {2873, {wxListEvent, getMask, 0}},
+ {2874, {wxListEvent, getItem, 0}},
+ {2875, {wxListEvent, isEditCancelled, 0}},
+ {2876, {wxDateEvent, getDate, 0}},
+ {2877, {wxCalendarEvent, getWeekDay, 0}},
+ {2878, {wxFileDirPickerEvent, getPath, 0}},
+ {2879, {wxColourPickerEvent, getColour, 0}},
+ {2880, {wxFontPickerEvent, getFont, 0}},
+ {2881, {wxStyledTextEvent, getPosition, 0}},
+ {2882, {wxStyledTextEvent, getKey, 0}},
+ {2883, {wxStyledTextEvent, getModifiers, 0}},
+ {2884, {wxStyledTextEvent, getModificationType, 0}},
+ {2885, {wxStyledTextEvent, getText, 0}},
+ {2886, {wxStyledTextEvent, getLength, 0}},
+ {2887, {wxStyledTextEvent, getLinesAdded, 0}},
+ {2888, {wxStyledTextEvent, getLine, 0}},
+ {2889, {wxStyledTextEvent, getFoldLevelNow, 0}},
+ {2890, {wxStyledTextEvent, getFoldLevelPrev, 0}},
+ {2891, {wxStyledTextEvent, getMargin, 0}},
+ {2892, {wxStyledTextEvent, getMessage, 0}},
+ {2893, {wxStyledTextEvent, getWParam, 0}},
+ {2894, {wxStyledTextEvent, getLParam, 0}},
+ {2895, {wxStyledTextEvent, getListType, 0}},
+ {2896, {wxStyledTextEvent, getX, 0}},
+ {2897, {wxStyledTextEvent, getY, 0}},
+ {2898, {wxStyledTextEvent, getDragText, 0}},
+ {2899, {wxStyledTextEvent, getDragAllowMove, 0}},
+ {2900, {wxStyledTextEvent, getDragResult, 0}},
+ {2901, {wxStyledTextEvent, getShift, 0}},
+ {2902, {wxStyledTextEvent, getControl, 0}},
+ {2903, {wxStyledTextEvent, getAlt, 0}},
+ {2904, {utils, getKeyState, 1}},
+ {2905, {utils, getMousePosition, 2}},
+ {2906, {utils, getMouseState, 0}},
+ {2907, {utils, setDetectableAutoRepeat, 1}},
+ {2908, {utils, bell, 0}},
+ {2909, {utils, findMenuItemId, 3}},
+ {2910, {utils, genericFindWindowAtPoint, 1}},
+ {2911, {utils, findWindowAtPoint, 1}},
+ {2912, {utils, beginBusyCursor, 1}},
+ {2913, {utils, endBusyCursor, 0}},
+ {2914, {utils, isBusy, 0}},
+ {2915, {utils, shutdown, 1}},
+ {2916, {utils, shell, 1}},
+ {2917, {utils, launchDefaultBrowser, 2}},
+ {2918, {utils, getEmailAddress, 0}},
+ {2919, {utils, getUserId, 0}},
+ {2920, {utils, getHomeDir, 0}},
+ {2921, {utils, newId, 0}},
+ {2922, {utils, registerId, 1}},
+ {2923, {utils, getCurrentId, 0}},
+ {2924, {utils, getOsDescription, 0}},
+ {2925, {utils, isPlatformLittleEndian, 0}},
+ {2926, {utils, isPlatform64Bit, 0}},
+ {2927, {wxPrintout, new, 1}},
+ {2928, {wxPrintout, destruct, 0}},
+ {2929, {wxPrintout, getDC, 0}},
+ {2930, {wxPrintout, getPageSizeMM, 2}},
+ {2931, {wxPrintout, getPageSizePixels, 2}},
+ {2932, {wxPrintout, getPaperRectPixels, 0}},
+ {2933, {wxPrintout, getPPIPrinter, 2}},
+ {2934, {wxPrintout, getPPIScreen, 2}},
+ {2935, {wxPrintout, getTitle, 0}},
+ {2936, {wxPrintout, isPreview, 0}},
+ {2937, {wxPrintout, fitThisSizeToPaper, 1}},
+ {2938, {wxPrintout, fitThisSizeToPage, 1}},
+ {2939, {wxPrintout, fitThisSizeToPageMargins, 2}},
+ {2940, {wxPrintout, mapScreenSizeToPaper, 0}},
+ {2941, {wxPrintout, mapScreenSizeToPage, 0}},
+ {2942, {wxPrintout, mapScreenSizeToPageMargins, 1}},
+ {2943, {wxPrintout, mapScreenSizeToDevice, 0}},
+ {2944, {wxPrintout, getLogicalPaperRect, 0}},
+ {2945, {wxPrintout, getLogicalPageRect, 0}},
+ {2946, {wxPrintout, getLogicalPageMarginsRect, 1}},
+ {2947, {wxPrintout, setLogicalOrigin, 2}},
+ {2948, {wxPrintout, offsetLogicalOrigin, 2}},
+ {2949, {wxStyledTextCtrl, new_2, 2}},
+ {2950, {wxStyledTextCtrl, new_0, 0}},
+ {2951, {wxStyledTextCtrl, destruct, 0}},
+ {2952, {wxStyledTextCtrl, create, 2}},
+ {2953, {wxStyledTextCtrl, addText, 1}},
+ {2954, {wxStyledTextCtrl, addStyledText, 1}},
+ {2955, {wxStyledTextCtrl, insertText, 2}},
+ {2956, {wxStyledTextCtrl, clearAll, 0}},
+ {2957, {wxStyledTextCtrl, clearDocumentStyle, 0}},
+ {2958, {wxStyledTextCtrl, getLength, 0}},
+ {2959, {wxStyledTextCtrl, getCharAt, 1}},
+ {2960, {wxStyledTextCtrl, getCurrentPos, 0}},
+ {2961, {wxStyledTextCtrl, getAnchor, 0}},
+ {2962, {wxStyledTextCtrl, getStyleAt, 1}},
+ {2963, {wxStyledTextCtrl, redo, 0}},
+ {2964, {wxStyledTextCtrl, setUndoCollection, 1}},
+ {2965, {wxStyledTextCtrl, selectAll, 0}},
+ {2966, {wxStyledTextCtrl, setSavePoint, 0}},
+ {2967, {wxStyledTextCtrl, getStyledText, 2}},
+ {2968, {wxStyledTextCtrl, canRedo, 0}},
+ {2969, {wxStyledTextCtrl, markerLineFromHandle, 1}},
+ {2970, {wxStyledTextCtrl, markerDeleteHandle, 1}},
+ {2971, {wxStyledTextCtrl, getUndoCollection, 0}},
+ {2972, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
+ {2973, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
+ {2974, {wxStyledTextCtrl, positionFromPoint, 1}},
+ {2975, {wxStyledTextCtrl, positionFromPointClose, 2}},
+ {2976, {wxStyledTextCtrl, gotoLine, 1}},
+ {2977, {wxStyledTextCtrl, gotoPos, 1}},
+ {2978, {wxStyledTextCtrl, setAnchor, 1}},
+ {2979, {wxStyledTextCtrl, getCurLine, 1}},
+ {2980, {wxStyledTextCtrl, getEndStyled, 0}},
+ {2981, {wxStyledTextCtrl, convertEOLs, 1}},
+ {2982, {wxStyledTextCtrl, getEOLMode, 0}},
+ {2983, {wxStyledTextCtrl, setEOLMode, 1}},
+ {2984, {wxStyledTextCtrl, startStyling, 2}},
+ {2985, {wxStyledTextCtrl, setStyling, 2}},
+ {2986, {wxStyledTextCtrl, getBufferedDraw, 0}},
+ {2987, {wxStyledTextCtrl, setBufferedDraw, 1}},
+ {2988, {wxStyledTextCtrl, setTabWidth, 1}},
+ {2989, {wxStyledTextCtrl, getTabWidth, 0}},
+ {2990, {wxStyledTextCtrl, setCodePage, 1}},
+ {2991, {wxStyledTextCtrl, markerDefine, 3}},
+ {2992, {wxStyledTextCtrl, markerSetForeground, 2}},
+ {2993, {wxStyledTextCtrl, markerSetBackground, 2}},
+ {2994, {wxStyledTextCtrl, markerAdd, 2}},
+ {2995, {wxStyledTextCtrl, markerDelete, 2}},
+ {2996, {wxStyledTextCtrl, markerDeleteAll, 1}},
+ {2997, {wxStyledTextCtrl, markerGet, 1}},
+ {2998, {wxStyledTextCtrl, markerNext, 2}},
+ {2999, {wxStyledTextCtrl, markerPrevious, 2}},
+ {3000, {wxStyledTextCtrl, markerDefineBitmap, 2}},
+ {3001, {wxStyledTextCtrl, markerAddSet, 2}},
+ {3002, {wxStyledTextCtrl, markerSetAlpha, 2}},
+ {3003, {wxStyledTextCtrl, setMarginType, 2}},
+ {3004, {wxStyledTextCtrl, getMarginType, 1}},
+ {3005, {wxStyledTextCtrl, setMarginWidth, 2}},
+ {3006, {wxStyledTextCtrl, getMarginWidth, 1}},
+ {3007, {wxStyledTextCtrl, setMarginMask, 2}},
+ {3008, {wxStyledTextCtrl, getMarginMask, 1}},
+ {3009, {wxStyledTextCtrl, setMarginSensitive, 2}},
+ {3010, {wxStyledTextCtrl, getMarginSensitive, 1}},
+ {3011, {wxStyledTextCtrl, styleClearAll, 0}},
+ {3012, {wxStyledTextCtrl, styleSetForeground, 2}},
+ {3013, {wxStyledTextCtrl, styleSetBackground, 2}},
+ {3014, {wxStyledTextCtrl, styleSetBold, 2}},
+ {3015, {wxStyledTextCtrl, styleSetItalic, 2}},
+ {3016, {wxStyledTextCtrl, styleSetSize, 2}},
+ {3017, {wxStyledTextCtrl, styleSetFaceName, 2}},
+ {3018, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
+ {3019, {wxStyledTextCtrl, styleResetDefault, 0}},
+ {3020, {wxStyledTextCtrl, styleSetUnderline, 2}},
+ {3021, {wxStyledTextCtrl, styleSetCase, 2}},
+ {3022, {wxStyledTextCtrl, styleSetHotSpot, 2}},
+ {3023, {wxStyledTextCtrl, setSelForeground, 2}},
+ {3024, {wxStyledTextCtrl, setSelBackground, 2}},
+ {3025, {wxStyledTextCtrl, getSelAlpha, 0}},
+ {3026, {wxStyledTextCtrl, setSelAlpha, 1}},
+ {3027, {wxStyledTextCtrl, setCaretForeground, 1}},
+ {3028, {wxStyledTextCtrl, cmdKeyAssign, 3}},
+ {3029, {wxStyledTextCtrl, cmdKeyClear, 2}},
+ {3030, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
+ {3031, {wxStyledTextCtrl, setStyleBytes, 2}},
+ {3032, {wxStyledTextCtrl, styleSetVisible, 2}},
+ {3033, {wxStyledTextCtrl, getCaretPeriod, 0}},
+ {3034, {wxStyledTextCtrl, setCaretPeriod, 1}},
+ {3035, {wxStyledTextCtrl, setWordChars, 1}},
+ {3036, {wxStyledTextCtrl, beginUndoAction, 0}},
+ {3037, {wxStyledTextCtrl, endUndoAction, 0}},
+ {3038, {wxStyledTextCtrl, indicatorSetStyle, 2}},
+ {3039, {wxStyledTextCtrl, indicatorGetStyle, 1}},
+ {3040, {wxStyledTextCtrl, indicatorSetForeground, 2}},
+ {3041, {wxStyledTextCtrl, indicatorGetForeground, 1}},
+ {3042, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
+ {3043, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
+ {3044, {wxStyledTextCtrl, getStyleBits, 0}},
+ {3045, {wxStyledTextCtrl, setLineState, 2}},
+ {3046, {wxStyledTextCtrl, getLineState, 1}},
+ {3047, {wxStyledTextCtrl, getMaxLineState, 0}},
+ {3048, {wxStyledTextCtrl, getCaretLineVisible, 0}},
+ {3049, {wxStyledTextCtrl, setCaretLineVisible, 1}},
+ {3050, {wxStyledTextCtrl, getCaretLineBackground, 0}},
+ {3051, {wxStyledTextCtrl, setCaretLineBackground, 1}},
+ {3052, {wxStyledTextCtrl, autoCompShow, 2}},
+ {3053, {wxStyledTextCtrl, autoCompCancel, 0}},
+ {3054, {wxStyledTextCtrl, autoCompActive, 0}},
+ {3055, {wxStyledTextCtrl, autoCompPosStart, 0}},
+ {3056, {wxStyledTextCtrl, autoCompComplete, 0}},
+ {3057, {wxStyledTextCtrl, autoCompStops, 1}},
+ {3058, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
+ {3059, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
+ {3060, {wxStyledTextCtrl, autoCompSelect, 1}},
+ {3061, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
+ {3062, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
+ {3063, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
+ {3064, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
+ {3065, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
+ {3066, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
+ {3067, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
+ {3068, {wxStyledTextCtrl, userListShow, 2}},
+ {3069, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
+ {3070, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
+ {3071, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
+ {3072, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
+ {3073, {wxStyledTextCtrl, registerImage, 2}},
+ {3074, {wxStyledTextCtrl, clearRegisteredImages, 0}},
+ {3075, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
+ {3076, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
+ {3077, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
+ {3078, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
+ {3079, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
+ {3080, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
+ {3081, {wxStyledTextCtrl, setIndent, 1}},
+ {3082, {wxStyledTextCtrl, getIndent, 0}},
+ {3083, {wxStyledTextCtrl, setUseTabs, 1}},
+ {3084, {wxStyledTextCtrl, getUseTabs, 0}},
+ {3085, {wxStyledTextCtrl, setLineIndentation, 2}},
+ {3086, {wxStyledTextCtrl, getLineIndentation, 1}},
+ {3087, {wxStyledTextCtrl, getLineIndentPosition, 1}},
+ {3088, {wxStyledTextCtrl, getColumn, 1}},
+ {3089, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
+ {3090, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
+ {3091, {wxStyledTextCtrl, setIndentationGuides, 1}},
+ {3092, {wxStyledTextCtrl, getIndentationGuides, 0}},
+ {3093, {wxStyledTextCtrl, setHighlightGuide, 1}},
+ {3094, {wxStyledTextCtrl, getHighlightGuide, 0}},
+ {3095, {wxStyledTextCtrl, getLineEndPosition, 1}},
+ {3096, {wxStyledTextCtrl, getCodePage, 0}},
+ {3097, {wxStyledTextCtrl, getCaretForeground, 0}},
+ {3098, {wxStyledTextCtrl, getReadOnly, 0}},
+ {3099, {wxStyledTextCtrl, setCurrentPos, 1}},
+ {3100, {wxStyledTextCtrl, setSelectionStart, 1}},
+ {3101, {wxStyledTextCtrl, getSelectionStart, 0}},
+ {3102, {wxStyledTextCtrl, setSelectionEnd, 1}},
+ {3103, {wxStyledTextCtrl, getSelectionEnd, 0}},
+ {3104, {wxStyledTextCtrl, setPrintMagnification, 1}},
+ {3105, {wxStyledTextCtrl, getPrintMagnification, 0}},
+ {3106, {wxStyledTextCtrl, setPrintColourMode, 1}},
+ {3107, {wxStyledTextCtrl, getPrintColourMode, 0}},
+ {3108, {wxStyledTextCtrl, findText, 4}},
+ {3109, {wxStyledTextCtrl, formatRange, 7}},
+ {3110, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
+ {3111, {wxStyledTextCtrl, getLine, 1}},
+ {3112, {wxStyledTextCtrl, getLineCount, 0}},
+ {3113, {wxStyledTextCtrl, setMarginLeft, 1}},
+ {3114, {wxStyledTextCtrl, getMarginLeft, 0}},
+ {3115, {wxStyledTextCtrl, setMarginRight, 1}},
+ {3116, {wxStyledTextCtrl, getMarginRight, 0}},
+ {3117, {wxStyledTextCtrl, getModify, 0}},
+ {3118, {wxStyledTextCtrl, setSelection, 2}},
+ {3119, {wxStyledTextCtrl, getSelectedText, 0}},
+ {3120, {wxStyledTextCtrl, getTextRange, 2}},
+ {3121, {wxStyledTextCtrl, hideSelection, 1}},
+ {3122, {wxStyledTextCtrl, lineFromPosition, 1}},
+ {3123, {wxStyledTextCtrl, positionFromLine, 1}},
+ {3124, {wxStyledTextCtrl, lineScroll, 2}},
+ {3125, {wxStyledTextCtrl, ensureCaretVisible, 0}},
+ {3126, {wxStyledTextCtrl, replaceSelection, 1}},
+ {3127, {wxStyledTextCtrl, setReadOnly, 1}},
+ {3128, {wxStyledTextCtrl, canPaste, 0}},
+ {3129, {wxStyledTextCtrl, canUndo, 0}},
+ {3130, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
+ {3131, {wxStyledTextCtrl, undo, 0}},
+ {3132, {wxStyledTextCtrl, cut, 0}},
+ {3133, {wxStyledTextCtrl, copy, 0}},
+ {3134, {wxStyledTextCtrl, paste, 0}},
+ {3135, {wxStyledTextCtrl, clear, 0}},
+ {3136, {wxStyledTextCtrl, setText, 1}},
+ {3137, {wxStyledTextCtrl, getText, 0}},
+ {3138, {wxStyledTextCtrl, getTextLength, 0}},
+ {3139, {wxStyledTextCtrl, getOvertype, 0}},
+ {3140, {wxStyledTextCtrl, setCaretWidth, 1}},
+ {3141, {wxStyledTextCtrl, getCaretWidth, 0}},
+ {3142, {wxStyledTextCtrl, setTargetStart, 1}},
+ {3143, {wxStyledTextCtrl, getTargetStart, 0}},
+ {3144, {wxStyledTextCtrl, setTargetEnd, 1}},
+ {3145, {wxStyledTextCtrl, getTargetEnd, 0}},
+ {3146, {wxStyledTextCtrl, replaceTarget, 1}},
+ {3147, {wxStyledTextCtrl, searchInTarget, 1}},
+ {3148, {wxStyledTextCtrl, setSearchFlags, 1}},
+ {3149, {wxStyledTextCtrl, getSearchFlags, 0}},
+ {3150, {wxStyledTextCtrl, callTipShow, 2}},
+ {3151, {wxStyledTextCtrl, callTipCancel, 0}},
+ {3152, {wxStyledTextCtrl, callTipActive, 0}},
+ {3153, {wxStyledTextCtrl, callTipPosAtStart, 0}},
+ {3154, {wxStyledTextCtrl, callTipSetHighlight, 2}},
+ {3155, {wxStyledTextCtrl, callTipSetBackground, 1}},
+ {3156, {wxStyledTextCtrl, callTipSetForeground, 1}},
+ {3157, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
+ {3158, {wxStyledTextCtrl, callTipUseStyle, 1}},
+ {3159, {wxStyledTextCtrl, visibleFromDocLine, 1}},
+ {3160, {wxStyledTextCtrl, docLineFromVisible, 1}},
+ {3161, {wxStyledTextCtrl, wrapCount, 1}},
+ {3162, {wxStyledTextCtrl, setFoldLevel, 2}},
+ {3163, {wxStyledTextCtrl, getFoldLevel, 1}},
+ {3164, {wxStyledTextCtrl, getLastChild, 2}},
+ {3165, {wxStyledTextCtrl, getFoldParent, 1}},
+ {3166, {wxStyledTextCtrl, showLines, 2}},
+ {3167, {wxStyledTextCtrl, hideLines, 2}},
+ {3168, {wxStyledTextCtrl, getLineVisible, 1}},
+ {3169, {wxStyledTextCtrl, setFoldExpanded, 2}},
+ {3170, {wxStyledTextCtrl, getFoldExpanded, 1}},
+ {3171, {wxStyledTextCtrl, toggleFold, 1}},
+ {3172, {wxStyledTextCtrl, ensureVisible, 1}},
+ {3173, {wxStyledTextCtrl, setFoldFlags, 1}},
+ {3174, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
+ {3175, {wxStyledTextCtrl, setTabIndents, 1}},
+ {3176, {wxStyledTextCtrl, getTabIndents, 0}},
+ {3177, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
+ {3178, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
+ {3179, {wxStyledTextCtrl, setMouseDwellTime, 1}},
+ {3180, {wxStyledTextCtrl, getMouseDwellTime, 0}},
+ {3181, {wxStyledTextCtrl, wordStartPosition, 2}},
+ {3182, {wxStyledTextCtrl, wordEndPosition, 2}},
+ {3183, {wxStyledTextCtrl, setWrapMode, 1}},
+ {3184, {wxStyledTextCtrl, getWrapMode, 0}},
+ {3185, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
+ {3186, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
+ {3187, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
+ {3188, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
+ {3189, {wxStyledTextCtrl, setWrapStartIndent, 1}},
+ {3190, {wxStyledTextCtrl, getWrapStartIndent, 0}},
+ {3191, {wxStyledTextCtrl, setLayoutCache, 1}},
+ {3192, {wxStyledTextCtrl, getLayoutCache, 0}},
+ {3193, {wxStyledTextCtrl, setScrollWidth, 1}},
+ {3194, {wxStyledTextCtrl, getScrollWidth, 0}},
+ {3195, {wxStyledTextCtrl, textWidth, 2}},
+ {3196, {wxStyledTextCtrl, getEndAtLastLine, 0}},
+ {3197, {wxStyledTextCtrl, textHeight, 1}},
+ {3198, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
+ {3199, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
+ {3200, {wxStyledTextCtrl, appendText, 1}},
+ {3201, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
+ {3202, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
+ {3203, {wxStyledTextCtrl, targetFromSelection, 0}},
+ {3204, {wxStyledTextCtrl, linesJoin, 0}},
+ {3205, {wxStyledTextCtrl, linesSplit, 1}},
+ {3206, {wxStyledTextCtrl, setFoldMarginColour, 2}},
+ {3207, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
+ {3208, {wxStyledTextCtrl, lineDown, 0}},
+ {3209, {wxStyledTextCtrl, lineDownExtend, 0}},
+ {3210, {wxStyledTextCtrl, lineUp, 0}},
+ {3211, {wxStyledTextCtrl, lineUpExtend, 0}},
+ {3212, {wxStyledTextCtrl, charLeft, 0}},
+ {3213, {wxStyledTextCtrl, charLeftExtend, 0}},
+ {3214, {wxStyledTextCtrl, charRight, 0}},
+ {3215, {wxStyledTextCtrl, charRightExtend, 0}},
+ {3216, {wxStyledTextCtrl, wordLeft, 0}},
+ {3217, {wxStyledTextCtrl, wordLeftExtend, 0}},
+ {3218, {wxStyledTextCtrl, wordRight, 0}},
+ {3219, {wxStyledTextCtrl, wordRightExtend, 0}},
+ {3220, {wxStyledTextCtrl, home, 0}},
+ {3221, {wxStyledTextCtrl, homeExtend, 0}},
+ {3222, {wxStyledTextCtrl, lineEnd, 0}},
+ {3223, {wxStyledTextCtrl, lineEndExtend, 0}},
+ {3224, {wxStyledTextCtrl, documentStart, 0}},
+ {3225, {wxStyledTextCtrl, documentStartExtend, 0}},
+ {3226, {wxStyledTextCtrl, documentEnd, 0}},
+ {3227, {wxStyledTextCtrl, documentEndExtend, 0}},
+ {3228, {wxStyledTextCtrl, pageUp, 0}},
+ {3229, {wxStyledTextCtrl, pageUpExtend, 0}},
+ {3230, {wxStyledTextCtrl, pageDown, 0}},
+ {3231, {wxStyledTextCtrl, pageDownExtend, 0}},
+ {3232, {wxStyledTextCtrl, editToggleOvertype, 0}},
+ {3233, {wxStyledTextCtrl, cancel, 0}},
+ {3234, {wxStyledTextCtrl, deleteBack, 0}},
+ {3235, {wxStyledTextCtrl, tab, 0}},
+ {3236, {wxStyledTextCtrl, backTab, 0}},
+ {3237, {wxStyledTextCtrl, newLine, 0}},
+ {3238, {wxStyledTextCtrl, formFeed, 0}},
+ {3239, {wxStyledTextCtrl, vCHome, 0}},
+ {3240, {wxStyledTextCtrl, vCHomeExtend, 0}},
+ {3241, {wxStyledTextCtrl, zoomIn, 0}},
+ {3242, {wxStyledTextCtrl, zoomOut, 0}},
+ {3243, {wxStyledTextCtrl, delWordLeft, 0}},
+ {3244, {wxStyledTextCtrl, delWordRight, 0}},
+ {3245, {wxStyledTextCtrl, lineCut, 0}},
+ {3246, {wxStyledTextCtrl, lineDelete, 0}},
+ {3247, {wxStyledTextCtrl, lineTranspose, 0}},
+ {3248, {wxStyledTextCtrl, lineDuplicate, 0}},
+ {3249, {wxStyledTextCtrl, lowerCase, 0}},
+ {3250, {wxStyledTextCtrl, upperCase, 0}},
+ {3251, {wxStyledTextCtrl, lineScrollDown, 0}},
+ {3252, {wxStyledTextCtrl, lineScrollUp, 0}},
+ {3253, {wxStyledTextCtrl, deleteBackNotLine, 0}},
+ {3254, {wxStyledTextCtrl, homeDisplay, 0}},
+ {3255, {wxStyledTextCtrl, homeDisplayExtend, 0}},
+ {3256, {wxStyledTextCtrl, lineEndDisplay, 0}},
+ {3257, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
+ {3258, {wxStyledTextCtrl, homeWrapExtend, 0}},
+ {3259, {wxStyledTextCtrl, lineEndWrap, 0}},
+ {3260, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
+ {3261, {wxStyledTextCtrl, vCHomeWrap, 0}},
+ {3262, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
+ {3263, {wxStyledTextCtrl, lineCopy, 0}},
+ {3264, {wxStyledTextCtrl, moveCaretInsideView, 0}},
+ {3265, {wxStyledTextCtrl, lineLength, 1}},
+ {3266, {wxStyledTextCtrl, braceHighlight, 2}},
+ {3267, {wxStyledTextCtrl, braceBadLight, 1}},
+ {3268, {wxStyledTextCtrl, braceMatch, 1}},
+ {3269, {wxStyledTextCtrl, getViewEOL, 0}},
+ {3270, {wxStyledTextCtrl, setViewEOL, 1}},
+ {3271, {wxStyledTextCtrl, setModEventMask, 1}},
+ {3272, {wxStyledTextCtrl, getEdgeColumn, 0}},
+ {3273, {wxStyledTextCtrl, setEdgeColumn, 1}},
+ {3274, {wxStyledTextCtrl, setEdgeMode, 1}},
+ {3275, {wxStyledTextCtrl, getEdgeMode, 0}},
+ {3276, {wxStyledTextCtrl, getEdgeColour, 0}},
+ {3277, {wxStyledTextCtrl, setEdgeColour, 1}},
+ {3278, {wxStyledTextCtrl, searchAnchor, 0}},
+ {3279, {wxStyledTextCtrl, searchNext, 2}},
+ {3280, {wxStyledTextCtrl, searchPrev, 2}},
+ {3281, {wxStyledTextCtrl, linesOnScreen, 0}},
+ {3282, {wxStyledTextCtrl, usePopUp, 1}},
+ {3283, {wxStyledTextCtrl, selectionIsRectangle, 0}},
+ {3284, {wxStyledTextCtrl, setZoom, 1}},
+ {3285, {wxStyledTextCtrl, getZoom, 0}},
+ {3286, {wxStyledTextCtrl, getModEventMask, 0}},
+ {3287, {wxStyledTextCtrl, setSTCFocus, 1}},
+ {3288, {wxStyledTextCtrl, getSTCFocus, 0}},
+ {3289, {wxStyledTextCtrl, setStatus, 1}},
+ {3290, {wxStyledTextCtrl, getStatus, 0}},
+ {3291, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
+ {3292, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
+ {3293, {wxStyledTextCtrl, setSTCCursor, 1}},
+ {3294, {wxStyledTextCtrl, getSTCCursor, 0}},
+ {3295, {wxStyledTextCtrl, setControlCharSymbol, 1}},
+ {3296, {wxStyledTextCtrl, getControlCharSymbol, 0}},
+ {3297, {wxStyledTextCtrl, wordPartLeft, 0}},
+ {3298, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
+ {3299, {wxStyledTextCtrl, wordPartRight, 0}},
+ {3300, {wxStyledTextCtrl, wordPartRightExtend, 0}},
+ {3301, {wxStyledTextCtrl, setVisiblePolicy, 2}},
+ {3302, {wxStyledTextCtrl, delLineLeft, 0}},
+ {3303, {wxStyledTextCtrl, delLineRight, 0}},
+ {3304, {wxStyledTextCtrl, getXOffset, 0}},
+ {3305, {wxStyledTextCtrl, chooseCaretX, 0}},
+ {3306, {wxStyledTextCtrl, setXCaretPolicy, 2}},
+ {3307, {wxStyledTextCtrl, setYCaretPolicy, 2}},
+ {3308, {wxStyledTextCtrl, getPrintWrapMode, 0}},
+ {3309, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
+ {3310, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
+ {3311, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
+ {3312, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
+ {3313, {wxStyledTextCtrl, paraDownExtend, 0}},
+ {3314, {wxStyledTextCtrl, paraUp, 0}},
+ {3315, {wxStyledTextCtrl, paraUpExtend, 0}},
+ {3316, {wxStyledTextCtrl, positionBefore, 1}},
+ {3317, {wxStyledTextCtrl, positionAfter, 1}},
+ {3318, {wxStyledTextCtrl, copyRange, 2}},
+ {3319, {wxStyledTextCtrl, copyText, 2}},
+ {3320, {wxStyledTextCtrl, setSelectionMode, 1}},
+ {3321, {wxStyledTextCtrl, getSelectionMode, 0}},
+ {3322, {wxStyledTextCtrl, lineDownRectExtend, 0}},
+ {3323, {wxStyledTextCtrl, lineUpRectExtend, 0}},
+ {3324, {wxStyledTextCtrl, charLeftRectExtend, 0}},
+ {3325, {wxStyledTextCtrl, charRightRectExtend, 0}},
+ {3326, {wxStyledTextCtrl, homeRectExtend, 0}},
+ {3327, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
+ {3328, {wxStyledTextCtrl, lineEndRectExtend, 0}},
+ {3329, {wxStyledTextCtrl, pageUpRectExtend, 0}},
+ {3330, {wxStyledTextCtrl, pageDownRectExtend, 0}},
+ {3331, {wxStyledTextCtrl, stutteredPageUp, 0}},
+ {3332, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
+ {3333, {wxStyledTextCtrl, stutteredPageDown, 0}},
+ {3334, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
+ {3335, {wxStyledTextCtrl, wordLeftEnd, 0}},
+ {3336, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
+ {3337, {wxStyledTextCtrl, wordRightEnd, 0}},
+ {3338, {wxStyledTextCtrl, wordRightEndExtend, 0}},
+ {3339, {wxStyledTextCtrl, setWhitespaceChars, 1}},
+ {3340, {wxStyledTextCtrl, setCharsDefault, 0}},
+ {3341, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
+ {3342, {wxStyledTextCtrl, allocate, 1}},
+ {3343, {wxStyledTextCtrl, findColumn, 2}},
+ {3344, {wxStyledTextCtrl, getCaretSticky, 0}},
+ {3345, {wxStyledTextCtrl, setCaretSticky, 1}},
+ {3346, {wxStyledTextCtrl, toggleCaretSticky, 0}},
+ {3347, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
+ {3348, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
+ {3349, {wxStyledTextCtrl, selectionDuplicate, 0}},
+ {3350, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
+ {3351, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
+ {3352, {wxStyledTextCtrl, startRecord, 0}},
+ {3353, {wxStyledTextCtrl, stopRecord, 0}},
+ {3354, {wxStyledTextCtrl, setLexer, 1}},
+ {3355, {wxStyledTextCtrl, getLexer, 0}},
+ {3356, {wxStyledTextCtrl, colourise, 2}},
+ {3357, {wxStyledTextCtrl, setProperty, 2}},
+ {3358, {wxStyledTextCtrl, setKeyWords, 2}},
+ {3359, {wxStyledTextCtrl, setLexerLanguage, 1}},
+ {3360, {wxStyledTextCtrl, getProperty, 1}},
+ {3361, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
+ {3362, {wxStyledTextCtrl, getCurrentLine, 0}},
+ {3363, {wxStyledTextCtrl, styleSetSpec, 2}},
+ {3364, {wxStyledTextCtrl, styleSetFont, 2}},
+ {3365, {wxStyledTextCtrl, styleSetFontAttr, 7}},
+ {3366, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
+ {3367, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
+ {3368, {wxStyledTextCtrl, cmdKeyExecute, 1}},
+ {3369, {wxStyledTextCtrl, setMargins, 2}},
+ {3370, {wxStyledTextCtrl, getSelection, 2}},
+ {3371, {wxStyledTextCtrl, pointFromPosition, 1}},
+ {3372, {wxStyledTextCtrl, scrollToLine, 1}},
+ {3373, {wxStyledTextCtrl, scrollToColumn, 1}},
{3374, {wxStyledTextCtrl, setVScrollBar, 1}},
{3375, {wxStyledTextCtrl, setHScrollBar, 1}},
{3376, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index fe35cb1374..b6e507b11d 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -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
@@ -1881,1277 +1881,1277 @@
-define(wxTreeCtrl_IsSelected, 2058).
-define(wxTreeCtrl_IsVisible, 2059).
-define(wxTreeCtrl_ItemHasChildren, 2060).
--define(wxTreeCtrl_PrependItem, 2061).
--define(wxTreeCtrl_ScrollTo, 2062).
--define(wxTreeCtrl_SelectItem_1, 2063).
--define(wxTreeCtrl_SelectItem_2, 2064).
--define(wxTreeCtrl_SetIndent, 2065).
--define(wxTreeCtrl_SetImageList, 2066).
--define(wxTreeCtrl_SetItemBackgroundColour, 2067).
--define(wxTreeCtrl_SetItemBold, 2068).
--define(wxTreeCtrl_SetItemData, 2069).
--define(wxTreeCtrl_SetItemDropHighlight, 2070).
--define(wxTreeCtrl_SetItemFont, 2071).
--define(wxTreeCtrl_SetItemHasChildren, 2072).
--define(wxTreeCtrl_SetItemImage_2, 2073).
--define(wxTreeCtrl_SetItemImage_3, 2074).
--define(wxTreeCtrl_SetItemText, 2075).
--define(wxTreeCtrl_SetItemTextColour, 2076).
--define(wxTreeCtrl_SetStateImageList, 2077).
--define(wxTreeCtrl_SetWindowStyle, 2078).
--define(wxTreeCtrl_SortChildren, 2079).
--define(wxTreeCtrl_Toggle, 2080).
--define(wxTreeCtrl_ToggleItemSelection, 2081).
--define(wxTreeCtrl_Unselect, 2082).
--define(wxTreeCtrl_UnselectAll, 2083).
--define(wxTreeCtrl_UnselectItem, 2084).
--define(wxScrollBar_new_0, 2085).
--define(wxScrollBar_new_3, 2086).
--define(wxScrollBar_destruct, 2087).
--define(wxScrollBar_Create, 2088).
--define(wxScrollBar_GetRange, 2089).
--define(wxScrollBar_GetPageSize, 2090).
--define(wxScrollBar_GetThumbPosition, 2091).
--define(wxScrollBar_GetThumbSize, 2092).
--define(wxScrollBar_SetThumbPosition, 2093).
--define(wxScrollBar_SetScrollbar, 2094).
--define(wxSpinButton_new_2, 2096).
--define(wxSpinButton_new_0, 2097).
--define(wxSpinButton_Create, 2098).
--define(wxSpinButton_GetMax, 2099).
--define(wxSpinButton_GetMin, 2100).
--define(wxSpinButton_GetValue, 2101).
--define(wxSpinButton_SetRange, 2102).
--define(wxSpinButton_SetValue, 2103).
--define(wxSpinButton_destroy, 2104).
--define(wxSpinCtrl_new_0, 2105).
--define(wxSpinCtrl_new_2, 2106).
--define(wxSpinCtrl_Create, 2108).
--define(wxSpinCtrl_SetValue_1_1, 2111).
--define(wxSpinCtrl_SetValue_1_0, 2112).
--define(wxSpinCtrl_GetValue, 2114).
--define(wxSpinCtrl_SetRange, 2116).
--define(wxSpinCtrl_SetSelection, 2117).
--define(wxSpinCtrl_GetMin, 2119).
--define(wxSpinCtrl_GetMax, 2121).
--define(wxSpinCtrl_destroy, 2122).
--define(wxStaticText_new_0, 2123).
--define(wxStaticText_new_4, 2124).
--define(wxStaticText_Create, 2125).
--define(wxStaticText_GetLabel, 2126).
--define(wxStaticText_SetLabel, 2127).
--define(wxStaticText_Wrap, 2128).
--define(wxStaticText_destroy, 2129).
--define(wxStaticBitmap_new_0, 2130).
--define(wxStaticBitmap_new_4, 2131).
--define(wxStaticBitmap_Create, 2132).
--define(wxStaticBitmap_GetBitmap, 2133).
--define(wxStaticBitmap_SetBitmap, 2134).
--define(wxStaticBitmap_destroy, 2135).
--define(wxRadioBox_new, 2136).
--define(wxRadioBox_destruct, 2138).
--define(wxRadioBox_Create, 2139).
--define(wxRadioBox_Enable_2, 2140).
--define(wxRadioBox_Enable_1, 2141).
--define(wxRadioBox_GetSelection, 2142).
--define(wxRadioBox_GetString, 2143).
--define(wxRadioBox_SetSelection, 2144).
--define(wxRadioBox_Show_2, 2145).
--define(wxRadioBox_Show_1, 2146).
--define(wxRadioBox_GetColumnCount, 2147).
--define(wxRadioBox_GetItemHelpText, 2148).
--define(wxRadioBox_GetItemToolTip, 2149).
--define(wxRadioBox_GetItemFromPoint, 2151).
--define(wxRadioBox_GetRowCount, 2152).
--define(wxRadioBox_IsItemEnabled, 2153).
--define(wxRadioBox_IsItemShown, 2154).
--define(wxRadioBox_SetItemHelpText, 2155).
--define(wxRadioBox_SetItemToolTip, 2156).
--define(wxRadioButton_new_0, 2157).
--define(wxRadioButton_new_4, 2158).
--define(wxRadioButton_Create, 2159).
--define(wxRadioButton_GetValue, 2160).
--define(wxRadioButton_SetValue, 2161).
--define(wxRadioButton_destroy, 2162).
--define(wxSlider_new_6, 2164).
--define(wxSlider_new_0, 2165).
--define(wxSlider_Create, 2166).
--define(wxSlider_GetLineSize, 2167).
--define(wxSlider_GetMax, 2168).
--define(wxSlider_GetMin, 2169).
--define(wxSlider_GetPageSize, 2170).
--define(wxSlider_GetThumbLength, 2171).
--define(wxSlider_GetValue, 2172).
--define(wxSlider_SetLineSize, 2173).
--define(wxSlider_SetPageSize, 2174).
--define(wxSlider_SetRange, 2175).
--define(wxSlider_SetThumbLength, 2176).
--define(wxSlider_SetValue, 2177).
--define(wxSlider_destroy, 2178).
--define(wxDialog_new_4, 2180).
--define(wxDialog_new_0, 2181).
--define(wxDialog_destruct, 2183).
--define(wxDialog_Create, 2184).
--define(wxDialog_CreateButtonSizer, 2185).
--define(wxDialog_CreateStdDialogButtonSizer, 2186).
--define(wxDialog_EndModal, 2187).
--define(wxDialog_GetAffirmativeId, 2188).
--define(wxDialog_GetReturnCode, 2189).
--define(wxDialog_IsModal, 2190).
--define(wxDialog_SetAffirmativeId, 2191).
--define(wxDialog_SetReturnCode, 2192).
--define(wxDialog_Show, 2193).
--define(wxDialog_ShowModal, 2194).
--define(wxColourDialog_new_0, 2195).
--define(wxColourDialog_new_2, 2196).
--define(wxColourDialog_destruct, 2197).
--define(wxColourDialog_Create, 2198).
--define(wxColourDialog_GetColourData, 2199).
--define(wxColourData_new_0, 2200).
--define(wxColourData_new_1, 2201).
--define(wxColourData_destruct, 2202).
--define(wxColourData_GetChooseFull, 2203).
--define(wxColourData_GetColour, 2204).
--define(wxColourData_GetCustomColour, 2206).
--define(wxColourData_SetChooseFull, 2207).
--define(wxColourData_SetColour, 2208).
--define(wxColourData_SetCustomColour, 2209).
--define(wxPalette_new_0, 2210).
--define(wxPalette_new_4, 2211).
--define(wxPalette_destruct, 2213).
--define(wxPalette_Create, 2214).
--define(wxPalette_GetColoursCount, 2215).
--define(wxPalette_GetPixel, 2216).
--define(wxPalette_GetRGB, 2217).
--define(wxPalette_IsOk, 2218).
--define(wxDirDialog_new, 2222).
--define(wxDirDialog_destruct, 2223).
--define(wxDirDialog_GetPath, 2224).
--define(wxDirDialog_GetMessage, 2225).
--define(wxDirDialog_SetMessage, 2226).
--define(wxDirDialog_SetPath, 2227).
--define(wxFileDialog_new, 2231).
--define(wxFileDialog_destruct, 2232).
--define(wxFileDialog_GetDirectory, 2233).
--define(wxFileDialog_GetFilename, 2234).
--define(wxFileDialog_GetFilenames, 2235).
--define(wxFileDialog_GetFilterIndex, 2236).
--define(wxFileDialog_GetMessage, 2237).
--define(wxFileDialog_GetPath, 2238).
--define(wxFileDialog_GetPaths, 2239).
--define(wxFileDialog_GetWildcard, 2240).
--define(wxFileDialog_SetDirectory, 2241).
--define(wxFileDialog_SetFilename, 2242).
--define(wxFileDialog_SetFilterIndex, 2243).
--define(wxFileDialog_SetMessage, 2244).
--define(wxFileDialog_SetPath, 2245).
--define(wxFileDialog_SetWildcard, 2246).
--define(wxPickerBase_SetInternalMargin, 2247).
--define(wxPickerBase_GetInternalMargin, 2248).
--define(wxPickerBase_SetTextCtrlProportion, 2249).
--define(wxPickerBase_SetPickerCtrlProportion, 2250).
--define(wxPickerBase_GetTextCtrlProportion, 2251).
--define(wxPickerBase_GetPickerCtrlProportion, 2252).
--define(wxPickerBase_HasTextCtrl, 2253).
--define(wxPickerBase_GetTextCtrl, 2254).
--define(wxPickerBase_IsTextCtrlGrowable, 2255).
--define(wxPickerBase_SetPickerCtrlGrowable, 2256).
--define(wxPickerBase_SetTextCtrlGrowable, 2257).
--define(wxPickerBase_IsPickerCtrlGrowable, 2258).
--define(wxFilePickerCtrl_new_0, 2259).
--define(wxFilePickerCtrl_new_3, 2260).
--define(wxFilePickerCtrl_Create, 2261).
--define(wxFilePickerCtrl_GetPath, 2262).
--define(wxFilePickerCtrl_SetPath, 2263).
--define(wxFilePickerCtrl_destroy, 2264).
--define(wxDirPickerCtrl_new_0, 2265).
--define(wxDirPickerCtrl_new_3, 2266).
--define(wxDirPickerCtrl_Create, 2267).
--define(wxDirPickerCtrl_GetPath, 2268).
--define(wxDirPickerCtrl_SetPath, 2269).
--define(wxDirPickerCtrl_destroy, 2270).
--define(wxColourPickerCtrl_new_0, 2271).
--define(wxColourPickerCtrl_new_3, 2272).
--define(wxColourPickerCtrl_Create, 2273).
--define(wxColourPickerCtrl_GetColour, 2274).
--define(wxColourPickerCtrl_SetColour_1_1, 2275).
--define(wxColourPickerCtrl_SetColour_1_0, 2276).
--define(wxColourPickerCtrl_destroy, 2277).
--define(wxDatePickerCtrl_new_0, 2278).
--define(wxDatePickerCtrl_new_3, 2279).
--define(wxDatePickerCtrl_GetRange, 2280).
--define(wxDatePickerCtrl_GetValue, 2281).
--define(wxDatePickerCtrl_SetRange, 2282).
--define(wxDatePickerCtrl_SetValue, 2283).
--define(wxDatePickerCtrl_destroy, 2284).
--define(wxFontPickerCtrl_new_0, 2285).
--define(wxFontPickerCtrl_new_3, 2286).
--define(wxFontPickerCtrl_Create, 2287).
--define(wxFontPickerCtrl_GetSelectedFont, 2288).
--define(wxFontPickerCtrl_SetSelectedFont, 2289).
--define(wxFontPickerCtrl_GetMaxPointSize, 2290).
--define(wxFontPickerCtrl_SetMaxPointSize, 2291).
--define(wxFontPickerCtrl_destroy, 2292).
--define(wxFindReplaceDialog_new_0, 2295).
--define(wxFindReplaceDialog_new_4, 2296).
--define(wxFindReplaceDialog_destruct, 2297).
--define(wxFindReplaceDialog_Create, 2298).
--define(wxFindReplaceDialog_GetData, 2299).
--define(wxFindReplaceData_new_0, 2300).
--define(wxFindReplaceData_new_1, 2301).
--define(wxFindReplaceData_GetFindString, 2302).
--define(wxFindReplaceData_GetReplaceString, 2303).
--define(wxFindReplaceData_GetFlags, 2304).
--define(wxFindReplaceData_SetFlags, 2305).
--define(wxFindReplaceData_SetFindString, 2306).
--define(wxFindReplaceData_SetReplaceString, 2307).
--define(wxFindReplaceData_destroy, 2308).
--define(wxMultiChoiceDialog_new_0, 2309).
--define(wxMultiChoiceDialog_new_5, 2311).
--define(wxMultiChoiceDialog_GetSelections, 2312).
--define(wxMultiChoiceDialog_SetSelections, 2313).
--define(wxMultiChoiceDialog_destroy, 2314).
--define(wxSingleChoiceDialog_new_0, 2315).
--define(wxSingleChoiceDialog_new_5, 2317).
--define(wxSingleChoiceDialog_GetSelection, 2318).
--define(wxSingleChoiceDialog_GetStringSelection, 2319).
--define(wxSingleChoiceDialog_SetSelection, 2320).
--define(wxSingleChoiceDialog_destroy, 2321).
--define(wxTextEntryDialog_new, 2322).
--define(wxTextEntryDialog_GetValue, 2323).
--define(wxTextEntryDialog_SetValue, 2324).
--define(wxTextEntryDialog_destroy, 2325).
--define(wxPasswordEntryDialog_new, 2326).
--define(wxPasswordEntryDialog_destroy, 2327).
--define(wxFontData_new_0, 2328).
--define(wxFontData_new_1, 2329).
--define(wxFontData_destruct, 2330).
--define(wxFontData_EnableEffects, 2331).
--define(wxFontData_GetAllowSymbols, 2332).
--define(wxFontData_GetColour, 2333).
--define(wxFontData_GetChosenFont, 2334).
--define(wxFontData_GetEnableEffects, 2335).
--define(wxFontData_GetInitialFont, 2336).
--define(wxFontData_GetShowHelp, 2337).
--define(wxFontData_SetAllowSymbols, 2338).
--define(wxFontData_SetChosenFont, 2339).
--define(wxFontData_SetColour, 2340).
--define(wxFontData_SetInitialFont, 2341).
--define(wxFontData_SetRange, 2342).
--define(wxFontData_SetShowHelp, 2343).
--define(wxFontDialog_new_0, 2347).
--define(wxFontDialog_new_2, 2349).
--define(wxFontDialog_Create, 2351).
--define(wxFontDialog_GetFontData, 2352).
--define(wxFontDialog_destroy, 2354).
--define(wxProgressDialog_new, 2355).
--define(wxProgressDialog_destruct, 2356).
--define(wxProgressDialog_Resume, 2357).
--define(wxProgressDialog_Update_2, 2358).
--define(wxProgressDialog_Update_0, 2359).
--define(wxMessageDialog_new, 2360).
--define(wxMessageDialog_destruct, 2361).
--define(wxPageSetupDialog_new, 2362).
--define(wxPageSetupDialog_destruct, 2363).
--define(wxPageSetupDialog_GetPageSetupData, 2364).
--define(wxPageSetupDialog_ShowModal, 2365).
--define(wxPageSetupDialogData_new_0, 2366).
--define(wxPageSetupDialogData_new_1_0, 2367).
--define(wxPageSetupDialogData_new_1_1, 2368).
--define(wxPageSetupDialogData_destruct, 2369).
--define(wxPageSetupDialogData_EnableHelp, 2370).
--define(wxPageSetupDialogData_EnableMargins, 2371).
--define(wxPageSetupDialogData_EnableOrientation, 2372).
--define(wxPageSetupDialogData_EnablePaper, 2373).
--define(wxPageSetupDialogData_EnablePrinter, 2374).
--define(wxPageSetupDialogData_GetDefaultMinMargins, 2375).
--define(wxPageSetupDialogData_GetEnableMargins, 2376).
--define(wxPageSetupDialogData_GetEnableOrientation, 2377).
--define(wxPageSetupDialogData_GetEnablePaper, 2378).
--define(wxPageSetupDialogData_GetEnablePrinter, 2379).
--define(wxPageSetupDialogData_GetEnableHelp, 2380).
--define(wxPageSetupDialogData_GetDefaultInfo, 2381).
--define(wxPageSetupDialogData_GetMarginTopLeft, 2382).
--define(wxPageSetupDialogData_GetMarginBottomRight, 2383).
--define(wxPageSetupDialogData_GetMinMarginTopLeft, 2384).
--define(wxPageSetupDialogData_GetMinMarginBottomRight, 2385).
--define(wxPageSetupDialogData_GetPaperId, 2386).
--define(wxPageSetupDialogData_GetPaperSize, 2387).
--define(wxPageSetupDialogData_GetPrintData, 2389).
--define(wxPageSetupDialogData_IsOk, 2390).
--define(wxPageSetupDialogData_SetDefaultInfo, 2391).
--define(wxPageSetupDialogData_SetDefaultMinMargins, 2392).
--define(wxPageSetupDialogData_SetMarginTopLeft, 2393).
--define(wxPageSetupDialogData_SetMarginBottomRight, 2394).
--define(wxPageSetupDialogData_SetMinMarginTopLeft, 2395).
--define(wxPageSetupDialogData_SetMinMarginBottomRight, 2396).
--define(wxPageSetupDialogData_SetPaperId, 2397).
--define(wxPageSetupDialogData_SetPaperSize_1_1, 2398).
--define(wxPageSetupDialogData_SetPaperSize_1_0, 2399).
--define(wxPageSetupDialogData_SetPrintData, 2400).
--define(wxPrintDialog_new_2_0, 2401).
--define(wxPrintDialog_new_2_1, 2402).
--define(wxPrintDialog_destruct, 2403).
--define(wxPrintDialog_GetPrintDialogData, 2404).
--define(wxPrintDialog_GetPrintDC, 2405).
--define(wxPrintDialogData_new_0, 2406).
--define(wxPrintDialogData_new_1_1, 2407).
--define(wxPrintDialogData_new_1_0, 2408).
--define(wxPrintDialogData_destruct, 2409).
--define(wxPrintDialogData_EnableHelp, 2410).
--define(wxPrintDialogData_EnablePageNumbers, 2411).
--define(wxPrintDialogData_EnablePrintToFile, 2412).
--define(wxPrintDialogData_EnableSelection, 2413).
--define(wxPrintDialogData_GetAllPages, 2414).
--define(wxPrintDialogData_GetCollate, 2415).
--define(wxPrintDialogData_GetFromPage, 2416).
--define(wxPrintDialogData_GetMaxPage, 2417).
--define(wxPrintDialogData_GetMinPage, 2418).
--define(wxPrintDialogData_GetNoCopies, 2419).
--define(wxPrintDialogData_GetPrintData, 2420).
--define(wxPrintDialogData_GetPrintToFile, 2421).
--define(wxPrintDialogData_GetSelection, 2422).
--define(wxPrintDialogData_GetToPage, 2423).
--define(wxPrintDialogData_IsOk, 2424).
--define(wxPrintDialogData_SetCollate, 2425).
--define(wxPrintDialogData_SetFromPage, 2426).
--define(wxPrintDialogData_SetMaxPage, 2427).
--define(wxPrintDialogData_SetMinPage, 2428).
--define(wxPrintDialogData_SetNoCopies, 2429).
--define(wxPrintDialogData_SetPrintData, 2430).
--define(wxPrintDialogData_SetPrintToFile, 2431).
--define(wxPrintDialogData_SetSelection, 2432).
--define(wxPrintDialogData_SetToPage, 2433).
--define(wxPrintData_new_0, 2434).
--define(wxPrintData_new_1, 2435).
--define(wxPrintData_destruct, 2436).
--define(wxPrintData_GetCollate, 2437).
--define(wxPrintData_GetBin, 2438).
--define(wxPrintData_GetColour, 2439).
--define(wxPrintData_GetDuplex, 2440).
--define(wxPrintData_GetNoCopies, 2441).
--define(wxPrintData_GetOrientation, 2442).
--define(wxPrintData_GetPaperId, 2443).
--define(wxPrintData_GetPrinterName, 2444).
--define(wxPrintData_GetQuality, 2445).
--define(wxPrintData_IsOk, 2446).
--define(wxPrintData_SetBin, 2447).
--define(wxPrintData_SetCollate, 2448).
--define(wxPrintData_SetColour, 2449).
--define(wxPrintData_SetDuplex, 2450).
--define(wxPrintData_SetNoCopies, 2451).
--define(wxPrintData_SetOrientation, 2452).
--define(wxPrintData_SetPaperId, 2453).
--define(wxPrintData_SetPrinterName, 2454).
--define(wxPrintData_SetQuality, 2455).
--define(wxPrintPreview_new_2, 2458).
--define(wxPrintPreview_new_3, 2459).
--define(wxPrintPreview_destruct, 2461).
--define(wxPrintPreview_GetCanvas, 2462).
--define(wxPrintPreview_GetCurrentPage, 2463).
--define(wxPrintPreview_GetFrame, 2464).
--define(wxPrintPreview_GetMaxPage, 2465).
--define(wxPrintPreview_GetMinPage, 2466).
--define(wxPrintPreview_GetPrintout, 2467).
--define(wxPrintPreview_GetPrintoutForPrinting, 2468).
--define(wxPrintPreview_IsOk, 2469).
--define(wxPrintPreview_PaintPage, 2470).
--define(wxPrintPreview_Print, 2471).
--define(wxPrintPreview_RenderPage, 2472).
--define(wxPrintPreview_SetCanvas, 2473).
--define(wxPrintPreview_SetCurrentPage, 2474).
--define(wxPrintPreview_SetFrame, 2475).
--define(wxPrintPreview_SetPrintout, 2476).
--define(wxPrintPreview_SetZoom, 2477).
--define(wxPreviewFrame_new, 2478).
--define(wxPreviewFrame_destruct, 2479).
--define(wxPreviewFrame_CreateControlBar, 2480).
--define(wxPreviewFrame_CreateCanvas, 2481).
--define(wxPreviewFrame_Initialize, 2482).
--define(wxPreviewFrame_OnCloseWindow, 2483).
--define(wxPreviewControlBar_new, 2484).
--define(wxPreviewControlBar_destruct, 2485).
--define(wxPreviewControlBar_CreateButtons, 2486).
--define(wxPreviewControlBar_GetPrintPreview, 2487).
--define(wxPreviewControlBar_GetZoomControl, 2488).
--define(wxPreviewControlBar_SetZoomControl, 2489).
--define(wxPrinter_new, 2491).
--define(wxPrinter_CreateAbortWindow, 2492).
--define(wxPrinter_GetAbort, 2493).
--define(wxPrinter_GetLastError, 2494).
--define(wxPrinter_GetPrintDialogData, 2495).
--define(wxPrinter_Print, 2496).
--define(wxPrinter_PrintDialog, 2497).
--define(wxPrinter_ReportError, 2498).
--define(wxPrinter_Setup, 2499).
--define(wxPrinter_destroy, 2500).
--define(wxXmlResource_new_1, 2501).
--define(wxXmlResource_new_2, 2502).
--define(wxXmlResource_destruct, 2503).
--define(wxXmlResource_AttachUnknownControl, 2504).
--define(wxXmlResource_ClearHandlers, 2505).
--define(wxXmlResource_CompareVersion, 2506).
--define(wxXmlResource_Get, 2507).
--define(wxXmlResource_GetFlags, 2508).
--define(wxXmlResource_GetVersion, 2509).
--define(wxXmlResource_GetXRCID, 2510).
--define(wxXmlResource_InitAllHandlers, 2511).
--define(wxXmlResource_Load, 2512).
--define(wxXmlResource_LoadBitmap, 2513).
--define(wxXmlResource_LoadDialog_2, 2514).
--define(wxXmlResource_LoadDialog_3, 2515).
--define(wxXmlResource_LoadFrame_2, 2516).
--define(wxXmlResource_LoadFrame_3, 2517).
--define(wxXmlResource_LoadIcon, 2518).
--define(wxXmlResource_LoadMenu, 2519).
--define(wxXmlResource_LoadMenuBar_2, 2520).
--define(wxXmlResource_LoadMenuBar_1, 2521).
--define(wxXmlResource_LoadPanel_2, 2522).
--define(wxXmlResource_LoadPanel_3, 2523).
--define(wxXmlResource_LoadToolBar, 2524).
--define(wxXmlResource_Set, 2525).
--define(wxXmlResource_SetFlags, 2526).
--define(wxXmlResource_Unload, 2527).
--define(wxXmlResource_xrcctrl, 2528).
--define(wxHtmlEasyPrinting_new, 2529).
--define(wxHtmlEasyPrinting_destruct, 2530).
--define(wxHtmlEasyPrinting_GetPrintData, 2531).
--define(wxHtmlEasyPrinting_GetPageSetupData, 2532).
--define(wxHtmlEasyPrinting_PreviewFile, 2533).
--define(wxHtmlEasyPrinting_PreviewText, 2534).
--define(wxHtmlEasyPrinting_PrintFile, 2535).
--define(wxHtmlEasyPrinting_PrintText, 2536).
--define(wxHtmlEasyPrinting_PageSetup, 2537).
--define(wxHtmlEasyPrinting_SetFonts, 2538).
--define(wxHtmlEasyPrinting_SetHeader, 2539).
--define(wxHtmlEasyPrinting_SetFooter, 2540).
--define(wxGLCanvas_new_2, 2542).
--define(wxGLCanvas_new_3_1, 2543).
--define(wxGLCanvas_new_3_0, 2544).
--define(wxGLCanvas_GetContext, 2545).
--define(wxGLCanvas_SetCurrent, 2547).
--define(wxGLCanvas_SwapBuffers, 2548).
--define(wxGLCanvas_destroy, 2549).
--define(wxAuiManager_new, 2550).
--define(wxAuiManager_destruct, 2551).
--define(wxAuiManager_AddPane_2_1, 2552).
--define(wxAuiManager_AddPane_3, 2553).
--define(wxAuiManager_AddPane_2_0, 2554).
--define(wxAuiManager_DetachPane, 2555).
--define(wxAuiManager_GetAllPanes, 2556).
--define(wxAuiManager_GetArtProvider, 2557).
--define(wxAuiManager_GetDockSizeConstraint, 2558).
--define(wxAuiManager_GetFlags, 2559).
--define(wxAuiManager_GetManagedWindow, 2560).
--define(wxAuiManager_GetManager, 2561).
--define(wxAuiManager_GetPane_1_1, 2562).
--define(wxAuiManager_GetPane_1_0, 2563).
--define(wxAuiManager_HideHint, 2564).
--define(wxAuiManager_InsertPane, 2565).
--define(wxAuiManager_LoadPaneInfo, 2566).
--define(wxAuiManager_LoadPerspective, 2567).
--define(wxAuiManager_SavePaneInfo, 2568).
--define(wxAuiManager_SavePerspective, 2569).
--define(wxAuiManager_SetArtProvider, 2570).
--define(wxAuiManager_SetDockSizeConstraint, 2571).
--define(wxAuiManager_SetFlags, 2572).
--define(wxAuiManager_SetManagedWindow, 2573).
--define(wxAuiManager_ShowHint, 2574).
--define(wxAuiManager_UnInit, 2575).
--define(wxAuiManager_Update, 2576).
--define(wxAuiPaneInfo_new_0, 2577).
--define(wxAuiPaneInfo_new_1, 2578).
--define(wxAuiPaneInfo_destruct, 2579).
--define(wxAuiPaneInfo_BestSize_1, 2580).
--define(wxAuiPaneInfo_BestSize_2, 2581).
--define(wxAuiPaneInfo_Bottom, 2582).
--define(wxAuiPaneInfo_BottomDockable, 2583).
--define(wxAuiPaneInfo_Caption, 2584).
--define(wxAuiPaneInfo_CaptionVisible, 2585).
--define(wxAuiPaneInfo_Centre, 2586).
--define(wxAuiPaneInfo_CentrePane, 2587).
--define(wxAuiPaneInfo_CloseButton, 2588).
--define(wxAuiPaneInfo_DefaultPane, 2589).
--define(wxAuiPaneInfo_DestroyOnClose, 2590).
--define(wxAuiPaneInfo_Direction, 2591).
--define(wxAuiPaneInfo_Dock, 2592).
--define(wxAuiPaneInfo_Dockable, 2593).
--define(wxAuiPaneInfo_Fixed, 2594).
--define(wxAuiPaneInfo_Float, 2595).
--define(wxAuiPaneInfo_Floatable, 2596).
--define(wxAuiPaneInfo_FloatingPosition_1, 2597).
--define(wxAuiPaneInfo_FloatingPosition_2, 2598).
--define(wxAuiPaneInfo_FloatingSize_1, 2599).
--define(wxAuiPaneInfo_FloatingSize_2, 2600).
--define(wxAuiPaneInfo_Gripper, 2601).
--define(wxAuiPaneInfo_GripperTop, 2602).
--define(wxAuiPaneInfo_HasBorder, 2603).
--define(wxAuiPaneInfo_HasCaption, 2604).
--define(wxAuiPaneInfo_HasCloseButton, 2605).
--define(wxAuiPaneInfo_HasFlag, 2606).
--define(wxAuiPaneInfo_HasGripper, 2607).
--define(wxAuiPaneInfo_HasGripperTop, 2608).
--define(wxAuiPaneInfo_HasMaximizeButton, 2609).
--define(wxAuiPaneInfo_HasMinimizeButton, 2610).
--define(wxAuiPaneInfo_HasPinButton, 2611).
--define(wxAuiPaneInfo_Hide, 2612).
--define(wxAuiPaneInfo_IsBottomDockable, 2613).
--define(wxAuiPaneInfo_IsDocked, 2614).
--define(wxAuiPaneInfo_IsFixed, 2615).
--define(wxAuiPaneInfo_IsFloatable, 2616).
--define(wxAuiPaneInfo_IsFloating, 2617).
--define(wxAuiPaneInfo_IsLeftDockable, 2618).
--define(wxAuiPaneInfo_IsMovable, 2619).
--define(wxAuiPaneInfo_IsOk, 2620).
--define(wxAuiPaneInfo_IsResizable, 2621).
--define(wxAuiPaneInfo_IsRightDockable, 2622).
--define(wxAuiPaneInfo_IsShown, 2623).
--define(wxAuiPaneInfo_IsToolbar, 2624).
--define(wxAuiPaneInfo_IsTopDockable, 2625).
--define(wxAuiPaneInfo_Layer, 2626).
--define(wxAuiPaneInfo_Left, 2627).
--define(wxAuiPaneInfo_LeftDockable, 2628).
--define(wxAuiPaneInfo_MaxSize_1, 2629).
--define(wxAuiPaneInfo_MaxSize_2, 2630).
--define(wxAuiPaneInfo_MaximizeButton, 2631).
--define(wxAuiPaneInfo_MinSize_1, 2632).
--define(wxAuiPaneInfo_MinSize_2, 2633).
--define(wxAuiPaneInfo_MinimizeButton, 2634).
--define(wxAuiPaneInfo_Movable, 2635).
--define(wxAuiPaneInfo_Name, 2636).
--define(wxAuiPaneInfo_PaneBorder, 2637).
--define(wxAuiPaneInfo_PinButton, 2638).
--define(wxAuiPaneInfo_Position, 2639).
--define(wxAuiPaneInfo_Resizable, 2640).
--define(wxAuiPaneInfo_Right, 2641).
--define(wxAuiPaneInfo_RightDockable, 2642).
--define(wxAuiPaneInfo_Row, 2643).
--define(wxAuiPaneInfo_SafeSet, 2644).
--define(wxAuiPaneInfo_SetFlag, 2645).
--define(wxAuiPaneInfo_Show, 2646).
--define(wxAuiPaneInfo_ToolbarPane, 2647).
--define(wxAuiPaneInfo_Top, 2648).
--define(wxAuiPaneInfo_TopDockable, 2649).
--define(wxAuiPaneInfo_Window, 2650).
--define(wxAuiNotebook_new_0, 2651).
--define(wxAuiNotebook_new_2, 2652).
--define(wxAuiNotebook_AddPage, 2653).
--define(wxAuiNotebook_Create, 2654).
--define(wxAuiNotebook_DeletePage, 2655).
--define(wxAuiNotebook_GetArtProvider, 2656).
--define(wxAuiNotebook_GetPage, 2657).
--define(wxAuiNotebook_GetPageBitmap, 2658).
--define(wxAuiNotebook_GetPageCount, 2659).
--define(wxAuiNotebook_GetPageIndex, 2660).
--define(wxAuiNotebook_GetPageText, 2661).
--define(wxAuiNotebook_GetSelection, 2662).
--define(wxAuiNotebook_InsertPage, 2663).
--define(wxAuiNotebook_RemovePage, 2664).
--define(wxAuiNotebook_SetArtProvider, 2665).
--define(wxAuiNotebook_SetFont, 2666).
--define(wxAuiNotebook_SetPageBitmap, 2667).
--define(wxAuiNotebook_SetPageText, 2668).
--define(wxAuiNotebook_SetSelection, 2669).
--define(wxAuiNotebook_SetTabCtrlHeight, 2670).
--define(wxAuiNotebook_SetUniformBitmapSize, 2671).
--define(wxAuiNotebook_destroy, 2672).
--define(wxMDIParentFrame_new_0, 2673).
--define(wxMDIParentFrame_new_4, 2674).
--define(wxMDIParentFrame_destruct, 2675).
--define(wxMDIParentFrame_ActivateNext, 2676).
--define(wxMDIParentFrame_ActivatePrevious, 2677).
--define(wxMDIParentFrame_ArrangeIcons, 2678).
--define(wxMDIParentFrame_Cascade, 2679).
--define(wxMDIParentFrame_Create, 2680).
--define(wxMDIParentFrame_GetActiveChild, 2681).
--define(wxMDIParentFrame_GetClientWindow, 2682).
--define(wxMDIParentFrame_Tile, 2683).
--define(wxMDIChildFrame_new_0, 2684).
--define(wxMDIChildFrame_new_4, 2685).
--define(wxMDIChildFrame_destruct, 2686).
--define(wxMDIChildFrame_Activate, 2687).
--define(wxMDIChildFrame_Create, 2688).
--define(wxMDIChildFrame_Maximize, 2689).
--define(wxMDIChildFrame_Restore, 2690).
--define(wxMDIClientWindow_new_0, 2691).
--define(wxMDIClientWindow_new_2, 2692).
--define(wxMDIClientWindow_destruct, 2693).
--define(wxMDIClientWindow_CreateClient, 2694).
--define(wxLayoutAlgorithm_new, 2695).
--define(wxLayoutAlgorithm_LayoutFrame, 2696).
--define(wxLayoutAlgorithm_LayoutMDIFrame, 2697).
--define(wxLayoutAlgorithm_LayoutWindow, 2698).
--define(wxLayoutAlgorithm_destroy, 2699).
--define(wxEvent_GetId, 2700).
--define(wxEvent_GetSkipped, 2701).
--define(wxEvent_GetTimestamp, 2702).
--define(wxEvent_IsCommandEvent, 2703).
--define(wxEvent_ResumePropagation, 2704).
--define(wxEvent_ShouldPropagate, 2705).
--define(wxEvent_Skip, 2706).
--define(wxEvent_StopPropagation, 2707).
--define(wxCommandEvent_getClientData, 2708).
--define(wxCommandEvent_GetExtraLong, 2709).
--define(wxCommandEvent_GetInt, 2710).
--define(wxCommandEvent_GetSelection, 2711).
--define(wxCommandEvent_GetString, 2712).
--define(wxCommandEvent_IsChecked, 2713).
--define(wxCommandEvent_IsSelection, 2714).
--define(wxCommandEvent_SetInt, 2715).
--define(wxCommandEvent_SetString, 2716).
--define(wxScrollEvent_GetOrientation, 2717).
--define(wxScrollEvent_GetPosition, 2718).
--define(wxScrollWinEvent_GetOrientation, 2719).
--define(wxScrollWinEvent_GetPosition, 2720).
--define(wxMouseEvent_AltDown, 2721).
--define(wxMouseEvent_Button, 2722).
--define(wxMouseEvent_ButtonDClick, 2723).
--define(wxMouseEvent_ButtonDown, 2724).
--define(wxMouseEvent_ButtonUp, 2725).
--define(wxMouseEvent_CmdDown, 2726).
--define(wxMouseEvent_ControlDown, 2727).
--define(wxMouseEvent_Dragging, 2728).
--define(wxMouseEvent_Entering, 2729).
--define(wxMouseEvent_GetButton, 2730).
--define(wxMouseEvent_GetPosition, 2733).
--define(wxMouseEvent_GetLogicalPosition, 2734).
--define(wxMouseEvent_GetLinesPerAction, 2735).
--define(wxMouseEvent_GetWheelRotation, 2736).
--define(wxMouseEvent_GetWheelDelta, 2737).
--define(wxMouseEvent_GetX, 2738).
--define(wxMouseEvent_GetY, 2739).
--define(wxMouseEvent_IsButton, 2740).
--define(wxMouseEvent_IsPageScroll, 2741).
--define(wxMouseEvent_Leaving, 2742).
--define(wxMouseEvent_LeftDClick, 2743).
--define(wxMouseEvent_LeftDown, 2744).
--define(wxMouseEvent_LeftIsDown, 2745).
--define(wxMouseEvent_LeftUp, 2746).
--define(wxMouseEvent_MetaDown, 2747).
--define(wxMouseEvent_MiddleDClick, 2748).
--define(wxMouseEvent_MiddleDown, 2749).
--define(wxMouseEvent_MiddleIsDown, 2750).
--define(wxMouseEvent_MiddleUp, 2751).
--define(wxMouseEvent_Moving, 2752).
--define(wxMouseEvent_RightDClick, 2753).
--define(wxMouseEvent_RightDown, 2754).
--define(wxMouseEvent_RightIsDown, 2755).
--define(wxMouseEvent_RightUp, 2756).
--define(wxMouseEvent_ShiftDown, 2757).
--define(wxSetCursorEvent_GetCursor, 2758).
--define(wxSetCursorEvent_GetX, 2759).
--define(wxSetCursorEvent_GetY, 2760).
--define(wxSetCursorEvent_HasCursor, 2761).
--define(wxSetCursorEvent_SetCursor, 2762).
--define(wxKeyEvent_AltDown, 2763).
--define(wxKeyEvent_CmdDown, 2764).
--define(wxKeyEvent_ControlDown, 2765).
--define(wxKeyEvent_GetKeyCode, 2766).
--define(wxKeyEvent_GetModifiers, 2767).
--define(wxKeyEvent_GetPosition, 2770).
--define(wxKeyEvent_GetRawKeyCode, 2771).
--define(wxKeyEvent_GetRawKeyFlags, 2772).
--define(wxKeyEvent_GetUnicodeKey, 2773).
--define(wxKeyEvent_GetX, 2774).
--define(wxKeyEvent_GetY, 2775).
--define(wxKeyEvent_HasModifiers, 2776).
--define(wxKeyEvent_MetaDown, 2777).
--define(wxKeyEvent_ShiftDown, 2778).
--define(wxSizeEvent_GetSize, 2779).
--define(wxMoveEvent_GetPosition, 2780).
--define(wxEraseEvent_GetDC, 2781).
--define(wxFocusEvent_GetWindow, 2782).
--define(wxChildFocusEvent_GetWindow, 2783).
--define(wxMenuEvent_GetMenu, 2784).
--define(wxMenuEvent_GetMenuId, 2785).
--define(wxMenuEvent_IsPopup, 2786).
--define(wxCloseEvent_CanVeto, 2787).
--define(wxCloseEvent_GetLoggingOff, 2788).
--define(wxCloseEvent_SetCanVeto, 2789).
--define(wxCloseEvent_SetLoggingOff, 2790).
--define(wxCloseEvent_Veto, 2791).
--define(wxShowEvent_SetShow, 2792).
--define(wxShowEvent_GetShow, 2793).
--define(wxIconizeEvent_Iconized, 2794).
--define(wxJoystickEvent_ButtonDown, 2795).
--define(wxJoystickEvent_ButtonIsDown, 2796).
--define(wxJoystickEvent_ButtonUp, 2797).
--define(wxJoystickEvent_GetButtonChange, 2798).
--define(wxJoystickEvent_GetButtonState, 2799).
--define(wxJoystickEvent_GetJoystick, 2800).
--define(wxJoystickEvent_GetPosition, 2801).
--define(wxJoystickEvent_GetZPosition, 2802).
--define(wxJoystickEvent_IsButton, 2803).
--define(wxJoystickEvent_IsMove, 2804).
--define(wxJoystickEvent_IsZMove, 2805).
--define(wxUpdateUIEvent_CanUpdate, 2806).
--define(wxUpdateUIEvent_Check, 2807).
--define(wxUpdateUIEvent_Enable, 2808).
--define(wxUpdateUIEvent_Show, 2809).
--define(wxUpdateUIEvent_GetChecked, 2810).
--define(wxUpdateUIEvent_GetEnabled, 2811).
--define(wxUpdateUIEvent_GetShown, 2812).
--define(wxUpdateUIEvent_GetSetChecked, 2813).
--define(wxUpdateUIEvent_GetSetEnabled, 2814).
--define(wxUpdateUIEvent_GetSetShown, 2815).
--define(wxUpdateUIEvent_GetSetText, 2816).
--define(wxUpdateUIEvent_GetText, 2817).
--define(wxUpdateUIEvent_GetMode, 2818).
--define(wxUpdateUIEvent_GetUpdateInterval, 2819).
--define(wxUpdateUIEvent_ResetUpdateTime, 2820).
--define(wxUpdateUIEvent_SetMode, 2821).
--define(wxUpdateUIEvent_SetText, 2822).
--define(wxUpdateUIEvent_SetUpdateInterval, 2823).
--define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2824).
--define(wxPaletteChangedEvent_SetChangedWindow, 2825).
--define(wxPaletteChangedEvent_GetChangedWindow, 2826).
--define(wxQueryNewPaletteEvent_SetPaletteRealized, 2827).
--define(wxQueryNewPaletteEvent_GetPaletteRealized, 2828).
--define(wxNavigationKeyEvent_GetDirection, 2829).
--define(wxNavigationKeyEvent_SetDirection, 2830).
--define(wxNavigationKeyEvent_IsWindowChange, 2831).
--define(wxNavigationKeyEvent_SetWindowChange, 2832).
--define(wxNavigationKeyEvent_IsFromTab, 2833).
--define(wxNavigationKeyEvent_SetFromTab, 2834).
--define(wxNavigationKeyEvent_GetCurrentFocus, 2835).
--define(wxNavigationKeyEvent_SetCurrentFocus, 2836).
--define(wxHelpEvent_GetOrigin, 2837).
--define(wxHelpEvent_GetPosition, 2838).
--define(wxHelpEvent_SetOrigin, 2839).
--define(wxHelpEvent_SetPosition, 2840).
--define(wxContextMenuEvent_GetPosition, 2841).
--define(wxContextMenuEvent_SetPosition, 2842).
--define(wxIdleEvent_CanSend, 2843).
--define(wxIdleEvent_GetMode, 2844).
--define(wxIdleEvent_RequestMore, 2845).
--define(wxIdleEvent_MoreRequested, 2846).
--define(wxIdleEvent_SetMode, 2847).
--define(wxGridEvent_AltDown, 2848).
--define(wxGridEvent_ControlDown, 2849).
--define(wxGridEvent_GetCol, 2850).
--define(wxGridEvent_GetPosition, 2851).
--define(wxGridEvent_GetRow, 2852).
--define(wxGridEvent_MetaDown, 2853).
--define(wxGridEvent_Selecting, 2854).
--define(wxGridEvent_ShiftDown, 2855).
--define(wxNotifyEvent_Allow, 2856).
--define(wxNotifyEvent_IsAllowed, 2857).
--define(wxNotifyEvent_Veto, 2858).
--define(wxSashEvent_GetEdge, 2859).
--define(wxSashEvent_GetDragRect, 2860).
--define(wxSashEvent_GetDragStatus, 2861).
--define(wxListEvent_GetCacheFrom, 2862).
--define(wxListEvent_GetCacheTo, 2863).
--define(wxListEvent_GetKeyCode, 2864).
--define(wxListEvent_GetIndex, 2865).
--define(wxListEvent_GetColumn, 2866).
--define(wxListEvent_GetPoint, 2867).
--define(wxListEvent_GetLabel, 2868).
--define(wxListEvent_GetText, 2869).
--define(wxListEvent_GetImage, 2870).
--define(wxListEvent_GetData, 2871).
--define(wxListEvent_GetMask, 2872).
--define(wxListEvent_GetItem, 2873).
--define(wxListEvent_IsEditCancelled, 2874).
--define(wxDateEvent_GetDate, 2875).
--define(wxCalendarEvent_GetWeekDay, 2876).
--define(wxFileDirPickerEvent_GetPath, 2877).
--define(wxColourPickerEvent_GetColour, 2878).
--define(wxFontPickerEvent_GetFont, 2879).
--define(wxStyledTextEvent_GetPosition, 2880).
--define(wxStyledTextEvent_GetKey, 2881).
--define(wxStyledTextEvent_GetModifiers, 2882).
--define(wxStyledTextEvent_GetModificationType, 2883).
--define(wxStyledTextEvent_GetText, 2884).
--define(wxStyledTextEvent_GetLength, 2885).
--define(wxStyledTextEvent_GetLinesAdded, 2886).
--define(wxStyledTextEvent_GetLine, 2887).
--define(wxStyledTextEvent_GetFoldLevelNow, 2888).
--define(wxStyledTextEvent_GetFoldLevelPrev, 2889).
--define(wxStyledTextEvent_GetMargin, 2890).
--define(wxStyledTextEvent_GetMessage, 2891).
--define(wxStyledTextEvent_GetWParam, 2892).
--define(wxStyledTextEvent_GetLParam, 2893).
--define(wxStyledTextEvent_GetListType, 2894).
--define(wxStyledTextEvent_GetX, 2895).
--define(wxStyledTextEvent_GetY, 2896).
--define(wxStyledTextEvent_GetDragText, 2897).
--define(wxStyledTextEvent_GetDragAllowMove, 2898).
--define(wxStyledTextEvent_GetDragResult, 2899).
--define(wxStyledTextEvent_GetShift, 2900).
--define(wxStyledTextEvent_GetControl, 2901).
--define(wxStyledTextEvent_GetAlt, 2902).
--define(utils_wxGetKeyState, 2903).
--define(utils_wxGetMousePosition, 2904).
--define(utils_wxGetMouseState, 2905).
--define(utils_wxSetDetectableAutoRepeat, 2906).
--define(utils_wxBell, 2907).
--define(utils_wxFindMenuItemId, 2908).
--define(utils_wxGenericFindWindowAtPoint, 2909).
--define(utils_wxFindWindowAtPoint, 2910).
--define(utils_wxBeginBusyCursor, 2911).
--define(utils_wxEndBusyCursor, 2912).
--define(utils_wxIsBusy, 2913).
--define(utils_wxShutdown, 2914).
--define(utils_wxShell, 2915).
--define(utils_wxLaunchDefaultBrowser, 2916).
--define(utils_wxGetEmailAddress, 2917).
--define(utils_wxGetUserId, 2918).
--define(utils_wxGetHomeDir, 2919).
--define(utils_wxNewId, 2920).
--define(utils_wxRegisterId, 2921).
--define(utils_wxGetCurrentId, 2922).
--define(utils_wxGetOsDescription, 2923).
--define(utils_wxIsPlatformLittleEndian, 2924).
--define(utils_wxIsPlatform64Bit, 2925).
--define(wxPrintout_new, 2926).
--define(wxPrintout_destruct, 2927).
--define(wxPrintout_GetDC, 2928).
--define(wxPrintout_GetPageSizeMM, 2929).
--define(wxPrintout_GetPageSizePixels, 2930).
--define(wxPrintout_GetPaperRectPixels, 2931).
--define(wxPrintout_GetPPIPrinter, 2932).
--define(wxPrintout_GetPPIScreen, 2933).
--define(wxPrintout_GetTitle, 2934).
--define(wxPrintout_IsPreview, 2935).
--define(wxPrintout_FitThisSizeToPaper, 2936).
--define(wxPrintout_FitThisSizeToPage, 2937).
--define(wxPrintout_FitThisSizeToPageMargins, 2938).
--define(wxPrintout_MapScreenSizeToPaper, 2939).
--define(wxPrintout_MapScreenSizeToPage, 2940).
--define(wxPrintout_MapScreenSizeToPageMargins, 2941).
--define(wxPrintout_MapScreenSizeToDevice, 2942).
--define(wxPrintout_GetLogicalPaperRect, 2943).
--define(wxPrintout_GetLogicalPageRect, 2944).
--define(wxPrintout_GetLogicalPageMarginsRect, 2945).
--define(wxPrintout_SetLogicalOrigin, 2946).
--define(wxPrintout_OffsetLogicalOrigin, 2947).
--define(wxStyledTextCtrl_new_2, 2948).
--define(wxStyledTextCtrl_new_0, 2949).
--define(wxStyledTextCtrl_destruct, 2950).
--define(wxStyledTextCtrl_Create, 2951).
--define(wxStyledTextCtrl_AddText, 2952).
--define(wxStyledTextCtrl_AddStyledText, 2953).
--define(wxStyledTextCtrl_InsertText, 2954).
--define(wxStyledTextCtrl_ClearAll, 2955).
--define(wxStyledTextCtrl_ClearDocumentStyle, 2956).
--define(wxStyledTextCtrl_GetLength, 2957).
--define(wxStyledTextCtrl_GetCharAt, 2958).
--define(wxStyledTextCtrl_GetCurrentPos, 2959).
--define(wxStyledTextCtrl_GetAnchor, 2960).
--define(wxStyledTextCtrl_GetStyleAt, 2961).
--define(wxStyledTextCtrl_Redo, 2962).
--define(wxStyledTextCtrl_SetUndoCollection, 2963).
--define(wxStyledTextCtrl_SelectAll, 2964).
--define(wxStyledTextCtrl_SetSavePoint, 2965).
--define(wxStyledTextCtrl_GetStyledText, 2966).
--define(wxStyledTextCtrl_CanRedo, 2967).
--define(wxStyledTextCtrl_MarkerLineFromHandle, 2968).
--define(wxStyledTextCtrl_MarkerDeleteHandle, 2969).
--define(wxStyledTextCtrl_GetUndoCollection, 2970).
--define(wxStyledTextCtrl_GetViewWhiteSpace, 2971).
--define(wxStyledTextCtrl_SetViewWhiteSpace, 2972).
--define(wxStyledTextCtrl_PositionFromPoint, 2973).
--define(wxStyledTextCtrl_PositionFromPointClose, 2974).
--define(wxStyledTextCtrl_GotoLine, 2975).
--define(wxStyledTextCtrl_GotoPos, 2976).
--define(wxStyledTextCtrl_SetAnchor, 2977).
--define(wxStyledTextCtrl_GetCurLine, 2978).
--define(wxStyledTextCtrl_GetEndStyled, 2979).
--define(wxStyledTextCtrl_ConvertEOLs, 2980).
--define(wxStyledTextCtrl_GetEOLMode, 2981).
--define(wxStyledTextCtrl_SetEOLMode, 2982).
--define(wxStyledTextCtrl_StartStyling, 2983).
--define(wxStyledTextCtrl_SetStyling, 2984).
--define(wxStyledTextCtrl_GetBufferedDraw, 2985).
--define(wxStyledTextCtrl_SetBufferedDraw, 2986).
--define(wxStyledTextCtrl_SetTabWidth, 2987).
--define(wxStyledTextCtrl_GetTabWidth, 2988).
--define(wxStyledTextCtrl_SetCodePage, 2989).
--define(wxStyledTextCtrl_MarkerDefine, 2990).
--define(wxStyledTextCtrl_MarkerSetForeground, 2991).
--define(wxStyledTextCtrl_MarkerSetBackground, 2992).
--define(wxStyledTextCtrl_MarkerAdd, 2993).
--define(wxStyledTextCtrl_MarkerDelete, 2994).
--define(wxStyledTextCtrl_MarkerDeleteAll, 2995).
--define(wxStyledTextCtrl_MarkerGet, 2996).
--define(wxStyledTextCtrl_MarkerNext, 2997).
--define(wxStyledTextCtrl_MarkerPrevious, 2998).
--define(wxStyledTextCtrl_MarkerDefineBitmap, 2999).
--define(wxStyledTextCtrl_MarkerAddSet, 3000).
--define(wxStyledTextCtrl_MarkerSetAlpha, 3001).
--define(wxStyledTextCtrl_SetMarginType, 3002).
--define(wxStyledTextCtrl_GetMarginType, 3003).
--define(wxStyledTextCtrl_SetMarginWidth, 3004).
--define(wxStyledTextCtrl_GetMarginWidth, 3005).
--define(wxStyledTextCtrl_SetMarginMask, 3006).
--define(wxStyledTextCtrl_GetMarginMask, 3007).
--define(wxStyledTextCtrl_SetMarginSensitive, 3008).
--define(wxStyledTextCtrl_GetMarginSensitive, 3009).
--define(wxStyledTextCtrl_StyleClearAll, 3010).
--define(wxStyledTextCtrl_StyleSetForeground, 3011).
--define(wxStyledTextCtrl_StyleSetBackground, 3012).
--define(wxStyledTextCtrl_StyleSetBold, 3013).
--define(wxStyledTextCtrl_StyleSetItalic, 3014).
--define(wxStyledTextCtrl_StyleSetSize, 3015).
--define(wxStyledTextCtrl_StyleSetFaceName, 3016).
--define(wxStyledTextCtrl_StyleSetEOLFilled, 3017).
--define(wxStyledTextCtrl_StyleResetDefault, 3018).
--define(wxStyledTextCtrl_StyleSetUnderline, 3019).
--define(wxStyledTextCtrl_StyleSetCase, 3020).
--define(wxStyledTextCtrl_StyleSetHotSpot, 3021).
--define(wxStyledTextCtrl_SetSelForeground, 3022).
--define(wxStyledTextCtrl_SetSelBackground, 3023).
--define(wxStyledTextCtrl_GetSelAlpha, 3024).
--define(wxStyledTextCtrl_SetSelAlpha, 3025).
--define(wxStyledTextCtrl_SetCaretForeground, 3026).
--define(wxStyledTextCtrl_CmdKeyAssign, 3027).
--define(wxStyledTextCtrl_CmdKeyClear, 3028).
--define(wxStyledTextCtrl_CmdKeyClearAll, 3029).
--define(wxStyledTextCtrl_SetStyleBytes, 3030).
--define(wxStyledTextCtrl_StyleSetVisible, 3031).
--define(wxStyledTextCtrl_GetCaretPeriod, 3032).
--define(wxStyledTextCtrl_SetCaretPeriod, 3033).
--define(wxStyledTextCtrl_SetWordChars, 3034).
--define(wxStyledTextCtrl_BeginUndoAction, 3035).
--define(wxStyledTextCtrl_EndUndoAction, 3036).
--define(wxStyledTextCtrl_IndicatorSetStyle, 3037).
--define(wxStyledTextCtrl_IndicatorGetStyle, 3038).
--define(wxStyledTextCtrl_IndicatorSetForeground, 3039).
--define(wxStyledTextCtrl_IndicatorGetForeground, 3040).
--define(wxStyledTextCtrl_SetWhitespaceForeground, 3041).
--define(wxStyledTextCtrl_SetWhitespaceBackground, 3042).
--define(wxStyledTextCtrl_GetStyleBits, 3043).
--define(wxStyledTextCtrl_SetLineState, 3044).
--define(wxStyledTextCtrl_GetLineState, 3045).
--define(wxStyledTextCtrl_GetMaxLineState, 3046).
--define(wxStyledTextCtrl_GetCaretLineVisible, 3047).
--define(wxStyledTextCtrl_SetCaretLineVisible, 3048).
--define(wxStyledTextCtrl_GetCaretLineBackground, 3049).
--define(wxStyledTextCtrl_SetCaretLineBackground, 3050).
--define(wxStyledTextCtrl_AutoCompShow, 3051).
--define(wxStyledTextCtrl_AutoCompCancel, 3052).
--define(wxStyledTextCtrl_AutoCompActive, 3053).
--define(wxStyledTextCtrl_AutoCompPosStart, 3054).
--define(wxStyledTextCtrl_AutoCompComplete, 3055).
--define(wxStyledTextCtrl_AutoCompStops, 3056).
--define(wxStyledTextCtrl_AutoCompSetSeparator, 3057).
--define(wxStyledTextCtrl_AutoCompGetSeparator, 3058).
--define(wxStyledTextCtrl_AutoCompSelect, 3059).
--define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3060).
--define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3061).
--define(wxStyledTextCtrl_AutoCompSetFillUps, 3062).
--define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3063).
--define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3064).
--define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3065).
--define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3066).
--define(wxStyledTextCtrl_UserListShow, 3067).
--define(wxStyledTextCtrl_AutoCompSetAutoHide, 3068).
--define(wxStyledTextCtrl_AutoCompGetAutoHide, 3069).
--define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3070).
--define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3071).
--define(wxStyledTextCtrl_RegisterImage, 3072).
--define(wxStyledTextCtrl_ClearRegisteredImages, 3073).
--define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3074).
--define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3075).
--define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3076).
--define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3077).
--define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3078).
--define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3079).
--define(wxStyledTextCtrl_SetIndent, 3080).
--define(wxStyledTextCtrl_GetIndent, 3081).
--define(wxStyledTextCtrl_SetUseTabs, 3082).
--define(wxStyledTextCtrl_GetUseTabs, 3083).
--define(wxStyledTextCtrl_SetLineIndentation, 3084).
--define(wxStyledTextCtrl_GetLineIndentation, 3085).
--define(wxStyledTextCtrl_GetLineIndentPosition, 3086).
--define(wxStyledTextCtrl_GetColumn, 3087).
--define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3088).
--define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3089).
--define(wxStyledTextCtrl_SetIndentationGuides, 3090).
--define(wxStyledTextCtrl_GetIndentationGuides, 3091).
--define(wxStyledTextCtrl_SetHighlightGuide, 3092).
--define(wxStyledTextCtrl_GetHighlightGuide, 3093).
--define(wxStyledTextCtrl_GetLineEndPosition, 3094).
--define(wxStyledTextCtrl_GetCodePage, 3095).
--define(wxStyledTextCtrl_GetCaretForeground, 3096).
--define(wxStyledTextCtrl_GetReadOnly, 3097).
--define(wxStyledTextCtrl_SetCurrentPos, 3098).
--define(wxStyledTextCtrl_SetSelectionStart, 3099).
--define(wxStyledTextCtrl_GetSelectionStart, 3100).
--define(wxStyledTextCtrl_SetSelectionEnd, 3101).
--define(wxStyledTextCtrl_GetSelectionEnd, 3102).
--define(wxStyledTextCtrl_SetPrintMagnification, 3103).
--define(wxStyledTextCtrl_GetPrintMagnification, 3104).
--define(wxStyledTextCtrl_SetPrintColourMode, 3105).
--define(wxStyledTextCtrl_GetPrintColourMode, 3106).
--define(wxStyledTextCtrl_FindText, 3107).
--define(wxStyledTextCtrl_FormatRange, 3108).
--define(wxStyledTextCtrl_GetFirstVisibleLine, 3109).
--define(wxStyledTextCtrl_GetLine, 3110).
--define(wxStyledTextCtrl_GetLineCount, 3111).
--define(wxStyledTextCtrl_SetMarginLeft, 3112).
--define(wxStyledTextCtrl_GetMarginLeft, 3113).
--define(wxStyledTextCtrl_SetMarginRight, 3114).
--define(wxStyledTextCtrl_GetMarginRight, 3115).
--define(wxStyledTextCtrl_GetModify, 3116).
--define(wxStyledTextCtrl_SetSelection, 3117).
--define(wxStyledTextCtrl_GetSelectedText, 3118).
--define(wxStyledTextCtrl_GetTextRange, 3119).
--define(wxStyledTextCtrl_HideSelection, 3120).
--define(wxStyledTextCtrl_LineFromPosition, 3121).
--define(wxStyledTextCtrl_PositionFromLine, 3122).
--define(wxStyledTextCtrl_LineScroll, 3123).
--define(wxStyledTextCtrl_EnsureCaretVisible, 3124).
--define(wxStyledTextCtrl_ReplaceSelection, 3125).
--define(wxStyledTextCtrl_SetReadOnly, 3126).
--define(wxStyledTextCtrl_CanPaste, 3127).
--define(wxStyledTextCtrl_CanUndo, 3128).
--define(wxStyledTextCtrl_EmptyUndoBuffer, 3129).
--define(wxStyledTextCtrl_Undo, 3130).
--define(wxStyledTextCtrl_Cut, 3131).
--define(wxStyledTextCtrl_Copy, 3132).
--define(wxStyledTextCtrl_Paste, 3133).
--define(wxStyledTextCtrl_Clear, 3134).
--define(wxStyledTextCtrl_SetText, 3135).
--define(wxStyledTextCtrl_GetText, 3136).
--define(wxStyledTextCtrl_GetTextLength, 3137).
--define(wxStyledTextCtrl_GetOvertype, 3138).
--define(wxStyledTextCtrl_SetCaretWidth, 3139).
--define(wxStyledTextCtrl_GetCaretWidth, 3140).
--define(wxStyledTextCtrl_SetTargetStart, 3141).
--define(wxStyledTextCtrl_GetTargetStart, 3142).
--define(wxStyledTextCtrl_SetTargetEnd, 3143).
--define(wxStyledTextCtrl_GetTargetEnd, 3144).
--define(wxStyledTextCtrl_ReplaceTarget, 3145).
--define(wxStyledTextCtrl_SearchInTarget, 3146).
--define(wxStyledTextCtrl_SetSearchFlags, 3147).
--define(wxStyledTextCtrl_GetSearchFlags, 3148).
--define(wxStyledTextCtrl_CallTipShow, 3149).
--define(wxStyledTextCtrl_CallTipCancel, 3150).
--define(wxStyledTextCtrl_CallTipActive, 3151).
--define(wxStyledTextCtrl_CallTipPosAtStart, 3152).
--define(wxStyledTextCtrl_CallTipSetHighlight, 3153).
--define(wxStyledTextCtrl_CallTipSetBackground, 3154).
--define(wxStyledTextCtrl_CallTipSetForeground, 3155).
--define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3156).
--define(wxStyledTextCtrl_CallTipUseStyle, 3157).
--define(wxStyledTextCtrl_VisibleFromDocLine, 3158).
--define(wxStyledTextCtrl_DocLineFromVisible, 3159).
--define(wxStyledTextCtrl_WrapCount, 3160).
--define(wxStyledTextCtrl_SetFoldLevel, 3161).
--define(wxStyledTextCtrl_GetFoldLevel, 3162).
--define(wxStyledTextCtrl_GetLastChild, 3163).
--define(wxStyledTextCtrl_GetFoldParent, 3164).
--define(wxStyledTextCtrl_ShowLines, 3165).
--define(wxStyledTextCtrl_HideLines, 3166).
--define(wxStyledTextCtrl_GetLineVisible, 3167).
--define(wxStyledTextCtrl_SetFoldExpanded, 3168).
--define(wxStyledTextCtrl_GetFoldExpanded, 3169).
--define(wxStyledTextCtrl_ToggleFold, 3170).
--define(wxStyledTextCtrl_EnsureVisible, 3171).
--define(wxStyledTextCtrl_SetFoldFlags, 3172).
--define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3173).
--define(wxStyledTextCtrl_SetTabIndents, 3174).
--define(wxStyledTextCtrl_GetTabIndents, 3175).
--define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3176).
--define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3177).
--define(wxStyledTextCtrl_SetMouseDwellTime, 3178).
--define(wxStyledTextCtrl_GetMouseDwellTime, 3179).
--define(wxStyledTextCtrl_WordStartPosition, 3180).
--define(wxStyledTextCtrl_WordEndPosition, 3181).
--define(wxStyledTextCtrl_SetWrapMode, 3182).
--define(wxStyledTextCtrl_GetWrapMode, 3183).
--define(wxStyledTextCtrl_SetWrapVisualFlags, 3184).
--define(wxStyledTextCtrl_GetWrapVisualFlags, 3185).
--define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3186).
--define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3187).
--define(wxStyledTextCtrl_SetWrapStartIndent, 3188).
--define(wxStyledTextCtrl_GetWrapStartIndent, 3189).
--define(wxStyledTextCtrl_SetLayoutCache, 3190).
--define(wxStyledTextCtrl_GetLayoutCache, 3191).
--define(wxStyledTextCtrl_SetScrollWidth, 3192).
--define(wxStyledTextCtrl_GetScrollWidth, 3193).
--define(wxStyledTextCtrl_TextWidth, 3194).
--define(wxStyledTextCtrl_GetEndAtLastLine, 3195).
--define(wxStyledTextCtrl_TextHeight, 3196).
--define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3197).
--define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3198).
--define(wxStyledTextCtrl_AppendText, 3199).
--define(wxStyledTextCtrl_GetTwoPhaseDraw, 3200).
--define(wxStyledTextCtrl_SetTwoPhaseDraw, 3201).
--define(wxStyledTextCtrl_TargetFromSelection, 3202).
--define(wxStyledTextCtrl_LinesJoin, 3203).
--define(wxStyledTextCtrl_LinesSplit, 3204).
--define(wxStyledTextCtrl_SetFoldMarginColour, 3205).
--define(wxStyledTextCtrl_SetFoldMarginHiColour, 3206).
--define(wxStyledTextCtrl_LineDown, 3207).
--define(wxStyledTextCtrl_LineDownExtend, 3208).
--define(wxStyledTextCtrl_LineUp, 3209).
--define(wxStyledTextCtrl_LineUpExtend, 3210).
--define(wxStyledTextCtrl_CharLeft, 3211).
--define(wxStyledTextCtrl_CharLeftExtend, 3212).
--define(wxStyledTextCtrl_CharRight, 3213).
--define(wxStyledTextCtrl_CharRightExtend, 3214).
--define(wxStyledTextCtrl_WordLeft, 3215).
--define(wxStyledTextCtrl_WordLeftExtend, 3216).
--define(wxStyledTextCtrl_WordRight, 3217).
--define(wxStyledTextCtrl_WordRightExtend, 3218).
--define(wxStyledTextCtrl_Home, 3219).
--define(wxStyledTextCtrl_HomeExtend, 3220).
--define(wxStyledTextCtrl_LineEnd, 3221).
--define(wxStyledTextCtrl_LineEndExtend, 3222).
--define(wxStyledTextCtrl_DocumentStart, 3223).
--define(wxStyledTextCtrl_DocumentStartExtend, 3224).
--define(wxStyledTextCtrl_DocumentEnd, 3225).
--define(wxStyledTextCtrl_DocumentEndExtend, 3226).
--define(wxStyledTextCtrl_PageUp, 3227).
--define(wxStyledTextCtrl_PageUpExtend, 3228).
--define(wxStyledTextCtrl_PageDown, 3229).
--define(wxStyledTextCtrl_PageDownExtend, 3230).
--define(wxStyledTextCtrl_EditToggleOvertype, 3231).
--define(wxStyledTextCtrl_Cancel, 3232).
--define(wxStyledTextCtrl_DeleteBack, 3233).
--define(wxStyledTextCtrl_Tab, 3234).
--define(wxStyledTextCtrl_BackTab, 3235).
--define(wxStyledTextCtrl_NewLine, 3236).
--define(wxStyledTextCtrl_FormFeed, 3237).
--define(wxStyledTextCtrl_VCHome, 3238).
--define(wxStyledTextCtrl_VCHomeExtend, 3239).
--define(wxStyledTextCtrl_ZoomIn, 3240).
--define(wxStyledTextCtrl_ZoomOut, 3241).
--define(wxStyledTextCtrl_DelWordLeft, 3242).
--define(wxStyledTextCtrl_DelWordRight, 3243).
--define(wxStyledTextCtrl_LineCut, 3244).
--define(wxStyledTextCtrl_LineDelete, 3245).
--define(wxStyledTextCtrl_LineTranspose, 3246).
--define(wxStyledTextCtrl_LineDuplicate, 3247).
--define(wxStyledTextCtrl_LowerCase, 3248).
--define(wxStyledTextCtrl_UpperCase, 3249).
--define(wxStyledTextCtrl_LineScrollDown, 3250).
--define(wxStyledTextCtrl_LineScrollUp, 3251).
--define(wxStyledTextCtrl_DeleteBackNotLine, 3252).
--define(wxStyledTextCtrl_HomeDisplay, 3253).
--define(wxStyledTextCtrl_HomeDisplayExtend, 3254).
--define(wxStyledTextCtrl_LineEndDisplay, 3255).
--define(wxStyledTextCtrl_LineEndDisplayExtend, 3256).
--define(wxStyledTextCtrl_HomeWrapExtend, 3257).
--define(wxStyledTextCtrl_LineEndWrap, 3258).
--define(wxStyledTextCtrl_LineEndWrapExtend, 3259).
--define(wxStyledTextCtrl_VCHomeWrap, 3260).
--define(wxStyledTextCtrl_VCHomeWrapExtend, 3261).
--define(wxStyledTextCtrl_LineCopy, 3262).
--define(wxStyledTextCtrl_MoveCaretInsideView, 3263).
--define(wxStyledTextCtrl_LineLength, 3264).
--define(wxStyledTextCtrl_BraceHighlight, 3265).
--define(wxStyledTextCtrl_BraceBadLight, 3266).
--define(wxStyledTextCtrl_BraceMatch, 3267).
--define(wxStyledTextCtrl_GetViewEOL, 3268).
--define(wxStyledTextCtrl_SetViewEOL, 3269).
--define(wxStyledTextCtrl_SetModEventMask, 3270).
--define(wxStyledTextCtrl_GetEdgeColumn, 3271).
--define(wxStyledTextCtrl_SetEdgeColumn, 3272).
--define(wxStyledTextCtrl_SetEdgeMode, 3273).
--define(wxStyledTextCtrl_GetEdgeMode, 3274).
--define(wxStyledTextCtrl_GetEdgeColour, 3275).
--define(wxStyledTextCtrl_SetEdgeColour, 3276).
--define(wxStyledTextCtrl_SearchAnchor, 3277).
--define(wxStyledTextCtrl_SearchNext, 3278).
--define(wxStyledTextCtrl_SearchPrev, 3279).
--define(wxStyledTextCtrl_LinesOnScreen, 3280).
--define(wxStyledTextCtrl_UsePopUp, 3281).
--define(wxStyledTextCtrl_SelectionIsRectangle, 3282).
--define(wxStyledTextCtrl_SetZoom, 3283).
--define(wxStyledTextCtrl_GetZoom, 3284).
--define(wxStyledTextCtrl_GetModEventMask, 3285).
--define(wxStyledTextCtrl_SetSTCFocus, 3286).
--define(wxStyledTextCtrl_GetSTCFocus, 3287).
--define(wxStyledTextCtrl_SetStatus, 3288).
--define(wxStyledTextCtrl_GetStatus, 3289).
--define(wxStyledTextCtrl_SetMouseDownCaptures, 3290).
--define(wxStyledTextCtrl_GetMouseDownCaptures, 3291).
--define(wxStyledTextCtrl_SetSTCCursor, 3292).
--define(wxStyledTextCtrl_GetSTCCursor, 3293).
--define(wxStyledTextCtrl_SetControlCharSymbol, 3294).
--define(wxStyledTextCtrl_GetControlCharSymbol, 3295).
--define(wxStyledTextCtrl_WordPartLeft, 3296).
--define(wxStyledTextCtrl_WordPartLeftExtend, 3297).
--define(wxStyledTextCtrl_WordPartRight, 3298).
--define(wxStyledTextCtrl_WordPartRightExtend, 3299).
--define(wxStyledTextCtrl_SetVisiblePolicy, 3300).
--define(wxStyledTextCtrl_DelLineLeft, 3301).
--define(wxStyledTextCtrl_DelLineRight, 3302).
--define(wxStyledTextCtrl_GetXOffset, 3303).
--define(wxStyledTextCtrl_ChooseCaretX, 3304).
--define(wxStyledTextCtrl_SetXCaretPolicy, 3305).
--define(wxStyledTextCtrl_SetYCaretPolicy, 3306).
--define(wxStyledTextCtrl_GetPrintWrapMode, 3307).
--define(wxStyledTextCtrl_SetHotspotActiveForeground, 3308).
--define(wxStyledTextCtrl_SetHotspotActiveBackground, 3309).
--define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3310).
--define(wxStyledTextCtrl_SetHotspotSingleLine, 3311).
--define(wxStyledTextCtrl_ParaDownExtend, 3312).
--define(wxStyledTextCtrl_ParaUp, 3313).
--define(wxStyledTextCtrl_ParaUpExtend, 3314).
--define(wxStyledTextCtrl_PositionBefore, 3315).
--define(wxStyledTextCtrl_PositionAfter, 3316).
--define(wxStyledTextCtrl_CopyRange, 3317).
--define(wxStyledTextCtrl_CopyText, 3318).
--define(wxStyledTextCtrl_SetSelectionMode, 3319).
--define(wxStyledTextCtrl_GetSelectionMode, 3320).
--define(wxStyledTextCtrl_LineDownRectExtend, 3321).
--define(wxStyledTextCtrl_LineUpRectExtend, 3322).
--define(wxStyledTextCtrl_CharLeftRectExtend, 3323).
--define(wxStyledTextCtrl_CharRightRectExtend, 3324).
--define(wxStyledTextCtrl_HomeRectExtend, 3325).
--define(wxStyledTextCtrl_VCHomeRectExtend, 3326).
--define(wxStyledTextCtrl_LineEndRectExtend, 3327).
--define(wxStyledTextCtrl_PageUpRectExtend, 3328).
--define(wxStyledTextCtrl_PageDownRectExtend, 3329).
--define(wxStyledTextCtrl_StutteredPageUp, 3330).
--define(wxStyledTextCtrl_StutteredPageUpExtend, 3331).
--define(wxStyledTextCtrl_StutteredPageDown, 3332).
--define(wxStyledTextCtrl_StutteredPageDownExtend, 3333).
--define(wxStyledTextCtrl_WordLeftEnd, 3334).
--define(wxStyledTextCtrl_WordLeftEndExtend, 3335).
--define(wxStyledTextCtrl_WordRightEnd, 3336).
--define(wxStyledTextCtrl_WordRightEndExtend, 3337).
--define(wxStyledTextCtrl_SetWhitespaceChars, 3338).
--define(wxStyledTextCtrl_SetCharsDefault, 3339).
--define(wxStyledTextCtrl_AutoCompGetCurrent, 3340).
--define(wxStyledTextCtrl_Allocate, 3341).
--define(wxStyledTextCtrl_FindColumn, 3342).
--define(wxStyledTextCtrl_GetCaretSticky, 3343).
--define(wxStyledTextCtrl_SetCaretSticky, 3344).
--define(wxStyledTextCtrl_ToggleCaretSticky, 3345).
--define(wxStyledTextCtrl_SetPasteConvertEndings, 3346).
--define(wxStyledTextCtrl_GetPasteConvertEndings, 3347).
--define(wxStyledTextCtrl_SelectionDuplicate, 3348).
--define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3349).
--define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3350).
--define(wxStyledTextCtrl_StartRecord, 3351).
--define(wxStyledTextCtrl_StopRecord, 3352).
--define(wxStyledTextCtrl_SetLexer, 3353).
--define(wxStyledTextCtrl_GetLexer, 3354).
--define(wxStyledTextCtrl_Colourise, 3355).
--define(wxStyledTextCtrl_SetProperty, 3356).
--define(wxStyledTextCtrl_SetKeyWords, 3357).
--define(wxStyledTextCtrl_SetLexerLanguage, 3358).
--define(wxStyledTextCtrl_GetProperty, 3359).
--define(wxStyledTextCtrl_GetStyleBitsNeeded, 3360).
--define(wxStyledTextCtrl_GetCurrentLine, 3361).
--define(wxStyledTextCtrl_StyleSetSpec, 3362).
--define(wxStyledTextCtrl_StyleSetFont, 3363).
--define(wxStyledTextCtrl_StyleSetFontAttr, 3364).
--define(wxStyledTextCtrl_StyleSetCharacterSet, 3365).
--define(wxStyledTextCtrl_StyleSetFontEncoding, 3366).
--define(wxStyledTextCtrl_CmdKeyExecute, 3367).
--define(wxStyledTextCtrl_SetMargins, 3368).
--define(wxStyledTextCtrl_GetSelection, 3369).
--define(wxStyledTextCtrl_PointFromPosition, 3370).
--define(wxStyledTextCtrl_ScrollToLine, 3371).
--define(wxStyledTextCtrl_ScrollToColumn, 3372).
--define(wxStyledTextCtrl_SendMsg, 3373).
+-define(wxTreeCtrl_IsTreeItemIdOk, 2061).
+-define(wxTreeCtrl_PrependItem, 2062).
+-define(wxTreeCtrl_ScrollTo, 2063).
+-define(wxTreeCtrl_SelectItem_1, 2064).
+-define(wxTreeCtrl_SelectItem_2, 2065).
+-define(wxTreeCtrl_SetIndent, 2066).
+-define(wxTreeCtrl_SetImageList, 2067).
+-define(wxTreeCtrl_SetItemBackgroundColour, 2068).
+-define(wxTreeCtrl_SetItemBold, 2069).
+-define(wxTreeCtrl_SetItemData, 2070).
+-define(wxTreeCtrl_SetItemDropHighlight, 2071).
+-define(wxTreeCtrl_SetItemFont, 2072).
+-define(wxTreeCtrl_SetItemHasChildren, 2073).
+-define(wxTreeCtrl_SetItemImage_2, 2074).
+-define(wxTreeCtrl_SetItemImage_3, 2075).
+-define(wxTreeCtrl_SetItemText, 2076).
+-define(wxTreeCtrl_SetItemTextColour, 2077).
+-define(wxTreeCtrl_SetStateImageList, 2078).
+-define(wxTreeCtrl_SetWindowStyle, 2079).
+-define(wxTreeCtrl_SortChildren, 2080).
+-define(wxTreeCtrl_Toggle, 2081).
+-define(wxTreeCtrl_ToggleItemSelection, 2082).
+-define(wxTreeCtrl_Unselect, 2083).
+-define(wxTreeCtrl_UnselectAll, 2084).
+-define(wxTreeCtrl_UnselectItem, 2085).
+-define(wxScrollBar_new_0, 2086).
+-define(wxScrollBar_new_3, 2087).
+-define(wxScrollBar_destruct, 2088).
+-define(wxScrollBar_Create, 2089).
+-define(wxScrollBar_GetRange, 2090).
+-define(wxScrollBar_GetPageSize, 2091).
+-define(wxScrollBar_GetThumbPosition, 2092).
+-define(wxScrollBar_GetThumbSize, 2093).
+-define(wxScrollBar_SetThumbPosition, 2094).
+-define(wxScrollBar_SetScrollbar, 2095).
+-define(wxSpinButton_new_2, 2097).
+-define(wxSpinButton_new_0, 2098).
+-define(wxSpinButton_Create, 2099).
+-define(wxSpinButton_GetMax, 2100).
+-define(wxSpinButton_GetMin, 2101).
+-define(wxSpinButton_GetValue, 2102).
+-define(wxSpinButton_SetRange, 2103).
+-define(wxSpinButton_SetValue, 2104).
+-define(wxSpinButton_destroy, 2105).
+-define(wxSpinCtrl_new_0, 2106).
+-define(wxSpinCtrl_new_2, 2107).
+-define(wxSpinCtrl_Create, 2109).
+-define(wxSpinCtrl_SetValue_1_1, 2112).
+-define(wxSpinCtrl_SetValue_1_0, 2113).
+-define(wxSpinCtrl_GetValue, 2115).
+-define(wxSpinCtrl_SetRange, 2117).
+-define(wxSpinCtrl_SetSelection, 2118).
+-define(wxSpinCtrl_GetMin, 2120).
+-define(wxSpinCtrl_GetMax, 2122).
+-define(wxSpinCtrl_destroy, 2123).
+-define(wxStaticText_new_0, 2124).
+-define(wxStaticText_new_4, 2125).
+-define(wxStaticText_Create, 2126).
+-define(wxStaticText_GetLabel, 2127).
+-define(wxStaticText_SetLabel, 2128).
+-define(wxStaticText_Wrap, 2129).
+-define(wxStaticText_destroy, 2130).
+-define(wxStaticBitmap_new_0, 2131).
+-define(wxStaticBitmap_new_4, 2132).
+-define(wxStaticBitmap_Create, 2133).
+-define(wxStaticBitmap_GetBitmap, 2134).
+-define(wxStaticBitmap_SetBitmap, 2135).
+-define(wxStaticBitmap_destroy, 2136).
+-define(wxRadioBox_new, 2137).
+-define(wxRadioBox_destruct, 2139).
+-define(wxRadioBox_Create, 2140).
+-define(wxRadioBox_Enable_2, 2141).
+-define(wxRadioBox_Enable_1, 2142).
+-define(wxRadioBox_GetSelection, 2143).
+-define(wxRadioBox_GetString, 2144).
+-define(wxRadioBox_SetSelection, 2145).
+-define(wxRadioBox_Show_2, 2146).
+-define(wxRadioBox_Show_1, 2147).
+-define(wxRadioBox_GetColumnCount, 2148).
+-define(wxRadioBox_GetItemHelpText, 2149).
+-define(wxRadioBox_GetItemToolTip, 2150).
+-define(wxRadioBox_GetItemFromPoint, 2152).
+-define(wxRadioBox_GetRowCount, 2153).
+-define(wxRadioBox_IsItemEnabled, 2154).
+-define(wxRadioBox_IsItemShown, 2155).
+-define(wxRadioBox_SetItemHelpText, 2156).
+-define(wxRadioBox_SetItemToolTip, 2157).
+-define(wxRadioButton_new_0, 2158).
+-define(wxRadioButton_new_4, 2159).
+-define(wxRadioButton_Create, 2160).
+-define(wxRadioButton_GetValue, 2161).
+-define(wxRadioButton_SetValue, 2162).
+-define(wxRadioButton_destroy, 2163).
+-define(wxSlider_new_6, 2165).
+-define(wxSlider_new_0, 2166).
+-define(wxSlider_Create, 2167).
+-define(wxSlider_GetLineSize, 2168).
+-define(wxSlider_GetMax, 2169).
+-define(wxSlider_GetMin, 2170).
+-define(wxSlider_GetPageSize, 2171).
+-define(wxSlider_GetThumbLength, 2172).
+-define(wxSlider_GetValue, 2173).
+-define(wxSlider_SetLineSize, 2174).
+-define(wxSlider_SetPageSize, 2175).
+-define(wxSlider_SetRange, 2176).
+-define(wxSlider_SetThumbLength, 2177).
+-define(wxSlider_SetValue, 2178).
+-define(wxSlider_destroy, 2179).
+-define(wxDialog_new_4, 2181).
+-define(wxDialog_new_0, 2182).
+-define(wxDialog_destruct, 2184).
+-define(wxDialog_Create, 2185).
+-define(wxDialog_CreateButtonSizer, 2186).
+-define(wxDialog_CreateStdDialogButtonSizer, 2187).
+-define(wxDialog_EndModal, 2188).
+-define(wxDialog_GetAffirmativeId, 2189).
+-define(wxDialog_GetReturnCode, 2190).
+-define(wxDialog_IsModal, 2191).
+-define(wxDialog_SetAffirmativeId, 2192).
+-define(wxDialog_SetReturnCode, 2193).
+-define(wxDialog_Show, 2194).
+-define(wxDialog_ShowModal, 2195).
+-define(wxColourDialog_new_0, 2196).
+-define(wxColourDialog_new_2, 2197).
+-define(wxColourDialog_destruct, 2198).
+-define(wxColourDialog_Create, 2199).
+-define(wxColourDialog_GetColourData, 2200).
+-define(wxColourData_new_0, 2201).
+-define(wxColourData_new_1, 2202).
+-define(wxColourData_destruct, 2203).
+-define(wxColourData_GetChooseFull, 2204).
+-define(wxColourData_GetColour, 2205).
+-define(wxColourData_GetCustomColour, 2207).
+-define(wxColourData_SetChooseFull, 2208).
+-define(wxColourData_SetColour, 2209).
+-define(wxColourData_SetCustomColour, 2210).
+-define(wxPalette_new_0, 2211).
+-define(wxPalette_new_4, 2212).
+-define(wxPalette_destruct, 2214).
+-define(wxPalette_Create, 2215).
+-define(wxPalette_GetColoursCount, 2216).
+-define(wxPalette_GetPixel, 2217).
+-define(wxPalette_GetRGB, 2218).
+-define(wxPalette_IsOk, 2219).
+-define(wxDirDialog_new, 2223).
+-define(wxDirDialog_destruct, 2224).
+-define(wxDirDialog_GetPath, 2225).
+-define(wxDirDialog_GetMessage, 2226).
+-define(wxDirDialog_SetMessage, 2227).
+-define(wxDirDialog_SetPath, 2228).
+-define(wxFileDialog_new, 2232).
+-define(wxFileDialog_destruct, 2233).
+-define(wxFileDialog_GetDirectory, 2234).
+-define(wxFileDialog_GetFilename, 2235).
+-define(wxFileDialog_GetFilenames, 2236).
+-define(wxFileDialog_GetFilterIndex, 2237).
+-define(wxFileDialog_GetMessage, 2238).
+-define(wxFileDialog_GetPath, 2239).
+-define(wxFileDialog_GetPaths, 2240).
+-define(wxFileDialog_GetWildcard, 2241).
+-define(wxFileDialog_SetDirectory, 2242).
+-define(wxFileDialog_SetFilename, 2243).
+-define(wxFileDialog_SetFilterIndex, 2244).
+-define(wxFileDialog_SetMessage, 2245).
+-define(wxFileDialog_SetPath, 2246).
+-define(wxFileDialog_SetWildcard, 2247).
+-define(wxPickerBase_SetInternalMargin, 2248).
+-define(wxPickerBase_GetInternalMargin, 2249).
+-define(wxPickerBase_SetTextCtrlProportion, 2250).
+-define(wxPickerBase_SetPickerCtrlProportion, 2251).
+-define(wxPickerBase_GetTextCtrlProportion, 2252).
+-define(wxPickerBase_GetPickerCtrlProportion, 2253).
+-define(wxPickerBase_HasTextCtrl, 2254).
+-define(wxPickerBase_GetTextCtrl, 2255).
+-define(wxPickerBase_IsTextCtrlGrowable, 2256).
+-define(wxPickerBase_SetPickerCtrlGrowable, 2257).
+-define(wxPickerBase_SetTextCtrlGrowable, 2258).
+-define(wxPickerBase_IsPickerCtrlGrowable, 2259).
+-define(wxFilePickerCtrl_new_0, 2260).
+-define(wxFilePickerCtrl_new_3, 2261).
+-define(wxFilePickerCtrl_Create, 2262).
+-define(wxFilePickerCtrl_GetPath, 2263).
+-define(wxFilePickerCtrl_SetPath, 2264).
+-define(wxFilePickerCtrl_destroy, 2265).
+-define(wxDirPickerCtrl_new_0, 2266).
+-define(wxDirPickerCtrl_new_3, 2267).
+-define(wxDirPickerCtrl_Create, 2268).
+-define(wxDirPickerCtrl_GetPath, 2269).
+-define(wxDirPickerCtrl_SetPath, 2270).
+-define(wxDirPickerCtrl_destroy, 2271).
+-define(wxColourPickerCtrl_new_0, 2272).
+-define(wxColourPickerCtrl_new_3, 2273).
+-define(wxColourPickerCtrl_Create, 2274).
+-define(wxColourPickerCtrl_GetColour, 2275).
+-define(wxColourPickerCtrl_SetColour_1_1, 2276).
+-define(wxColourPickerCtrl_SetColour_1_0, 2277).
+-define(wxColourPickerCtrl_destroy, 2278).
+-define(wxDatePickerCtrl_new_0, 2279).
+-define(wxDatePickerCtrl_new_3, 2280).
+-define(wxDatePickerCtrl_GetRange, 2281).
+-define(wxDatePickerCtrl_GetValue, 2282).
+-define(wxDatePickerCtrl_SetRange, 2283).
+-define(wxDatePickerCtrl_SetValue, 2284).
+-define(wxDatePickerCtrl_destroy, 2285).
+-define(wxFontPickerCtrl_new_0, 2286).
+-define(wxFontPickerCtrl_new_3, 2287).
+-define(wxFontPickerCtrl_Create, 2288).
+-define(wxFontPickerCtrl_GetSelectedFont, 2289).
+-define(wxFontPickerCtrl_SetSelectedFont, 2290).
+-define(wxFontPickerCtrl_GetMaxPointSize, 2291).
+-define(wxFontPickerCtrl_SetMaxPointSize, 2292).
+-define(wxFontPickerCtrl_destroy, 2293).
+-define(wxFindReplaceDialog_new_0, 2296).
+-define(wxFindReplaceDialog_new_4, 2297).
+-define(wxFindReplaceDialog_destruct, 2298).
+-define(wxFindReplaceDialog_Create, 2299).
+-define(wxFindReplaceDialog_GetData, 2300).
+-define(wxFindReplaceData_new_0, 2301).
+-define(wxFindReplaceData_new_1, 2302).
+-define(wxFindReplaceData_GetFindString, 2303).
+-define(wxFindReplaceData_GetReplaceString, 2304).
+-define(wxFindReplaceData_GetFlags, 2305).
+-define(wxFindReplaceData_SetFlags, 2306).
+-define(wxFindReplaceData_SetFindString, 2307).
+-define(wxFindReplaceData_SetReplaceString, 2308).
+-define(wxFindReplaceData_destroy, 2309).
+-define(wxMultiChoiceDialog_new_0, 2310).
+-define(wxMultiChoiceDialog_new_5, 2312).
+-define(wxMultiChoiceDialog_GetSelections, 2313).
+-define(wxMultiChoiceDialog_SetSelections, 2314).
+-define(wxMultiChoiceDialog_destroy, 2315).
+-define(wxSingleChoiceDialog_new_0, 2316).
+-define(wxSingleChoiceDialog_new_5, 2318).
+-define(wxSingleChoiceDialog_GetSelection, 2319).
+-define(wxSingleChoiceDialog_GetStringSelection, 2320).
+-define(wxSingleChoiceDialog_SetSelection, 2321).
+-define(wxSingleChoiceDialog_destroy, 2322).
+-define(wxTextEntryDialog_new, 2323).
+-define(wxTextEntryDialog_GetValue, 2324).
+-define(wxTextEntryDialog_SetValue, 2325).
+-define(wxTextEntryDialog_destroy, 2326).
+-define(wxPasswordEntryDialog_new, 2327).
+-define(wxPasswordEntryDialog_destroy, 2328).
+-define(wxFontData_new_0, 2329).
+-define(wxFontData_new_1, 2330).
+-define(wxFontData_destruct, 2331).
+-define(wxFontData_EnableEffects, 2332).
+-define(wxFontData_GetAllowSymbols, 2333).
+-define(wxFontData_GetColour, 2334).
+-define(wxFontData_GetChosenFont, 2335).
+-define(wxFontData_GetEnableEffects, 2336).
+-define(wxFontData_GetInitialFont, 2337).
+-define(wxFontData_GetShowHelp, 2338).
+-define(wxFontData_SetAllowSymbols, 2339).
+-define(wxFontData_SetChosenFont, 2340).
+-define(wxFontData_SetColour, 2341).
+-define(wxFontData_SetInitialFont, 2342).
+-define(wxFontData_SetRange, 2343).
+-define(wxFontData_SetShowHelp, 2344).
+-define(wxFontDialog_new_0, 2348).
+-define(wxFontDialog_new_2, 2350).
+-define(wxFontDialog_Create, 2352).
+-define(wxFontDialog_GetFontData, 2353).
+-define(wxFontDialog_destroy, 2355).
+-define(wxProgressDialog_new, 2356).
+-define(wxProgressDialog_destruct, 2357).
+-define(wxProgressDialog_Resume, 2358).
+-define(wxProgressDialog_Update_2, 2359).
+-define(wxProgressDialog_Update_0, 2360).
+-define(wxMessageDialog_new, 2361).
+-define(wxMessageDialog_destruct, 2362).
+-define(wxPageSetupDialog_new, 2363).
+-define(wxPageSetupDialog_destruct, 2364).
+-define(wxPageSetupDialog_GetPageSetupData, 2365).
+-define(wxPageSetupDialog_ShowModal, 2366).
+-define(wxPageSetupDialogData_new_0, 2367).
+-define(wxPageSetupDialogData_new_1_0, 2368).
+-define(wxPageSetupDialogData_new_1_1, 2369).
+-define(wxPageSetupDialogData_destruct, 2370).
+-define(wxPageSetupDialogData_EnableHelp, 2371).
+-define(wxPageSetupDialogData_EnableMargins, 2372).
+-define(wxPageSetupDialogData_EnableOrientation, 2373).
+-define(wxPageSetupDialogData_EnablePaper, 2374).
+-define(wxPageSetupDialogData_EnablePrinter, 2375).
+-define(wxPageSetupDialogData_GetDefaultMinMargins, 2376).
+-define(wxPageSetupDialogData_GetEnableMargins, 2377).
+-define(wxPageSetupDialogData_GetEnableOrientation, 2378).
+-define(wxPageSetupDialogData_GetEnablePaper, 2379).
+-define(wxPageSetupDialogData_GetEnablePrinter, 2380).
+-define(wxPageSetupDialogData_GetEnableHelp, 2381).
+-define(wxPageSetupDialogData_GetDefaultInfo, 2382).
+-define(wxPageSetupDialogData_GetMarginTopLeft, 2383).
+-define(wxPageSetupDialogData_GetMarginBottomRight, 2384).
+-define(wxPageSetupDialogData_GetMinMarginTopLeft, 2385).
+-define(wxPageSetupDialogData_GetMinMarginBottomRight, 2386).
+-define(wxPageSetupDialogData_GetPaperId, 2387).
+-define(wxPageSetupDialogData_GetPaperSize, 2388).
+-define(wxPageSetupDialogData_GetPrintData, 2390).
+-define(wxPageSetupDialogData_IsOk, 2391).
+-define(wxPageSetupDialogData_SetDefaultInfo, 2392).
+-define(wxPageSetupDialogData_SetDefaultMinMargins, 2393).
+-define(wxPageSetupDialogData_SetMarginTopLeft, 2394).
+-define(wxPageSetupDialogData_SetMarginBottomRight, 2395).
+-define(wxPageSetupDialogData_SetMinMarginTopLeft, 2396).
+-define(wxPageSetupDialogData_SetMinMarginBottomRight, 2397).
+-define(wxPageSetupDialogData_SetPaperId, 2398).
+-define(wxPageSetupDialogData_SetPaperSize_1_1, 2399).
+-define(wxPageSetupDialogData_SetPaperSize_1_0, 2400).
+-define(wxPageSetupDialogData_SetPrintData, 2401).
+-define(wxPrintDialog_new_2_0, 2402).
+-define(wxPrintDialog_new_2_1, 2403).
+-define(wxPrintDialog_destruct, 2404).
+-define(wxPrintDialog_GetPrintDialogData, 2405).
+-define(wxPrintDialog_GetPrintDC, 2406).
+-define(wxPrintDialogData_new_0, 2407).
+-define(wxPrintDialogData_new_1_1, 2408).
+-define(wxPrintDialogData_new_1_0, 2409).
+-define(wxPrintDialogData_destruct, 2410).
+-define(wxPrintDialogData_EnableHelp, 2411).
+-define(wxPrintDialogData_EnablePageNumbers, 2412).
+-define(wxPrintDialogData_EnablePrintToFile, 2413).
+-define(wxPrintDialogData_EnableSelection, 2414).
+-define(wxPrintDialogData_GetAllPages, 2415).
+-define(wxPrintDialogData_GetCollate, 2416).
+-define(wxPrintDialogData_GetFromPage, 2417).
+-define(wxPrintDialogData_GetMaxPage, 2418).
+-define(wxPrintDialogData_GetMinPage, 2419).
+-define(wxPrintDialogData_GetNoCopies, 2420).
+-define(wxPrintDialogData_GetPrintData, 2421).
+-define(wxPrintDialogData_GetPrintToFile, 2422).
+-define(wxPrintDialogData_GetSelection, 2423).
+-define(wxPrintDialogData_GetToPage, 2424).
+-define(wxPrintDialogData_IsOk, 2425).
+-define(wxPrintDialogData_SetCollate, 2426).
+-define(wxPrintDialogData_SetFromPage, 2427).
+-define(wxPrintDialogData_SetMaxPage, 2428).
+-define(wxPrintDialogData_SetMinPage, 2429).
+-define(wxPrintDialogData_SetNoCopies, 2430).
+-define(wxPrintDialogData_SetPrintData, 2431).
+-define(wxPrintDialogData_SetPrintToFile, 2432).
+-define(wxPrintDialogData_SetSelection, 2433).
+-define(wxPrintDialogData_SetToPage, 2434).
+-define(wxPrintData_new_0, 2435).
+-define(wxPrintData_new_1, 2436).
+-define(wxPrintData_destruct, 2437).
+-define(wxPrintData_GetCollate, 2438).
+-define(wxPrintData_GetBin, 2439).
+-define(wxPrintData_GetColour, 2440).
+-define(wxPrintData_GetDuplex, 2441).
+-define(wxPrintData_GetNoCopies, 2442).
+-define(wxPrintData_GetOrientation, 2443).
+-define(wxPrintData_GetPaperId, 2444).
+-define(wxPrintData_GetPrinterName, 2445).
+-define(wxPrintData_GetQuality, 2446).
+-define(wxPrintData_IsOk, 2447).
+-define(wxPrintData_SetBin, 2448).
+-define(wxPrintData_SetCollate, 2449).
+-define(wxPrintData_SetColour, 2450).
+-define(wxPrintData_SetDuplex, 2451).
+-define(wxPrintData_SetNoCopies, 2452).
+-define(wxPrintData_SetOrientation, 2453).
+-define(wxPrintData_SetPaperId, 2454).
+-define(wxPrintData_SetPrinterName, 2455).
+-define(wxPrintData_SetQuality, 2456).
+-define(wxPrintPreview_new_2, 2459).
+-define(wxPrintPreview_new_3, 2460).
+-define(wxPrintPreview_destruct, 2462).
+-define(wxPrintPreview_GetCanvas, 2463).
+-define(wxPrintPreview_GetCurrentPage, 2464).
+-define(wxPrintPreview_GetFrame, 2465).
+-define(wxPrintPreview_GetMaxPage, 2466).
+-define(wxPrintPreview_GetMinPage, 2467).
+-define(wxPrintPreview_GetPrintout, 2468).
+-define(wxPrintPreview_GetPrintoutForPrinting, 2469).
+-define(wxPrintPreview_IsOk, 2470).
+-define(wxPrintPreview_PaintPage, 2471).
+-define(wxPrintPreview_Print, 2472).
+-define(wxPrintPreview_RenderPage, 2473).
+-define(wxPrintPreview_SetCanvas, 2474).
+-define(wxPrintPreview_SetCurrentPage, 2475).
+-define(wxPrintPreview_SetFrame, 2476).
+-define(wxPrintPreview_SetPrintout, 2477).
+-define(wxPrintPreview_SetZoom, 2478).
+-define(wxPreviewFrame_new, 2479).
+-define(wxPreviewFrame_destruct, 2480).
+-define(wxPreviewFrame_CreateControlBar, 2481).
+-define(wxPreviewFrame_CreateCanvas, 2482).
+-define(wxPreviewFrame_Initialize, 2483).
+-define(wxPreviewFrame_OnCloseWindow, 2484).
+-define(wxPreviewControlBar_new, 2485).
+-define(wxPreviewControlBar_destruct, 2486).
+-define(wxPreviewControlBar_CreateButtons, 2487).
+-define(wxPreviewControlBar_GetPrintPreview, 2488).
+-define(wxPreviewControlBar_GetZoomControl, 2489).
+-define(wxPreviewControlBar_SetZoomControl, 2490).
+-define(wxPrinter_new, 2492).
+-define(wxPrinter_CreateAbortWindow, 2493).
+-define(wxPrinter_GetAbort, 2494).
+-define(wxPrinter_GetLastError, 2495).
+-define(wxPrinter_GetPrintDialogData, 2496).
+-define(wxPrinter_Print, 2497).
+-define(wxPrinter_PrintDialog, 2498).
+-define(wxPrinter_ReportError, 2499).
+-define(wxPrinter_Setup, 2500).
+-define(wxPrinter_destroy, 2501).
+-define(wxXmlResource_new_1, 2502).
+-define(wxXmlResource_new_2, 2503).
+-define(wxXmlResource_destruct, 2504).
+-define(wxXmlResource_AttachUnknownControl, 2505).
+-define(wxXmlResource_ClearHandlers, 2506).
+-define(wxXmlResource_CompareVersion, 2507).
+-define(wxXmlResource_Get, 2508).
+-define(wxXmlResource_GetFlags, 2509).
+-define(wxXmlResource_GetVersion, 2510).
+-define(wxXmlResource_GetXRCID, 2511).
+-define(wxXmlResource_InitAllHandlers, 2512).
+-define(wxXmlResource_Load, 2513).
+-define(wxXmlResource_LoadBitmap, 2514).
+-define(wxXmlResource_LoadDialog_2, 2515).
+-define(wxXmlResource_LoadDialog_3, 2516).
+-define(wxXmlResource_LoadFrame_2, 2517).
+-define(wxXmlResource_LoadFrame_3, 2518).
+-define(wxXmlResource_LoadIcon, 2519).
+-define(wxXmlResource_LoadMenu, 2520).
+-define(wxXmlResource_LoadMenuBar_2, 2521).
+-define(wxXmlResource_LoadMenuBar_1, 2522).
+-define(wxXmlResource_LoadPanel_2, 2523).
+-define(wxXmlResource_LoadPanel_3, 2524).
+-define(wxXmlResource_LoadToolBar, 2525).
+-define(wxXmlResource_Set, 2526).
+-define(wxXmlResource_SetFlags, 2527).
+-define(wxXmlResource_Unload, 2528).
+-define(wxXmlResource_xrcctrl, 2529).
+-define(wxHtmlEasyPrinting_new, 2530).
+-define(wxHtmlEasyPrinting_destruct, 2531).
+-define(wxHtmlEasyPrinting_GetPrintData, 2532).
+-define(wxHtmlEasyPrinting_GetPageSetupData, 2533).
+-define(wxHtmlEasyPrinting_PreviewFile, 2534).
+-define(wxHtmlEasyPrinting_PreviewText, 2535).
+-define(wxHtmlEasyPrinting_PrintFile, 2536).
+-define(wxHtmlEasyPrinting_PrintText, 2537).
+-define(wxHtmlEasyPrinting_PageSetup, 2538).
+-define(wxHtmlEasyPrinting_SetFonts, 2539).
+-define(wxHtmlEasyPrinting_SetHeader, 2540).
+-define(wxHtmlEasyPrinting_SetFooter, 2541).
+-define(wxGLCanvas_new_2, 2543).
+-define(wxGLCanvas_new_3_1, 2544).
+-define(wxGLCanvas_new_3_0, 2545).
+-define(wxGLCanvas_GetContext, 2546).
+-define(wxGLCanvas_SetCurrent, 2548).
+-define(wxGLCanvas_SwapBuffers, 2549).
+-define(wxGLCanvas_destroy, 2550).
+-define(wxAuiManager_new, 2551).
+-define(wxAuiManager_destruct, 2552).
+-define(wxAuiManager_AddPane_2_1, 2553).
+-define(wxAuiManager_AddPane_3, 2554).
+-define(wxAuiManager_AddPane_2_0, 2555).
+-define(wxAuiManager_DetachPane, 2556).
+-define(wxAuiManager_GetAllPanes, 2557).
+-define(wxAuiManager_GetArtProvider, 2558).
+-define(wxAuiManager_GetDockSizeConstraint, 2559).
+-define(wxAuiManager_GetFlags, 2560).
+-define(wxAuiManager_GetManagedWindow, 2561).
+-define(wxAuiManager_GetManager, 2562).
+-define(wxAuiManager_GetPane_1_1, 2563).
+-define(wxAuiManager_GetPane_1_0, 2564).
+-define(wxAuiManager_HideHint, 2565).
+-define(wxAuiManager_InsertPane, 2566).
+-define(wxAuiManager_LoadPaneInfo, 2567).
+-define(wxAuiManager_LoadPerspective, 2568).
+-define(wxAuiManager_SavePaneInfo, 2569).
+-define(wxAuiManager_SavePerspective, 2570).
+-define(wxAuiManager_SetArtProvider, 2571).
+-define(wxAuiManager_SetDockSizeConstraint, 2572).
+-define(wxAuiManager_SetFlags, 2573).
+-define(wxAuiManager_SetManagedWindow, 2574).
+-define(wxAuiManager_ShowHint, 2575).
+-define(wxAuiManager_UnInit, 2576).
+-define(wxAuiManager_Update, 2577).
+-define(wxAuiPaneInfo_new_0, 2578).
+-define(wxAuiPaneInfo_new_1, 2579).
+-define(wxAuiPaneInfo_destruct, 2580).
+-define(wxAuiPaneInfo_BestSize_1, 2581).
+-define(wxAuiPaneInfo_BestSize_2, 2582).
+-define(wxAuiPaneInfo_Bottom, 2583).
+-define(wxAuiPaneInfo_BottomDockable, 2584).
+-define(wxAuiPaneInfo_Caption, 2585).
+-define(wxAuiPaneInfo_CaptionVisible, 2586).
+-define(wxAuiPaneInfo_Centre, 2587).
+-define(wxAuiPaneInfo_CentrePane, 2588).
+-define(wxAuiPaneInfo_CloseButton, 2589).
+-define(wxAuiPaneInfo_DefaultPane, 2590).
+-define(wxAuiPaneInfo_DestroyOnClose, 2591).
+-define(wxAuiPaneInfo_Direction, 2592).
+-define(wxAuiPaneInfo_Dock, 2593).
+-define(wxAuiPaneInfo_Dockable, 2594).
+-define(wxAuiPaneInfo_Fixed, 2595).
+-define(wxAuiPaneInfo_Float, 2596).
+-define(wxAuiPaneInfo_Floatable, 2597).
+-define(wxAuiPaneInfo_FloatingPosition_1, 2598).
+-define(wxAuiPaneInfo_FloatingPosition_2, 2599).
+-define(wxAuiPaneInfo_FloatingSize_1, 2600).
+-define(wxAuiPaneInfo_FloatingSize_2, 2601).
+-define(wxAuiPaneInfo_Gripper, 2602).
+-define(wxAuiPaneInfo_GripperTop, 2603).
+-define(wxAuiPaneInfo_HasBorder, 2604).
+-define(wxAuiPaneInfo_HasCaption, 2605).
+-define(wxAuiPaneInfo_HasCloseButton, 2606).
+-define(wxAuiPaneInfo_HasFlag, 2607).
+-define(wxAuiPaneInfo_HasGripper, 2608).
+-define(wxAuiPaneInfo_HasGripperTop, 2609).
+-define(wxAuiPaneInfo_HasMaximizeButton, 2610).
+-define(wxAuiPaneInfo_HasMinimizeButton, 2611).
+-define(wxAuiPaneInfo_HasPinButton, 2612).
+-define(wxAuiPaneInfo_Hide, 2613).
+-define(wxAuiPaneInfo_IsBottomDockable, 2614).
+-define(wxAuiPaneInfo_IsDocked, 2615).
+-define(wxAuiPaneInfo_IsFixed, 2616).
+-define(wxAuiPaneInfo_IsFloatable, 2617).
+-define(wxAuiPaneInfo_IsFloating, 2618).
+-define(wxAuiPaneInfo_IsLeftDockable, 2619).
+-define(wxAuiPaneInfo_IsMovable, 2620).
+-define(wxAuiPaneInfo_IsOk, 2621).
+-define(wxAuiPaneInfo_IsResizable, 2622).
+-define(wxAuiPaneInfo_IsRightDockable, 2623).
+-define(wxAuiPaneInfo_IsShown, 2624).
+-define(wxAuiPaneInfo_IsToolbar, 2625).
+-define(wxAuiPaneInfo_IsTopDockable, 2626).
+-define(wxAuiPaneInfo_Layer, 2627).
+-define(wxAuiPaneInfo_Left, 2628).
+-define(wxAuiPaneInfo_LeftDockable, 2629).
+-define(wxAuiPaneInfo_MaxSize_1, 2630).
+-define(wxAuiPaneInfo_MaxSize_2, 2631).
+-define(wxAuiPaneInfo_MaximizeButton, 2632).
+-define(wxAuiPaneInfo_MinSize_1, 2633).
+-define(wxAuiPaneInfo_MinSize_2, 2634).
+-define(wxAuiPaneInfo_MinimizeButton, 2635).
+-define(wxAuiPaneInfo_Movable, 2636).
+-define(wxAuiPaneInfo_Name, 2637).
+-define(wxAuiPaneInfo_PaneBorder, 2638).
+-define(wxAuiPaneInfo_PinButton, 2639).
+-define(wxAuiPaneInfo_Position, 2640).
+-define(wxAuiPaneInfo_Resizable, 2641).
+-define(wxAuiPaneInfo_Right, 2642).
+-define(wxAuiPaneInfo_RightDockable, 2643).
+-define(wxAuiPaneInfo_Row, 2644).
+-define(wxAuiPaneInfo_SafeSet, 2645).
+-define(wxAuiPaneInfo_SetFlag, 2646).
+-define(wxAuiPaneInfo_Show, 2647).
+-define(wxAuiPaneInfo_ToolbarPane, 2648).
+-define(wxAuiPaneInfo_Top, 2649).
+-define(wxAuiPaneInfo_TopDockable, 2650).
+-define(wxAuiPaneInfo_Window, 2651).
+-define(wxAuiNotebook_new_0, 2652).
+-define(wxAuiNotebook_new_2, 2653).
+-define(wxAuiNotebook_AddPage, 2654).
+-define(wxAuiNotebook_Create, 2655).
+-define(wxAuiNotebook_DeletePage, 2656).
+-define(wxAuiNotebook_GetArtProvider, 2657).
+-define(wxAuiNotebook_GetPage, 2658).
+-define(wxAuiNotebook_GetPageBitmap, 2659).
+-define(wxAuiNotebook_GetPageCount, 2660).
+-define(wxAuiNotebook_GetPageIndex, 2661).
+-define(wxAuiNotebook_GetPageText, 2662).
+-define(wxAuiNotebook_GetSelection, 2663).
+-define(wxAuiNotebook_InsertPage, 2664).
+-define(wxAuiNotebook_RemovePage, 2665).
+-define(wxAuiNotebook_SetArtProvider, 2666).
+-define(wxAuiNotebook_SetFont, 2667).
+-define(wxAuiNotebook_SetPageBitmap, 2668).
+-define(wxAuiNotebook_SetPageText, 2669).
+-define(wxAuiNotebook_SetSelection, 2670).
+-define(wxAuiNotebook_SetTabCtrlHeight, 2671).
+-define(wxAuiNotebook_SetUniformBitmapSize, 2672).
+-define(wxAuiNotebook_destroy, 2673).
+-define(wxMDIParentFrame_new_0, 2674).
+-define(wxMDIParentFrame_new_4, 2675).
+-define(wxMDIParentFrame_destruct, 2676).
+-define(wxMDIParentFrame_ActivateNext, 2677).
+-define(wxMDIParentFrame_ActivatePrevious, 2678).
+-define(wxMDIParentFrame_ArrangeIcons, 2679).
+-define(wxMDIParentFrame_Cascade, 2680).
+-define(wxMDIParentFrame_Create, 2681).
+-define(wxMDIParentFrame_GetActiveChild, 2682).
+-define(wxMDIParentFrame_GetClientWindow, 2683).
+-define(wxMDIParentFrame_Tile, 2684).
+-define(wxMDIChildFrame_new_0, 2685).
+-define(wxMDIChildFrame_new_4, 2686).
+-define(wxMDIChildFrame_destruct, 2687).
+-define(wxMDIChildFrame_Activate, 2688).
+-define(wxMDIChildFrame_Create, 2689).
+-define(wxMDIChildFrame_Maximize, 2690).
+-define(wxMDIChildFrame_Restore, 2691).
+-define(wxMDIClientWindow_new_0, 2692).
+-define(wxMDIClientWindow_new_2, 2693).
+-define(wxMDIClientWindow_destruct, 2694).
+-define(wxMDIClientWindow_CreateClient, 2695).
+-define(wxLayoutAlgorithm_new, 2696).
+-define(wxLayoutAlgorithm_LayoutFrame, 2697).
+-define(wxLayoutAlgorithm_LayoutMDIFrame, 2698).
+-define(wxLayoutAlgorithm_LayoutWindow, 2699).
+-define(wxLayoutAlgorithm_destroy, 2700).
+-define(wxEvent_GetId, 2701).
+-define(wxEvent_GetSkipped, 2702).
+-define(wxEvent_GetTimestamp, 2703).
+-define(wxEvent_IsCommandEvent, 2704).
+-define(wxEvent_ResumePropagation, 2705).
+-define(wxEvent_ShouldPropagate, 2706).
+-define(wxEvent_Skip, 2707).
+-define(wxEvent_StopPropagation, 2708).
+-define(wxCommandEvent_getClientData, 2709).
+-define(wxCommandEvent_GetExtraLong, 2710).
+-define(wxCommandEvent_GetInt, 2711).
+-define(wxCommandEvent_GetSelection, 2712).
+-define(wxCommandEvent_GetString, 2713).
+-define(wxCommandEvent_IsChecked, 2714).
+-define(wxCommandEvent_IsSelection, 2715).
+-define(wxCommandEvent_SetInt, 2716).
+-define(wxCommandEvent_SetString, 2717).
+-define(wxScrollEvent_GetOrientation, 2718).
+-define(wxScrollEvent_GetPosition, 2719).
+-define(wxScrollWinEvent_GetOrientation, 2720).
+-define(wxScrollWinEvent_GetPosition, 2721).
+-define(wxMouseEvent_AltDown, 2722).
+-define(wxMouseEvent_Button, 2723).
+-define(wxMouseEvent_ButtonDClick, 2724).
+-define(wxMouseEvent_ButtonDown, 2725).
+-define(wxMouseEvent_ButtonUp, 2726).
+-define(wxMouseEvent_CmdDown, 2727).
+-define(wxMouseEvent_ControlDown, 2728).
+-define(wxMouseEvent_Dragging, 2729).
+-define(wxMouseEvent_Entering, 2730).
+-define(wxMouseEvent_GetButton, 2731).
+-define(wxMouseEvent_GetPosition, 2734).
+-define(wxMouseEvent_GetLogicalPosition, 2735).
+-define(wxMouseEvent_GetLinesPerAction, 2736).
+-define(wxMouseEvent_GetWheelRotation, 2737).
+-define(wxMouseEvent_GetWheelDelta, 2738).
+-define(wxMouseEvent_GetX, 2739).
+-define(wxMouseEvent_GetY, 2740).
+-define(wxMouseEvent_IsButton, 2741).
+-define(wxMouseEvent_IsPageScroll, 2742).
+-define(wxMouseEvent_Leaving, 2743).
+-define(wxMouseEvent_LeftDClick, 2744).
+-define(wxMouseEvent_LeftDown, 2745).
+-define(wxMouseEvent_LeftIsDown, 2746).
+-define(wxMouseEvent_LeftUp, 2747).
+-define(wxMouseEvent_MetaDown, 2748).
+-define(wxMouseEvent_MiddleDClick, 2749).
+-define(wxMouseEvent_MiddleDown, 2750).
+-define(wxMouseEvent_MiddleIsDown, 2751).
+-define(wxMouseEvent_MiddleUp, 2752).
+-define(wxMouseEvent_Moving, 2753).
+-define(wxMouseEvent_RightDClick, 2754).
+-define(wxMouseEvent_RightDown, 2755).
+-define(wxMouseEvent_RightIsDown, 2756).
+-define(wxMouseEvent_RightUp, 2757).
+-define(wxMouseEvent_ShiftDown, 2758).
+-define(wxSetCursorEvent_GetCursor, 2759).
+-define(wxSetCursorEvent_GetX, 2760).
+-define(wxSetCursorEvent_GetY, 2761).
+-define(wxSetCursorEvent_HasCursor, 2762).
+-define(wxSetCursorEvent_SetCursor, 2763).
+-define(wxKeyEvent_AltDown, 2764).
+-define(wxKeyEvent_CmdDown, 2765).
+-define(wxKeyEvent_ControlDown, 2766).
+-define(wxKeyEvent_GetKeyCode, 2767).
+-define(wxKeyEvent_GetModifiers, 2768).
+-define(wxKeyEvent_GetPosition, 2771).
+-define(wxKeyEvent_GetRawKeyCode, 2772).
+-define(wxKeyEvent_GetRawKeyFlags, 2773).
+-define(wxKeyEvent_GetUnicodeKey, 2774).
+-define(wxKeyEvent_GetX, 2775).
+-define(wxKeyEvent_GetY, 2776).
+-define(wxKeyEvent_HasModifiers, 2777).
+-define(wxKeyEvent_MetaDown, 2778).
+-define(wxKeyEvent_ShiftDown, 2779).
+-define(wxSizeEvent_GetSize, 2780).
+-define(wxMoveEvent_GetPosition, 2781).
+-define(wxEraseEvent_GetDC, 2782).
+-define(wxFocusEvent_GetWindow, 2783).
+-define(wxChildFocusEvent_GetWindow, 2784).
+-define(wxMenuEvent_GetMenu, 2785).
+-define(wxMenuEvent_GetMenuId, 2786).
+-define(wxMenuEvent_IsPopup, 2787).
+-define(wxCloseEvent_CanVeto, 2788).
+-define(wxCloseEvent_GetLoggingOff, 2789).
+-define(wxCloseEvent_SetCanVeto, 2790).
+-define(wxCloseEvent_SetLoggingOff, 2791).
+-define(wxCloseEvent_Veto, 2792).
+-define(wxShowEvent_SetShow, 2793).
+-define(wxShowEvent_GetShow, 2794).
+-define(wxIconizeEvent_Iconized, 2795).
+-define(wxJoystickEvent_ButtonDown, 2796).
+-define(wxJoystickEvent_ButtonIsDown, 2797).
+-define(wxJoystickEvent_ButtonUp, 2798).
+-define(wxJoystickEvent_GetButtonChange, 2799).
+-define(wxJoystickEvent_GetButtonState, 2800).
+-define(wxJoystickEvent_GetJoystick, 2801).
+-define(wxJoystickEvent_GetPosition, 2802).
+-define(wxJoystickEvent_GetZPosition, 2803).
+-define(wxJoystickEvent_IsButton, 2804).
+-define(wxJoystickEvent_IsMove, 2805).
+-define(wxJoystickEvent_IsZMove, 2806).
+-define(wxUpdateUIEvent_CanUpdate, 2807).
+-define(wxUpdateUIEvent_Check, 2808).
+-define(wxUpdateUIEvent_Enable, 2809).
+-define(wxUpdateUIEvent_Show, 2810).
+-define(wxUpdateUIEvent_GetChecked, 2811).
+-define(wxUpdateUIEvent_GetEnabled, 2812).
+-define(wxUpdateUIEvent_GetShown, 2813).
+-define(wxUpdateUIEvent_GetSetChecked, 2814).
+-define(wxUpdateUIEvent_GetSetEnabled, 2815).
+-define(wxUpdateUIEvent_GetSetShown, 2816).
+-define(wxUpdateUIEvent_GetSetText, 2817).
+-define(wxUpdateUIEvent_GetText, 2818).
+-define(wxUpdateUIEvent_GetMode, 2819).
+-define(wxUpdateUIEvent_GetUpdateInterval, 2820).
+-define(wxUpdateUIEvent_ResetUpdateTime, 2821).
+-define(wxUpdateUIEvent_SetMode, 2822).
+-define(wxUpdateUIEvent_SetText, 2823).
+-define(wxUpdateUIEvent_SetUpdateInterval, 2824).
+-define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2825).
+-define(wxPaletteChangedEvent_SetChangedWindow, 2826).
+-define(wxPaletteChangedEvent_GetChangedWindow, 2827).
+-define(wxQueryNewPaletteEvent_SetPaletteRealized, 2828).
+-define(wxQueryNewPaletteEvent_GetPaletteRealized, 2829).
+-define(wxNavigationKeyEvent_GetDirection, 2830).
+-define(wxNavigationKeyEvent_SetDirection, 2831).
+-define(wxNavigationKeyEvent_IsWindowChange, 2832).
+-define(wxNavigationKeyEvent_SetWindowChange, 2833).
+-define(wxNavigationKeyEvent_IsFromTab, 2834).
+-define(wxNavigationKeyEvent_SetFromTab, 2835).
+-define(wxNavigationKeyEvent_GetCurrentFocus, 2836).
+-define(wxNavigationKeyEvent_SetCurrentFocus, 2837).
+-define(wxHelpEvent_GetOrigin, 2838).
+-define(wxHelpEvent_GetPosition, 2839).
+-define(wxHelpEvent_SetOrigin, 2840).
+-define(wxHelpEvent_SetPosition, 2841).
+-define(wxContextMenuEvent_GetPosition, 2842).
+-define(wxContextMenuEvent_SetPosition, 2843).
+-define(wxIdleEvent_CanSend, 2844).
+-define(wxIdleEvent_GetMode, 2845).
+-define(wxIdleEvent_RequestMore, 2846).
+-define(wxIdleEvent_MoreRequested, 2847).
+-define(wxIdleEvent_SetMode, 2848).
+-define(wxGridEvent_AltDown, 2849).
+-define(wxGridEvent_ControlDown, 2850).
+-define(wxGridEvent_GetCol, 2851).
+-define(wxGridEvent_GetPosition, 2852).
+-define(wxGridEvent_GetRow, 2853).
+-define(wxGridEvent_MetaDown, 2854).
+-define(wxGridEvent_Selecting, 2855).
+-define(wxGridEvent_ShiftDown, 2856).
+-define(wxNotifyEvent_Allow, 2857).
+-define(wxNotifyEvent_IsAllowed, 2858).
+-define(wxNotifyEvent_Veto, 2859).
+-define(wxSashEvent_GetEdge, 2860).
+-define(wxSashEvent_GetDragRect, 2861).
+-define(wxSashEvent_GetDragStatus, 2862).
+-define(wxListEvent_GetCacheFrom, 2863).
+-define(wxListEvent_GetCacheTo, 2864).
+-define(wxListEvent_GetKeyCode, 2865).
+-define(wxListEvent_GetIndex, 2866).
+-define(wxListEvent_GetColumn, 2867).
+-define(wxListEvent_GetPoint, 2868).
+-define(wxListEvent_GetLabel, 2869).
+-define(wxListEvent_GetText, 2870).
+-define(wxListEvent_GetImage, 2871).
+-define(wxListEvent_GetData, 2872).
+-define(wxListEvent_GetMask, 2873).
+-define(wxListEvent_GetItem, 2874).
+-define(wxListEvent_IsEditCancelled, 2875).
+-define(wxDateEvent_GetDate, 2876).
+-define(wxCalendarEvent_GetWeekDay, 2877).
+-define(wxFileDirPickerEvent_GetPath, 2878).
+-define(wxColourPickerEvent_GetColour, 2879).
+-define(wxFontPickerEvent_GetFont, 2880).
+-define(wxStyledTextEvent_GetPosition, 2881).
+-define(wxStyledTextEvent_GetKey, 2882).
+-define(wxStyledTextEvent_GetModifiers, 2883).
+-define(wxStyledTextEvent_GetModificationType, 2884).
+-define(wxStyledTextEvent_GetText, 2885).
+-define(wxStyledTextEvent_GetLength, 2886).
+-define(wxStyledTextEvent_GetLinesAdded, 2887).
+-define(wxStyledTextEvent_GetLine, 2888).
+-define(wxStyledTextEvent_GetFoldLevelNow, 2889).
+-define(wxStyledTextEvent_GetFoldLevelPrev, 2890).
+-define(wxStyledTextEvent_GetMargin, 2891).
+-define(wxStyledTextEvent_GetMessage, 2892).
+-define(wxStyledTextEvent_GetWParam, 2893).
+-define(wxStyledTextEvent_GetLParam, 2894).
+-define(wxStyledTextEvent_GetListType, 2895).
+-define(wxStyledTextEvent_GetX, 2896).
+-define(wxStyledTextEvent_GetY, 2897).
+-define(wxStyledTextEvent_GetDragText, 2898).
+-define(wxStyledTextEvent_GetDragAllowMove, 2899).
+-define(wxStyledTextEvent_GetDragResult, 2900).
+-define(wxStyledTextEvent_GetShift, 2901).
+-define(wxStyledTextEvent_GetControl, 2902).
+-define(wxStyledTextEvent_GetAlt, 2903).
+-define(utils_wxGetKeyState, 2904).
+-define(utils_wxGetMousePosition, 2905).
+-define(utils_wxGetMouseState, 2906).
+-define(utils_wxSetDetectableAutoRepeat, 2907).
+-define(utils_wxBell, 2908).
+-define(utils_wxFindMenuItemId, 2909).
+-define(utils_wxGenericFindWindowAtPoint, 2910).
+-define(utils_wxFindWindowAtPoint, 2911).
+-define(utils_wxBeginBusyCursor, 2912).
+-define(utils_wxEndBusyCursor, 2913).
+-define(utils_wxIsBusy, 2914).
+-define(utils_wxShutdown, 2915).
+-define(utils_wxShell, 2916).
+-define(utils_wxLaunchDefaultBrowser, 2917).
+-define(utils_wxGetEmailAddress, 2918).
+-define(utils_wxGetUserId, 2919).
+-define(utils_wxGetHomeDir, 2920).
+-define(utils_wxNewId, 2921).
+-define(utils_wxRegisterId, 2922).
+-define(utils_wxGetCurrentId, 2923).
+-define(utils_wxGetOsDescription, 2924).
+-define(utils_wxIsPlatformLittleEndian, 2925).
+-define(utils_wxIsPlatform64Bit, 2926).
+-define(wxPrintout_new, 2927).
+-define(wxPrintout_destruct, 2928).
+-define(wxPrintout_GetDC, 2929).
+-define(wxPrintout_GetPageSizeMM, 2930).
+-define(wxPrintout_GetPageSizePixels, 2931).
+-define(wxPrintout_GetPaperRectPixels, 2932).
+-define(wxPrintout_GetPPIPrinter, 2933).
+-define(wxPrintout_GetPPIScreen, 2934).
+-define(wxPrintout_GetTitle, 2935).
+-define(wxPrintout_IsPreview, 2936).
+-define(wxPrintout_FitThisSizeToPaper, 2937).
+-define(wxPrintout_FitThisSizeToPage, 2938).
+-define(wxPrintout_FitThisSizeToPageMargins, 2939).
+-define(wxPrintout_MapScreenSizeToPaper, 2940).
+-define(wxPrintout_MapScreenSizeToPage, 2941).
+-define(wxPrintout_MapScreenSizeToPageMargins, 2942).
+-define(wxPrintout_MapScreenSizeToDevice, 2943).
+-define(wxPrintout_GetLogicalPaperRect, 2944).
+-define(wxPrintout_GetLogicalPageRect, 2945).
+-define(wxPrintout_GetLogicalPageMarginsRect, 2946).
+-define(wxPrintout_SetLogicalOrigin, 2947).
+-define(wxPrintout_OffsetLogicalOrigin, 2948).
+-define(wxStyledTextCtrl_new_2, 2949).
+-define(wxStyledTextCtrl_new_0, 2950).
+-define(wxStyledTextCtrl_destruct, 2951).
+-define(wxStyledTextCtrl_Create, 2952).
+-define(wxStyledTextCtrl_AddText, 2953).
+-define(wxStyledTextCtrl_AddStyledText, 2954).
+-define(wxStyledTextCtrl_InsertText, 2955).
+-define(wxStyledTextCtrl_ClearAll, 2956).
+-define(wxStyledTextCtrl_ClearDocumentStyle, 2957).
+-define(wxStyledTextCtrl_GetLength, 2958).
+-define(wxStyledTextCtrl_GetCharAt, 2959).
+-define(wxStyledTextCtrl_GetCurrentPos, 2960).
+-define(wxStyledTextCtrl_GetAnchor, 2961).
+-define(wxStyledTextCtrl_GetStyleAt, 2962).
+-define(wxStyledTextCtrl_Redo, 2963).
+-define(wxStyledTextCtrl_SetUndoCollection, 2964).
+-define(wxStyledTextCtrl_SelectAll, 2965).
+-define(wxStyledTextCtrl_SetSavePoint, 2966).
+-define(wxStyledTextCtrl_GetStyledText, 2967).
+-define(wxStyledTextCtrl_CanRedo, 2968).
+-define(wxStyledTextCtrl_MarkerLineFromHandle, 2969).
+-define(wxStyledTextCtrl_MarkerDeleteHandle, 2970).
+-define(wxStyledTextCtrl_GetUndoCollection, 2971).
+-define(wxStyledTextCtrl_GetViewWhiteSpace, 2972).
+-define(wxStyledTextCtrl_SetViewWhiteSpace, 2973).
+-define(wxStyledTextCtrl_PositionFromPoint, 2974).
+-define(wxStyledTextCtrl_PositionFromPointClose, 2975).
+-define(wxStyledTextCtrl_GotoLine, 2976).
+-define(wxStyledTextCtrl_GotoPos, 2977).
+-define(wxStyledTextCtrl_SetAnchor, 2978).
+-define(wxStyledTextCtrl_GetCurLine, 2979).
+-define(wxStyledTextCtrl_GetEndStyled, 2980).
+-define(wxStyledTextCtrl_ConvertEOLs, 2981).
+-define(wxStyledTextCtrl_GetEOLMode, 2982).
+-define(wxStyledTextCtrl_SetEOLMode, 2983).
+-define(wxStyledTextCtrl_StartStyling, 2984).
+-define(wxStyledTextCtrl_SetStyling, 2985).
+-define(wxStyledTextCtrl_GetBufferedDraw, 2986).
+-define(wxStyledTextCtrl_SetBufferedDraw, 2987).
+-define(wxStyledTextCtrl_SetTabWidth, 2988).
+-define(wxStyledTextCtrl_GetTabWidth, 2989).
+-define(wxStyledTextCtrl_SetCodePage, 2990).
+-define(wxStyledTextCtrl_MarkerDefine, 2991).
+-define(wxStyledTextCtrl_MarkerSetForeground, 2992).
+-define(wxStyledTextCtrl_MarkerSetBackground, 2993).
+-define(wxStyledTextCtrl_MarkerAdd, 2994).
+-define(wxStyledTextCtrl_MarkerDelete, 2995).
+-define(wxStyledTextCtrl_MarkerDeleteAll, 2996).
+-define(wxStyledTextCtrl_MarkerGet, 2997).
+-define(wxStyledTextCtrl_MarkerNext, 2998).
+-define(wxStyledTextCtrl_MarkerPrevious, 2999).
+-define(wxStyledTextCtrl_MarkerDefineBitmap, 3000).
+-define(wxStyledTextCtrl_MarkerAddSet, 3001).
+-define(wxStyledTextCtrl_MarkerSetAlpha, 3002).
+-define(wxStyledTextCtrl_SetMarginType, 3003).
+-define(wxStyledTextCtrl_GetMarginType, 3004).
+-define(wxStyledTextCtrl_SetMarginWidth, 3005).
+-define(wxStyledTextCtrl_GetMarginWidth, 3006).
+-define(wxStyledTextCtrl_SetMarginMask, 3007).
+-define(wxStyledTextCtrl_GetMarginMask, 3008).
+-define(wxStyledTextCtrl_SetMarginSensitive, 3009).
+-define(wxStyledTextCtrl_GetMarginSensitive, 3010).
+-define(wxStyledTextCtrl_StyleClearAll, 3011).
+-define(wxStyledTextCtrl_StyleSetForeground, 3012).
+-define(wxStyledTextCtrl_StyleSetBackground, 3013).
+-define(wxStyledTextCtrl_StyleSetBold, 3014).
+-define(wxStyledTextCtrl_StyleSetItalic, 3015).
+-define(wxStyledTextCtrl_StyleSetSize, 3016).
+-define(wxStyledTextCtrl_StyleSetFaceName, 3017).
+-define(wxStyledTextCtrl_StyleSetEOLFilled, 3018).
+-define(wxStyledTextCtrl_StyleResetDefault, 3019).
+-define(wxStyledTextCtrl_StyleSetUnderline, 3020).
+-define(wxStyledTextCtrl_StyleSetCase, 3021).
+-define(wxStyledTextCtrl_StyleSetHotSpot, 3022).
+-define(wxStyledTextCtrl_SetSelForeground, 3023).
+-define(wxStyledTextCtrl_SetSelBackground, 3024).
+-define(wxStyledTextCtrl_GetSelAlpha, 3025).
+-define(wxStyledTextCtrl_SetSelAlpha, 3026).
+-define(wxStyledTextCtrl_SetCaretForeground, 3027).
+-define(wxStyledTextCtrl_CmdKeyAssign, 3028).
+-define(wxStyledTextCtrl_CmdKeyClear, 3029).
+-define(wxStyledTextCtrl_CmdKeyClearAll, 3030).
+-define(wxStyledTextCtrl_SetStyleBytes, 3031).
+-define(wxStyledTextCtrl_StyleSetVisible, 3032).
+-define(wxStyledTextCtrl_GetCaretPeriod, 3033).
+-define(wxStyledTextCtrl_SetCaretPeriod, 3034).
+-define(wxStyledTextCtrl_SetWordChars, 3035).
+-define(wxStyledTextCtrl_BeginUndoAction, 3036).
+-define(wxStyledTextCtrl_EndUndoAction, 3037).
+-define(wxStyledTextCtrl_IndicatorSetStyle, 3038).
+-define(wxStyledTextCtrl_IndicatorGetStyle, 3039).
+-define(wxStyledTextCtrl_IndicatorSetForeground, 3040).
+-define(wxStyledTextCtrl_IndicatorGetForeground, 3041).
+-define(wxStyledTextCtrl_SetWhitespaceForeground, 3042).
+-define(wxStyledTextCtrl_SetWhitespaceBackground, 3043).
+-define(wxStyledTextCtrl_GetStyleBits, 3044).
+-define(wxStyledTextCtrl_SetLineState, 3045).
+-define(wxStyledTextCtrl_GetLineState, 3046).
+-define(wxStyledTextCtrl_GetMaxLineState, 3047).
+-define(wxStyledTextCtrl_GetCaretLineVisible, 3048).
+-define(wxStyledTextCtrl_SetCaretLineVisible, 3049).
+-define(wxStyledTextCtrl_GetCaretLineBackground, 3050).
+-define(wxStyledTextCtrl_SetCaretLineBackground, 3051).
+-define(wxStyledTextCtrl_AutoCompShow, 3052).
+-define(wxStyledTextCtrl_AutoCompCancel, 3053).
+-define(wxStyledTextCtrl_AutoCompActive, 3054).
+-define(wxStyledTextCtrl_AutoCompPosStart, 3055).
+-define(wxStyledTextCtrl_AutoCompComplete, 3056).
+-define(wxStyledTextCtrl_AutoCompStops, 3057).
+-define(wxStyledTextCtrl_AutoCompSetSeparator, 3058).
+-define(wxStyledTextCtrl_AutoCompGetSeparator, 3059).
+-define(wxStyledTextCtrl_AutoCompSelect, 3060).
+-define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3061).
+-define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3062).
+-define(wxStyledTextCtrl_AutoCompSetFillUps, 3063).
+-define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3064).
+-define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3065).
+-define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3066).
+-define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3067).
+-define(wxStyledTextCtrl_UserListShow, 3068).
+-define(wxStyledTextCtrl_AutoCompSetAutoHide, 3069).
+-define(wxStyledTextCtrl_AutoCompGetAutoHide, 3070).
+-define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3071).
+-define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3072).
+-define(wxStyledTextCtrl_RegisterImage, 3073).
+-define(wxStyledTextCtrl_ClearRegisteredImages, 3074).
+-define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3075).
+-define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3076).
+-define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3077).
+-define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3078).
+-define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3079).
+-define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3080).
+-define(wxStyledTextCtrl_SetIndent, 3081).
+-define(wxStyledTextCtrl_GetIndent, 3082).
+-define(wxStyledTextCtrl_SetUseTabs, 3083).
+-define(wxStyledTextCtrl_GetUseTabs, 3084).
+-define(wxStyledTextCtrl_SetLineIndentation, 3085).
+-define(wxStyledTextCtrl_GetLineIndentation, 3086).
+-define(wxStyledTextCtrl_GetLineIndentPosition, 3087).
+-define(wxStyledTextCtrl_GetColumn, 3088).
+-define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3089).
+-define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3090).
+-define(wxStyledTextCtrl_SetIndentationGuides, 3091).
+-define(wxStyledTextCtrl_GetIndentationGuides, 3092).
+-define(wxStyledTextCtrl_SetHighlightGuide, 3093).
+-define(wxStyledTextCtrl_GetHighlightGuide, 3094).
+-define(wxStyledTextCtrl_GetLineEndPosition, 3095).
+-define(wxStyledTextCtrl_GetCodePage, 3096).
+-define(wxStyledTextCtrl_GetCaretForeground, 3097).
+-define(wxStyledTextCtrl_GetReadOnly, 3098).
+-define(wxStyledTextCtrl_SetCurrentPos, 3099).
+-define(wxStyledTextCtrl_SetSelectionStart, 3100).
+-define(wxStyledTextCtrl_GetSelectionStart, 3101).
+-define(wxStyledTextCtrl_SetSelectionEnd, 3102).
+-define(wxStyledTextCtrl_GetSelectionEnd, 3103).
+-define(wxStyledTextCtrl_SetPrintMagnification, 3104).
+-define(wxStyledTextCtrl_GetPrintMagnification, 3105).
+-define(wxStyledTextCtrl_SetPrintColourMode, 3106).
+-define(wxStyledTextCtrl_GetPrintColourMode, 3107).
+-define(wxStyledTextCtrl_FindText, 3108).
+-define(wxStyledTextCtrl_FormatRange, 3109).
+-define(wxStyledTextCtrl_GetFirstVisibleLine, 3110).
+-define(wxStyledTextCtrl_GetLine, 3111).
+-define(wxStyledTextCtrl_GetLineCount, 3112).
+-define(wxStyledTextCtrl_SetMarginLeft, 3113).
+-define(wxStyledTextCtrl_GetMarginLeft, 3114).
+-define(wxStyledTextCtrl_SetMarginRight, 3115).
+-define(wxStyledTextCtrl_GetMarginRight, 3116).
+-define(wxStyledTextCtrl_GetModify, 3117).
+-define(wxStyledTextCtrl_SetSelection, 3118).
+-define(wxStyledTextCtrl_GetSelectedText, 3119).
+-define(wxStyledTextCtrl_GetTextRange, 3120).
+-define(wxStyledTextCtrl_HideSelection, 3121).
+-define(wxStyledTextCtrl_LineFromPosition, 3122).
+-define(wxStyledTextCtrl_PositionFromLine, 3123).
+-define(wxStyledTextCtrl_LineScroll, 3124).
+-define(wxStyledTextCtrl_EnsureCaretVisible, 3125).
+-define(wxStyledTextCtrl_ReplaceSelection, 3126).
+-define(wxStyledTextCtrl_SetReadOnly, 3127).
+-define(wxStyledTextCtrl_CanPaste, 3128).
+-define(wxStyledTextCtrl_CanUndo, 3129).
+-define(wxStyledTextCtrl_EmptyUndoBuffer, 3130).
+-define(wxStyledTextCtrl_Undo, 3131).
+-define(wxStyledTextCtrl_Cut, 3132).
+-define(wxStyledTextCtrl_Copy, 3133).
+-define(wxStyledTextCtrl_Paste, 3134).
+-define(wxStyledTextCtrl_Clear, 3135).
+-define(wxStyledTextCtrl_SetText, 3136).
+-define(wxStyledTextCtrl_GetText, 3137).
+-define(wxStyledTextCtrl_GetTextLength, 3138).
+-define(wxStyledTextCtrl_GetOvertype, 3139).
+-define(wxStyledTextCtrl_SetCaretWidth, 3140).
+-define(wxStyledTextCtrl_GetCaretWidth, 3141).
+-define(wxStyledTextCtrl_SetTargetStart, 3142).
+-define(wxStyledTextCtrl_GetTargetStart, 3143).
+-define(wxStyledTextCtrl_SetTargetEnd, 3144).
+-define(wxStyledTextCtrl_GetTargetEnd, 3145).
+-define(wxStyledTextCtrl_ReplaceTarget, 3146).
+-define(wxStyledTextCtrl_SearchInTarget, 3147).
+-define(wxStyledTextCtrl_SetSearchFlags, 3148).
+-define(wxStyledTextCtrl_GetSearchFlags, 3149).
+-define(wxStyledTextCtrl_CallTipShow, 3150).
+-define(wxStyledTextCtrl_CallTipCancel, 3151).
+-define(wxStyledTextCtrl_CallTipActive, 3152).
+-define(wxStyledTextCtrl_CallTipPosAtStart, 3153).
+-define(wxStyledTextCtrl_CallTipSetHighlight, 3154).
+-define(wxStyledTextCtrl_CallTipSetBackground, 3155).
+-define(wxStyledTextCtrl_CallTipSetForeground, 3156).
+-define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3157).
+-define(wxStyledTextCtrl_CallTipUseStyle, 3158).
+-define(wxStyledTextCtrl_VisibleFromDocLine, 3159).
+-define(wxStyledTextCtrl_DocLineFromVisible, 3160).
+-define(wxStyledTextCtrl_WrapCount, 3161).
+-define(wxStyledTextCtrl_SetFoldLevel, 3162).
+-define(wxStyledTextCtrl_GetFoldLevel, 3163).
+-define(wxStyledTextCtrl_GetLastChild, 3164).
+-define(wxStyledTextCtrl_GetFoldParent, 3165).
+-define(wxStyledTextCtrl_ShowLines, 3166).
+-define(wxStyledTextCtrl_HideLines, 3167).
+-define(wxStyledTextCtrl_GetLineVisible, 3168).
+-define(wxStyledTextCtrl_SetFoldExpanded, 3169).
+-define(wxStyledTextCtrl_GetFoldExpanded, 3170).
+-define(wxStyledTextCtrl_ToggleFold, 3171).
+-define(wxStyledTextCtrl_EnsureVisible, 3172).
+-define(wxStyledTextCtrl_SetFoldFlags, 3173).
+-define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3174).
+-define(wxStyledTextCtrl_SetTabIndents, 3175).
+-define(wxStyledTextCtrl_GetTabIndents, 3176).
+-define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3177).
+-define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3178).
+-define(wxStyledTextCtrl_SetMouseDwellTime, 3179).
+-define(wxStyledTextCtrl_GetMouseDwellTime, 3180).
+-define(wxStyledTextCtrl_WordStartPosition, 3181).
+-define(wxStyledTextCtrl_WordEndPosition, 3182).
+-define(wxStyledTextCtrl_SetWrapMode, 3183).
+-define(wxStyledTextCtrl_GetWrapMode, 3184).
+-define(wxStyledTextCtrl_SetWrapVisualFlags, 3185).
+-define(wxStyledTextCtrl_GetWrapVisualFlags, 3186).
+-define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3187).
+-define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3188).
+-define(wxStyledTextCtrl_SetWrapStartIndent, 3189).
+-define(wxStyledTextCtrl_GetWrapStartIndent, 3190).
+-define(wxStyledTextCtrl_SetLayoutCache, 3191).
+-define(wxStyledTextCtrl_GetLayoutCache, 3192).
+-define(wxStyledTextCtrl_SetScrollWidth, 3193).
+-define(wxStyledTextCtrl_GetScrollWidth, 3194).
+-define(wxStyledTextCtrl_TextWidth, 3195).
+-define(wxStyledTextCtrl_GetEndAtLastLine, 3196).
+-define(wxStyledTextCtrl_TextHeight, 3197).
+-define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3198).
+-define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3199).
+-define(wxStyledTextCtrl_AppendText, 3200).
+-define(wxStyledTextCtrl_GetTwoPhaseDraw, 3201).
+-define(wxStyledTextCtrl_SetTwoPhaseDraw, 3202).
+-define(wxStyledTextCtrl_TargetFromSelection, 3203).
+-define(wxStyledTextCtrl_LinesJoin, 3204).
+-define(wxStyledTextCtrl_LinesSplit, 3205).
+-define(wxStyledTextCtrl_SetFoldMarginColour, 3206).
+-define(wxStyledTextCtrl_SetFoldMarginHiColour, 3207).
+-define(wxStyledTextCtrl_LineDown, 3208).
+-define(wxStyledTextCtrl_LineDownExtend, 3209).
+-define(wxStyledTextCtrl_LineUp, 3210).
+-define(wxStyledTextCtrl_LineUpExtend, 3211).
+-define(wxStyledTextCtrl_CharLeft, 3212).
+-define(wxStyledTextCtrl_CharLeftExtend, 3213).
+-define(wxStyledTextCtrl_CharRight, 3214).
+-define(wxStyledTextCtrl_CharRightExtend, 3215).
+-define(wxStyledTextCtrl_WordLeft, 3216).
+-define(wxStyledTextCtrl_WordLeftExtend, 3217).
+-define(wxStyledTextCtrl_WordRight, 3218).
+-define(wxStyledTextCtrl_WordRightExtend, 3219).
+-define(wxStyledTextCtrl_Home, 3220).
+-define(wxStyledTextCtrl_HomeExtend, 3221).
+-define(wxStyledTextCtrl_LineEnd, 3222).
+-define(wxStyledTextCtrl_LineEndExtend, 3223).
+-define(wxStyledTextCtrl_DocumentStart, 3224).
+-define(wxStyledTextCtrl_DocumentStartExtend, 3225).
+-define(wxStyledTextCtrl_DocumentEnd, 3226).
+-define(wxStyledTextCtrl_DocumentEndExtend, 3227).
+-define(wxStyledTextCtrl_PageUp, 3228).
+-define(wxStyledTextCtrl_PageUpExtend, 3229).
+-define(wxStyledTextCtrl_PageDown, 3230).
+-define(wxStyledTextCtrl_PageDownExtend, 3231).
+-define(wxStyledTextCtrl_EditToggleOvertype, 3232).
+-define(wxStyledTextCtrl_Cancel, 3233).
+-define(wxStyledTextCtrl_DeleteBack, 3234).
+-define(wxStyledTextCtrl_Tab, 3235).
+-define(wxStyledTextCtrl_BackTab, 3236).
+-define(wxStyledTextCtrl_NewLine, 3237).
+-define(wxStyledTextCtrl_FormFeed, 3238).
+-define(wxStyledTextCtrl_VCHome, 3239).
+-define(wxStyledTextCtrl_VCHomeExtend, 3240).
+-define(wxStyledTextCtrl_ZoomIn, 3241).
+-define(wxStyledTextCtrl_ZoomOut, 3242).
+-define(wxStyledTextCtrl_DelWordLeft, 3243).
+-define(wxStyledTextCtrl_DelWordRight, 3244).
+-define(wxStyledTextCtrl_LineCut, 3245).
+-define(wxStyledTextCtrl_LineDelete, 3246).
+-define(wxStyledTextCtrl_LineTranspose, 3247).
+-define(wxStyledTextCtrl_LineDuplicate, 3248).
+-define(wxStyledTextCtrl_LowerCase, 3249).
+-define(wxStyledTextCtrl_UpperCase, 3250).
+-define(wxStyledTextCtrl_LineScrollDown, 3251).
+-define(wxStyledTextCtrl_LineScrollUp, 3252).
+-define(wxStyledTextCtrl_DeleteBackNotLine, 3253).
+-define(wxStyledTextCtrl_HomeDisplay, 3254).
+-define(wxStyledTextCtrl_HomeDisplayExtend, 3255).
+-define(wxStyledTextCtrl_LineEndDisplay, 3256).
+-define(wxStyledTextCtrl_LineEndDisplayExtend, 3257).
+-define(wxStyledTextCtrl_HomeWrapExtend, 3258).
+-define(wxStyledTextCtrl_LineEndWrap, 3259).
+-define(wxStyledTextCtrl_LineEndWrapExtend, 3260).
+-define(wxStyledTextCtrl_VCHomeWrap, 3261).
+-define(wxStyledTextCtrl_VCHomeWrapExtend, 3262).
+-define(wxStyledTextCtrl_LineCopy, 3263).
+-define(wxStyledTextCtrl_MoveCaretInsideView, 3264).
+-define(wxStyledTextCtrl_LineLength, 3265).
+-define(wxStyledTextCtrl_BraceHighlight, 3266).
+-define(wxStyledTextCtrl_BraceBadLight, 3267).
+-define(wxStyledTextCtrl_BraceMatch, 3268).
+-define(wxStyledTextCtrl_GetViewEOL, 3269).
+-define(wxStyledTextCtrl_SetViewEOL, 3270).
+-define(wxStyledTextCtrl_SetModEventMask, 3271).
+-define(wxStyledTextCtrl_GetEdgeColumn, 3272).
+-define(wxStyledTextCtrl_SetEdgeColumn, 3273).
+-define(wxStyledTextCtrl_SetEdgeMode, 3274).
+-define(wxStyledTextCtrl_GetEdgeMode, 3275).
+-define(wxStyledTextCtrl_GetEdgeColour, 3276).
+-define(wxStyledTextCtrl_SetEdgeColour, 3277).
+-define(wxStyledTextCtrl_SearchAnchor, 3278).
+-define(wxStyledTextCtrl_SearchNext, 3279).
+-define(wxStyledTextCtrl_SearchPrev, 3280).
+-define(wxStyledTextCtrl_LinesOnScreen, 3281).
+-define(wxStyledTextCtrl_UsePopUp, 3282).
+-define(wxStyledTextCtrl_SelectionIsRectangle, 3283).
+-define(wxStyledTextCtrl_SetZoom, 3284).
+-define(wxStyledTextCtrl_GetZoom, 3285).
+-define(wxStyledTextCtrl_GetModEventMask, 3286).
+-define(wxStyledTextCtrl_SetSTCFocus, 3287).
+-define(wxStyledTextCtrl_GetSTCFocus, 3288).
+-define(wxStyledTextCtrl_SetStatus, 3289).
+-define(wxStyledTextCtrl_GetStatus, 3290).
+-define(wxStyledTextCtrl_SetMouseDownCaptures, 3291).
+-define(wxStyledTextCtrl_GetMouseDownCaptures, 3292).
+-define(wxStyledTextCtrl_SetSTCCursor, 3293).
+-define(wxStyledTextCtrl_GetSTCCursor, 3294).
+-define(wxStyledTextCtrl_SetControlCharSymbol, 3295).
+-define(wxStyledTextCtrl_GetControlCharSymbol, 3296).
+-define(wxStyledTextCtrl_WordPartLeft, 3297).
+-define(wxStyledTextCtrl_WordPartLeftExtend, 3298).
+-define(wxStyledTextCtrl_WordPartRight, 3299).
+-define(wxStyledTextCtrl_WordPartRightExtend, 3300).
+-define(wxStyledTextCtrl_SetVisiblePolicy, 3301).
+-define(wxStyledTextCtrl_DelLineLeft, 3302).
+-define(wxStyledTextCtrl_DelLineRight, 3303).
+-define(wxStyledTextCtrl_GetXOffset, 3304).
+-define(wxStyledTextCtrl_ChooseCaretX, 3305).
+-define(wxStyledTextCtrl_SetXCaretPolicy, 3306).
+-define(wxStyledTextCtrl_SetYCaretPolicy, 3307).
+-define(wxStyledTextCtrl_GetPrintWrapMode, 3308).
+-define(wxStyledTextCtrl_SetHotspotActiveForeground, 3309).
+-define(wxStyledTextCtrl_SetHotspotActiveBackground, 3310).
+-define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3311).
+-define(wxStyledTextCtrl_SetHotspotSingleLine, 3312).
+-define(wxStyledTextCtrl_ParaDownExtend, 3313).
+-define(wxStyledTextCtrl_ParaUp, 3314).
+-define(wxStyledTextCtrl_ParaUpExtend, 3315).
+-define(wxStyledTextCtrl_PositionBefore, 3316).
+-define(wxStyledTextCtrl_PositionAfter, 3317).
+-define(wxStyledTextCtrl_CopyRange, 3318).
+-define(wxStyledTextCtrl_CopyText, 3319).
+-define(wxStyledTextCtrl_SetSelectionMode, 3320).
+-define(wxStyledTextCtrl_GetSelectionMode, 3321).
+-define(wxStyledTextCtrl_LineDownRectExtend, 3322).
+-define(wxStyledTextCtrl_LineUpRectExtend, 3323).
+-define(wxStyledTextCtrl_CharLeftRectExtend, 3324).
+-define(wxStyledTextCtrl_CharRightRectExtend, 3325).
+-define(wxStyledTextCtrl_HomeRectExtend, 3326).
+-define(wxStyledTextCtrl_VCHomeRectExtend, 3327).
+-define(wxStyledTextCtrl_LineEndRectExtend, 3328).
+-define(wxStyledTextCtrl_PageUpRectExtend, 3329).
+-define(wxStyledTextCtrl_PageDownRectExtend, 3330).
+-define(wxStyledTextCtrl_StutteredPageUp, 3331).
+-define(wxStyledTextCtrl_StutteredPageUpExtend, 3332).
+-define(wxStyledTextCtrl_StutteredPageDown, 3333).
+-define(wxStyledTextCtrl_StutteredPageDownExtend, 3334).
+-define(wxStyledTextCtrl_WordLeftEnd, 3335).
+-define(wxStyledTextCtrl_WordLeftEndExtend, 3336).
+-define(wxStyledTextCtrl_WordRightEnd, 3337).
+-define(wxStyledTextCtrl_WordRightEndExtend, 3338).
+-define(wxStyledTextCtrl_SetWhitespaceChars, 3339).
+-define(wxStyledTextCtrl_SetCharsDefault, 3340).
+-define(wxStyledTextCtrl_AutoCompGetCurrent, 3341).
+-define(wxStyledTextCtrl_Allocate, 3342).
+-define(wxStyledTextCtrl_FindColumn, 3343).
+-define(wxStyledTextCtrl_GetCaretSticky, 3344).
+-define(wxStyledTextCtrl_SetCaretSticky, 3345).
+-define(wxStyledTextCtrl_ToggleCaretSticky, 3346).
+-define(wxStyledTextCtrl_SetPasteConvertEndings, 3347).
+-define(wxStyledTextCtrl_GetPasteConvertEndings, 3348).
+-define(wxStyledTextCtrl_SelectionDuplicate, 3349).
+-define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3350).
+-define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3351).
+-define(wxStyledTextCtrl_StartRecord, 3352).
+-define(wxStyledTextCtrl_StopRecord, 3353).
+-define(wxStyledTextCtrl_SetLexer, 3354).
+-define(wxStyledTextCtrl_GetLexer, 3355).
+-define(wxStyledTextCtrl_Colourise, 3356).
+-define(wxStyledTextCtrl_SetProperty, 3357).
+-define(wxStyledTextCtrl_SetKeyWords, 3358).
+-define(wxStyledTextCtrl_SetLexerLanguage, 3359).
+-define(wxStyledTextCtrl_GetProperty, 3360).
+-define(wxStyledTextCtrl_GetStyleBitsNeeded, 3361).
+-define(wxStyledTextCtrl_GetCurrentLine, 3362).
+-define(wxStyledTextCtrl_StyleSetSpec, 3363).
+-define(wxStyledTextCtrl_StyleSetFont, 3364).
+-define(wxStyledTextCtrl_StyleSetFontAttr, 3365).
+-define(wxStyledTextCtrl_StyleSetCharacterSet, 3366).
+-define(wxStyledTextCtrl_StyleSetFontEncoding, 3367).
+-define(wxStyledTextCtrl_CmdKeyExecute, 3368).
+-define(wxStyledTextCtrl_SetMargins, 3369).
+-define(wxStyledTextCtrl_GetSelection, 3370).
+-define(wxStyledTextCtrl_PointFromPosition, 3371).
+-define(wxStyledTextCtrl_ScrollToLine, 3372).
+-define(wxStyledTextCtrl_ScrollToColumn, 3373).
-define(wxStyledTextCtrl_SetVScrollBar, 3374).
-define(wxStyledTextCtrl_SetHScrollBar, 3375).
-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3376).
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..a8604c76b9 100644
--- a/lib/wx/src/wxe_server.erl
+++ b/lib/wx/src/wxe_server.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
@@ -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..4186d73c88 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-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
@@ -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)),
@@ -129,11 +130,19 @@ treeCtrl(Config) ->
wxWindow:setSizerAndFit(Panel, Sizer),
wxFrame:show(Frame),
+ ok = wxTreeCtrl:expand(Tree, Root),
?m([], wxTreeCtrl:getItemData(Tree, Root)),
?m({data,item1}, wxTreeCtrl:getItemData(Tree, Item1)),
?m({data,item2}, wxTreeCtrl:getItemData(Tree, Item2)),
?m({data,item3}, wxTreeCtrl:getItemData(Tree, Item3)),
+ {true, {X0,Y0,W0,H0}} = ?m({_,_},wxTreeCtrl:getBoundingRect(Tree, Item1, [{textOnly, true}])),
+ ?m({true, {_,Y1,_,_}} when Y1 > Y0, wxTreeCtrl:getBoundingRect(Tree, Item2)),
+ ?m({Item1, _}, wxTreeCtrl:hitTest(Tree, {X0+W0 div 2, Y0+H0 div 2})),
+ ?m(true, wxTreeCtrl:isTreeItemIdOk(Item1)),
+ ?m({0, _}, wxTreeCtrl:hitTest(Tree, {X0+W0 div 2, Y0+H0+H0})),
+ ?m(false, wxTreeCtrl:isTreeItemIdOk(0)),
+
wxFrame:connect(Tree, command_tree_item_expanded),
wxFrame:connect(Tree, command_tree_item_collapsed),
wxFrame:connect(Frame, close_window),
@@ -476,7 +485,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/vsn.mk b/lib/wx/vsn.mk
index 719eb0daf0..1c406ccec6 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 0.99.2
+WX_VSN = 1.0
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/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 056097ab2b..1de854860c 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>2012</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,29 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Where necessary a comment stating encoding has been
+ added to Erlang files. The comment is meant to be removed
+ in Erlang/OTP R17B when UTF-8 becomes the default
+ encoding. </p>
+ <p>
+ Own Id: OTP-10630</p>
+ </item>
+ <item>
+ <p> Some examples overflowing the width of PDF pages have
+ been corrected. </p>
+ <p>
+ Own Id: OTP-10665</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
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_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/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/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 599bc0b9d3..de809e8ce0 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.2
+XMERL_VSN = 1.3.3
diff --git a/make/emd2exml.in b/make/emd2exml.in
index 16c38379d9..5bfe89894e 100644
--- a/make/emd2exml.in
+++ b/make/emd2exml.in
@@ -5,7 +5,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. 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
@@ -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 d74220588c..785926b997 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -4,7 +4,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -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
@@ -256,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
@@ -269,44 +270,44 @@ $(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl
$(MAN1DIR)/%.1: %.xml
- date=`date +"%B %e %Y"`; \
+ 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"`; \
+ 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"`; \
+ date=`date +"%B %e, %Y"`; \
specs_file=`pwd`/$(SPECDIR)/specs_$*.xml; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --stringparam specs_file "$$specs_file" --xinclude -path $(DOCGEN)/priv/dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
else
$(MAN3DIR)/%.3: %.xml
- date=`date +"%B %e %Y"`; \
+ 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 $<
endif
# left for compatibility
$(MAN4DIR)/%.4: %.xml
- date=`date +"%B %e %Y"`; \
+ 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 $<
$(MAN4DIR)/%.5: %.xml
- date=`date +"%B %e %Y"`; \
+ 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 $<
# left for compatibility
$(MAN6DIR)/%.6: %_app.xml
- date=`date +"%B %e %Y"`; \
+ 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 $<
$(MAN6DIR)/%.7: %_app.xml
- date=`date +"%B %e %Y"`; \
+ 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 $<
$(MAN9DIR)/%.9: %.xml
- date=`date +"%B %e %Y"`; \
+ 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 $<
@@ -314,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..0c9a8a087f 100644
--- a/make/otp_ded.mk.in
+++ b/make/otp_ded.mk.in
@@ -2,7 +2,7 @@
# ----------------------------------------------------
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-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,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 0be0a2de56..b6afcd1c8b 100644
--- a/make/otp_release_targets.mk
+++ b/make/otp_release_targets.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. 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
@@ -32,7 +32,7 @@ endif
ifeq ($(TOPDOC),)
$(HTMLDIR)/index.html: $(XML_FILES) $(SPECS_FILES)
- date=`date +"%B %e %Y"`; \
+ date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --noout \
--stringparam outdir $(HTMLDIR) \
--stringparam docgen "$(DOCGEN)" \
@@ -53,7 +53,7 @@ $(HTMLDIR)/index.html: $(XML_FILES) $(SPECS_FILES)
endif
$(HTMLDIR)/users_guide.html: $(XML_FILES)
- date=`date +"%B %e %Y"`; \
+ date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --noout \
--stringparam outdir $(HTMLDIR) \
--stringparam docgen "$(DOCGEN)" \
@@ -72,7 +72,7 @@ $(HTMLDIR)/users_guide.html: $(XML_FILES)
$(DOCGEN)/priv/xsl/db_html.xsl book.xml
%.fo: $(XML_FILES) $(SPECS_FILES)
- date=`date +"%B %e %Y"`; \
+ date=`date +"%B %e, %Y"`; \
$(XSLTPROC) \
--stringparam docgen "$(DOCGEN)" \
--stringparam gendate "$$date" \
@@ -94,7 +94,7 @@ ifneq ($(XML_FILES),)
# Generation of application index data
# ----------------------------------------------------
$(HTMLDIR)/$(APPLICATION).eix: $(XML_FILES) $(SPECS_FILES)
- date=`date +"%B %e %Y"`; \
+ date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --stringparam docgen "$(DOCGEN)" \
--stringparam gendate "$$date" \
--stringparam appname "$(APPLICATION)" \
@@ -106,6 +106,9 @@ $(HTMLDIR)/$(APPLICATION).eix: $(XML_FILES) $(SPECS_FILES)
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..938f878ebe
--- /dev/null
+++ b/make/output.mk.in
@@ -0,0 +1,139 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+# ----------------------------------------------------
+# Make include file for otp
+#
+# %CopyrightBegin%
+#
+# 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
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, 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
+# ----------------------------------------------------
+
+# These variables are used to produce less output when running make with V=0 or
+# with the --enable-silent-rules flag.
+#
+# For each compiler kind of file generated by a command during the building of
+# Erlang/OTP, a variable named <command>_verbose should be defined and used as
+# a command prefix. The generic gen_verbose variable is offered for
+# miscellaneous operations like sed, cp or magical Perl incantations.
+#
+# A second variable V_<COMMAND> is also provided for major compilation tools
+# like CC, ERLC or JAVAC.
+
+# DEFAULT_VERBOSITY is set by the --enable-silent-rules configure flag.
+ifeq ($(V),)
+V = @DEFAULT_VERBOSITY@
+endif
+
+# v_p should be used with `test` to dynamically print things.
+ifeq ($(V),0)
+v_p = 0
+else
+v_p = 1
+endif
+
+# V_at is the @ prefix when silent rules are enabled.
+V_at_0 = @
+V_at = $(V_at_$(V))
+
+# V_colon makes the prefixed command into a no-op if silent rules are enabled,
+# useful to annihilate an `echo` command.
+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)
+
+cpp_verbose_0 = @echo " CPP "$@;
+cpp_verbose = $(cpp_verbose_$(V))
+
+# For the diameter compiler.
+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))
+
+# V_MAKE isn't defined and shouldn't be to avoid breaking parallel building and
+# the following warning:
+#
+# warning: jobserver unavailable: using -j1. Add `+' to parent make rule.
+#
+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 should be used instead of gen_verbose when sed or another tool
+# is used to insert a version number into a file.
+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 85be25a8a1..093fde8034 100755
--- a/otp_build
+++ b/otp_build
@@ -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..61277306f1 100644
--- a/system/README
+++ b/system/README
@@ -1,7 +1,7 @@
-Erlang/OTP December 8, 2011
+Erlang/OTP Januari 25, 2013
-Release of Erlang 5.9/OTP R15B
+Release of Erlang 5.10/OTP R16
1. GENERAL
@@ -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/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 821175bb09..3bbaf7a0ad 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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..66311a3738 100644
--- a/system/doc/efficiency_guide/drivers.xml
+++ b/system/doc/efficiency_guide/drivers.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2011</year>
+ <year>2009</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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/part.xml b/system/doc/embedded/part.xml
index abedce46d6..6dd0b73965 100644
--- a/system/doc/embedded/part.xml
+++ b/system/doc/embedded/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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..d11f5a1004 100644
--- a/system/doc/embedded/xmlfiles.mk
+++ b/system/doc/embedded/xmlfiles.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009. All Rights Reserved.
+# Copyright Ericsson AB 2009-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,5 +18,4 @@
#
EMBEDDED_CHAPTER_FILES = \
embedded_solaris.xml \
- embedded_nt.xml \
- vxworks.xml
+ embedded_nt.xml
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 8affeaf10a..6923f52d8a 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -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..2cffd30283 100644
--- a/system/doc/installation_guide/part.xml
+++ b/system/doc/installation_guide/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2000</year><year>2010</year>
+ <year>2000</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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..3995c607af 100644
--- a/system/doc/installation_guide/xmlfiles.mk
+++ b/system/doc/installation_guide/xmlfiles.mk
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-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
@@ -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/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml
index df1c0f8fa8..531d50b2cf 100644
--- a/system/doc/reference_manual/data_types.xml
+++ b/system/doc/reference_manual/data_types.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -393,7 +393,15 @@ hello
12> <input>term_to_binary({a,b,c}).</input>
&lt;&lt;131,104,3,100,0,1,97,100,0,1,98,100,0,1,99>>
13> <input>binary_to_term(&lt;&lt;131,104,3,100,0,1,97,100,0,1,98,100,0,1,99>>).</input>
-{a,b,c}</pre>
+{a,b,c}
+14> <input>binary_to_integer(&lt;&lt;"77">>).</input>
+77
+15> <input>integer_to_binary(77).</input>
+&lt;&lt;"77">>
+16> <input>float_to_binary(7.0).</input>
+&lt;&lt;"7.00000000000000000000e+00">>
+17> <input>binary_to_float(&lt;&lt;"7.000e+00>>").</input>
+7.0</pre>
</section>
</chapter>
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..58e2baac17 100644
--- a/system/doc/reference_manual/ports.xml
+++ b/system/doc/reference_manual/ports.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml
index c3620f83f6..1279493ba8 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2003</year><year>2011</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Types and Function Specifications</title>
@@ -30,111 +30,121 @@
</header>
<section>
- <title>Introduction of Types</title>
- <p>
- Erlang is a dynamically typed language. Still, it comes with a
- language extension for declaring sets of Erlang terms to form a
- particular type, effectively forming a specific sub-type of the set
- of all Erlang terms.
- </p>
- <p>
- Subsequently, these types can be used to specify types of record fields
- and the argument and return types of functions.
- </p>
- <p>
- Type information can be used to document function interfaces,
- provide more information for bug detection tools such as <c>Dialyzer</c>,
- and can be exploited by documentation tools such as <c>Edoc</c> for
- generating program documentation of various forms.
- It is expected that the type language described in this document will
- supersede and replace the purely comment-based <c>@type</c> and
- <c>@spec</c> declarations used by <c>Edoc</c>.
- </p>
+ <title>The Erlang Type Language</title>
+ <p>
+ Erlang is a dynamically typed language. Still, it comes with a
+ notation for declaring sets of Erlang terms to form a particular
+ type, effectively forming a specific sub-type of the set of all
+ Erlang terms.
+ </p>
+ <p>
+ Subsequently, these types can be used to specify types of record fields
+ and the argument and return types of functions.
+ </p>
+ <p>
+ Type information can be used to document function interfaces,
+ provide more information for bug detection tools such as <c>Dialyzer</c>,
+ and can be exploited by documentation tools such as <c>Edoc</c> for
+ generating program documentation of various forms.
+ It is expected that the type language described in this document will
+ supersede and replace the purely comment-based <c>@type</c> and
+ <c>@spec</c> declarations used by <c>Edoc</c>.
+ </p>
</section>
<section>
<marker id="syntax"></marker>
<title>Types and their Syntax</title>
<p>
- Types describe sets of Erlang terms.
- Types consist and are built from a set of predefined types (e.g. <c>integer()</c>,
- <c>atom()</c>, <c>pid()</c>, ...) described below.
- Predefined types represent a typically infinite set of Erlang terms which
- belong to this type.
- For example, the type <c>atom()</c> stands for the set of all Erlang atoms.
- </p>
- <p>
- For integers and atoms, we allow for singleton types (e.g. the integers <c>-1</c>
- and <c>42</c> or the atoms <c>'foo'</c> and <c>'bar'</c>).
+ Types describe sets of Erlang terms.
+ Types consist and are built from a set of predefined types
+ (e.g. <c>integer()</c>, <c>atom()</c>, <c>pid()</c>, ...)
+ described below.
+ Predefined types represent a typically infinite set of Erlang terms which
+ belong to this type. For example, the type <c>atom()</c> stands for the
+ set of all Erlang atoms.
+ </p>
+ <p>
+ For integers and atoms, we allow for singleton types (e.g. the integers
+ <c>-1</c> and <c>42</c> or the atoms <c>'foo'</c> and <c>'bar'</c>).
- All other types are built using unions of either predefined types or singleton
- types. In a type union between a type and one of its sub-types the sub-type is
- absorbed by the super-type and the union is subsequently treated as if the
- sub-type was not a constituent of the union. For example, the type union:
+ All other types are built using unions of either predefined
+ types or singleton types. In a type union between a type and one
+ of its sub-types the sub-type is absorbed by the super-type and
+ the union is subsequently treated as if the sub-type was not a
+ constituent of the union. For example, the type union:
</p>
- <pre>
- atom() | 'bar' | integer() | 42</pre>
- <p>
- describes the same set of terms as the type union:
- </p>
- <pre>
-atom() | integer()</pre>
- <p>
- Because of sub-type relations that exist between types, types form a lattice
- where the topmost element, any(), denotes the set of all Erlang terms and
- the bottom-most element, none(), denotes the empty set of terms.
- </p>
- <p>
- The set of predefined types and the syntax for types is given below:
- </p>
- <pre><![CDATA[
-Type :: any() %% The top type, the set of all Erlang terms.
- | none() %% The bottom type, contains no terms.
- | pid()
- | port()
- | reference()
- | [] %% nil
- | Atom
- | Binary
- | float()
- | Fun
- | Integer
- | List
- | Tuple
- | Union
- | UserDefined %% described in Section 2
+ <pre> atom() | 'bar' | integer() | 42</pre>
+ <p>
+ describes the same set of terms as the type union:
+ </p>
+ <pre> atom() | integer()</pre>
+ <p>
+ Because of sub-type relations that exist between types, types
+ form a lattice where the topmost element, <c>any()</c>, denotes
+ the set of all Erlang terms and the bottom-most element, <c>none()</c>,
+ denotes the empty set of terms.
+ </p>
+ <p>
+ The set of predefined types and the syntax for types is given below:
+ </p>
+ <pre><![CDATA[
+ Type :: any() %% The top type, the set of all Erlang terms
+ | none() %% The bottom type, contains no terms
+ | pid()
+ | port()
+ | reference()
+ | [] %% nil
+ | Atom
+ | Bitstring
+ | float()
+ | Fun
+ | Integer
+ | List
+ | Tuple
+ | Union
+ | UserDefined %% described in Section 6.3
-Union :: Type1 | Type2
+ Atom :: atom()
+ | Erlang_Atom %% 'foo', 'bar', ...
-Atom :: atom()
- | Erlang_Atom %% 'foo', 'bar', ...
+ Bitstring :: <<>>
+ | <<_:M>> %% M is a positive integer
+ | <<_:_*N>> %% N is a positive integer
+ | <<_:M, _:_*N>>
-Binary :: binary() %% <<_:_ * 8>>
- | <<>>
- | <<_:Erlang_Integer>> %% Base size
- | <<_:_*Erlang_Integer>> %% Unit size
- | <<_:Erlang_Integer, _:_*Erlang_Integer>>
+ Fun :: fun() %% any function
+ | fun((...) -> Type) %% any arity, returning Type
+ | fun(() -> Type)
+ | fun((TList) -> Type)
-Fun :: fun() %% any function
- | fun((...) -> Type) %% any arity, returning Type
- | fun(() -> Type)
- | fun((TList) -> Type)
+ Integer :: integer()
+ | Erlang_Integer %% ..., -1, 0, 1, ... 42 ...
+ | Erlang_Integer..Erlang_Integer %% specifies an integer range
-Integer :: integer()
- | Erlang_Integer %% ..., -1, 0, 1, ... 42 ...
- | Erlang_Integer..Erlang_Integer %% specifies an integer range
+ List :: list(Type) %% Proper list ([]-terminated)
+ | improper_list(Type1, Type2) %% Type1=contents, Type2=termination
+ | maybe_improper_list(Type1, Type2) %% Type1 and Type2 as above
-List :: list(Type) %% Proper list ([]-terminated)
- | improper_list(Type1, Type2) %% Type1=contents, Type2=termination
- | maybe_improper_list(Type1, Type2) %% Type1 and Type2 as above
+ Tuple :: tuple() %% stands for a tuple of any size
+ | {}
+ | {TList}
-Tuple :: tuple() %% stands for a tuple of any size
- | {}
- | {TList}
+ TList :: Type
+ | Type, TList
-TList :: Type
- | Type, TList
+ Union :: Type1 | Type2
]]></pre>
<p>
+ The general form of bitstrings is <c>&lt;&lt;_:M, _:_*N&gt;&gt;</c>,
+ where <c>M</c> and <c>N</c> are positive integers. It denotes a
+ bitstring that is <c>M + (k*N)</c> bits long (i.e., a bitstring that
+ starts with <c>M</c> bits and continues with <c>k</c> segments of
+ <c>N</c> bits each, where <c>k</c> is also a positive integer).
+ The notations <c>&lt;&lt;_:_*N&gt;&gt;</c>, <c>&lt;&lt;_:M&gt;&gt;</c>,
+ and <c>&lt;&lt;&gt;&gt;</c> are convenient shorthands for the cases
+ that <c>M</c>, <c>N</c>, or both, respectively, are zero.
+ </p>
+ <p>
Because lists are commonly used, they have shorthand type notations.
The type <c>list(T)</c> has the shorthand <c>[T]</c>.
The shorthand <c>[T,...]</c> stands for
@@ -154,11 +164,17 @@ TList :: Type
</p>
<table>
<row>
- <cell><b>Built-in type</b></cell><cell><b>Stands for</b></cell>
+ <cell><b>Built-in type</b></cell><cell><b>Defined as</b></cell>
</row>
<row>
<cell><c>term()</c></cell><cell><c>any()</c></cell>
</row>
+ <row>
+ <cell><c>binary()</c></cell><cell><c>&lt;&lt;_:*8&gt;&gt;</c></cell>
+ </row>
+ <row>
+ <cell><c>bitstring()</c></cell><cell><c>&lt;&lt;_:*1&gt;&gt;</c></cell>
+ </row>
<row>
<cell><c>boolean()</c></cell><cell><c>'false' | 'true'</c></cell>
</row>
@@ -169,15 +185,6 @@ TList :: Type
<cell><c>char()</c></cell><cell><c>0..16#10ffff</c></cell>
</row>
<row>
- <cell><c>non_neg_integer()</c></cell><cell><c>0..</c></cell>
- </row>
- <row>
- <cell><c>pos_integer()</c></cell><cell><c>1..</c></cell>
- </row>
- <row>
- <cell><c>neg_integer()</c></cell><cell><c>..-1</c></cell>
- </row>
- <row>
<cell><c>number()</c></cell><cell><c>integer() | float()</c></cell>
</row>
<row>
@@ -196,7 +203,7 @@ TList :: Type
<cell><c>nonempty_string()</c></cell><cell><c>[char(),...]</c></cell>
</row>
<row>
- <cell><c>iolist()</c></cell><cell><c>maybe_improper_list(char() | binary() | iolist(), binary() | [])</c></cell>
+ <cell><c>iolist()</c></cell><cell><c>maybe_improper_list(byte() | binary() | iolist(), binary() | [])</c></cell>
</row>
<row>
<cell><c>module()</c></cell><cell><c>atom()</c></cell>
@@ -214,35 +221,54 @@ TList :: Type
<cell><c>no_return()</c></cell><cell><c>none()</c></cell>
</row>
</table>
+ <p>
+ In addition, the following three built-in types exist and can be
+ thought as defined below, though strictly their "type definition" is
+ not valid syntax according to the type language defined above.
+ </p>
+ <table>
+ <row>
+ <cell><b>Built-in type</b></cell><cell><b>Could be thought defined by the syntax</b></cell>
+ </row>
+ <row>
+ <cell><c>non_neg_integer()</c></cell><cell><c>0..</c></cell>
+ </row>
+ <row>
+ <cell><c>pos_integer()</c></cell><cell><c>1..</c></cell>
+ </row>
+ <row>
+ <cell><c>neg_integer()</c></cell><cell><c>..-1</c></cell>
+ </row>
+ </table>
<p>
Users are not allowed to define types with the same names as the
predefined or built-in ones. This is checked by the compiler and
its violation results in a compilation error.
- (For bootstrapping purposes, it can also result to just a warning if this
- involves a built-in type which has just been introduced.)
+ (For bootstrapping purposes, it can also result to just a warning
+ if this involves a built-in type which has just been introduced.)
</p>
<note>
The following built-in list types also exist,
but they are expected to be rarely used. Hence, they have long names:
</note>
<pre>
-nonempty_maybe_improper_list(Type) :: nonempty_maybe_improper_list(Type, any())
-nonempty_maybe_improper_list() :: nonempty_maybe_improper_list(any())</pre>
+ nonempty_maybe_improper_list(Type) :: nonempty_maybe_improper_list(Type, any())
+ nonempty_maybe_improper_list() :: nonempty_maybe_improper_list(any())</pre>
<p>
where the following two types
define the set of Erlang terms one would expect:
</p>
<pre>
-nonempty_improper_list(Type1, Type2)
-nonempty_maybe_improper_list(Type1, Type2)</pre>
+ nonempty_improper_list(Type1, Type2)
+ nonempty_maybe_improper_list(Type1, Type2)</pre>
<p>
Also for convenience, we allow for record notation to be used.
Records are just shorthands for the corresponding tuples.
</p>
<pre>
-Record :: #Erlang_Atom{}
- | #Erlang_Atom{Fields}</pre>
+ Record :: #Erlang_Atom{}
+ | #Erlang_Atom{Fields}</pre>
<p>
Records have been extended to possibly contain type information.
This is described in the sub-section <seealso marker="#typeinrecords">"Type information in record declarations"</seealso> below.
@@ -257,8 +283,8 @@ Record :: #Erlang_Atom{}
compiler attributes as in the following:
</p>
<pre>
--type my_struct_type() :: Type.
--opaque my_opaq_type() :: Type.</pre>
+ -type my_struct_type() :: Type.
+ -opaque my_opaq_type() :: Type.</pre>
<p>
where the type name is an atom (<c>'my_struct_type'</c> in the above)
followed by parentheses. Type is a type as defined in the
@@ -279,23 +305,23 @@ Record :: #Erlang_Atom{}
definition. A concrete example appears below:
</p>
<pre>
--type orddict(Key, Val) :: [{Key, Val}].</pre>
+ -type orddict(Key, Val) :: [{Key, Val}].</pre>
<p>
A module can export some types in order to declare that other modules
are allowed to refer to them as <em>remote types</em>.
This declaration has the following form:
<pre>
--export_type([T1/A1, ..., Tk/Ak]).</pre>
+ -export_type([T1/A1, ..., Tk/Ak]).</pre>
where the Ti's are atoms (the name of the type) and the Ai's are their
arguments. An example is given below:
<pre>
--export_type([my_struct_type/0, orddict/2]).</pre>
+ -export_type([my_struct_type/0, orddict/2]).</pre>
Assuming that these types are exported from module <c>'mod'</c> then
one can refer to them from other modules using remote type expressions
like those below:
<pre>
-mod:my_struct_type()
-mod:orddict(atom(), term())</pre>
+ mod:my_struct_type()
+ mod:orddict(atom(), term())</pre>
One is not allowed to refer to types which are not declared as exported.
</p>
<p>
@@ -317,19 +343,19 @@ mod:orddict(atom(), term())</pre>
record. The syntax for this is:
</p>
<pre>
--record(rec, {field1 :: Type1, field2, field3 :: Type3}).</pre>
+ -record(rec, {field1 :: Type1, field2, field3 :: Type3}).</pre>
<p>
For fields without type annotations, their type defaults to any().
I.e., the above is a shorthand for:
</p>
<pre>
--record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).</pre>
+ -record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).</pre>
<p>
In the presence of initial values for fields,
the type must be declared after the initialization as in the following:
</p>
<pre>
--record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3}).</pre>
+ -record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3}).</pre>
<p>
Naturally, the initial values for fields should be compatible
with (i.e. a member of) the corresponding types.
@@ -340,13 +366,13 @@ mod:orddict(atom(), term())</pre>
effects:
</p>
<pre>
--record(rec, {f1 = 42 :: integer(),
- f2 :: float(),
- f3 :: 'a' | 'b'}).
+ -record(rec, {f1 = 42 :: integer(),
+ f2 :: float(),
+ f3 :: 'a' | 'b'}).
--record(rec, {f1 = 42 :: integer(),
- f2 :: 'undefined' | float(),
- f3 :: 'undefined' | 'a' | 'b'}).</pre>
+ -record(rec, {f1 = 42 :: integer(),
+ f2 :: 'undefined' | float(),
+ f3 :: 'undefined' | 'a' | 'b'}).</pre>
<p>
For this reason, it is recommended that records contain initializers,
whenever possible.
@@ -355,15 +381,13 @@ mod:orddict(atom(), term())</pre>
Any record, containing type information or not, once defined,
can be used as a type using the syntax:
</p>
- <pre>
-#rec{}</pre>
+ <pre> #rec{}</pre>
<p>
In addition, the record fields can be further specified when using
a record type by adding type information about the field in
the following manner:
</p>
- <pre>
-#rec{some_field :: Type}</pre>
+ <pre> #rec{some_field :: Type}</pre>
<p>
Any unspecified fields are assumed to have the type in the original
record declaration.
@@ -377,7 +401,7 @@ mod:orddict(atom(), term())</pre>
compiler attribute <c>'-spec'</c>. The general format is as follows:
</p>
<pre>
--spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType.</pre>
+ -spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType.</pre>
<p>
The arity of the function has to match the number of arguments,
or else a compilation error occurs.
@@ -392,19 +416,19 @@ mod:orddict(atom(), term())</pre>
For most uses within a given module, the following shorthand suffices:
</p>
<pre>
--spec Function(ArgType1, ..., ArgTypeN) -> ReturnType.</pre>
+ -spec Function(ArgType1, ..., ArgTypeN) -> ReturnType.</pre>
<p>
Also, for documentation purposes, argument names can be given:
</p>
<pre>
--spec Function(ArgName1 :: Type1, ..., ArgNameN :: TypeN) -> RT.</pre>
+ -spec Function(ArgName1 :: Type1, ..., ArgNameN :: TypeN) -> RT.</pre>
<p>
A function specification can be overloaded.
That is, it can have several types, separated by a semicolon (<c>;</c>):
</p>
<pre>
--spec foo(T1, T2) -> T3
- ; (T4, T5) -> T6.</pre>
+ -spec foo(T1, T2) -> T3
+ ; (T4, T5) -> T6.</pre>
<p>
A current restriction, which currently results in a warning
(OBS: not an error) by the compiler, is that the domains of
@@ -412,8 +436,8 @@ mod:orddict(atom(), term())</pre>
For example, the following specification results in a warning:
</p>
<pre>
--spec foo(pos_integer()) -> pos_integer()
- ; (integer()) -> integer().</pre>
+ -spec foo(pos_integer()) -> pos_integer()
+ ; (integer()) -> integer().</pre>
<p>
Type variables can be used in specifications to specify relations for
the input and output arguments of a function.
@@ -421,47 +445,66 @@ mod:orddict(atom(), term())</pre>
polymorphic identity function:
</p>
<pre>
--spec id(X) -> X.</pre>
+ -spec id(X) -> X.</pre>
<p>
However, note that the above specification does not restrict the input
and output type in any way.
- We can constrain these types by guard-like subtype constraints:
+ We can constrain these types by guard-like subtype constraints
+ and provide bounded quantification:
</p>
- <pre>
--spec id(X) -> X when is_subtype(X, tuple()).</pre>
+ <pre> -spec id(X) -> X when X :: tuple().</pre>
<p>
- or equivalently by the more succinct and more modern form of the above:
- </p>
- <pre>
--spec id(X) -> X when X :: tuple().</pre>
- <p>
- and provide bounded quantification. Currently, the <c>::</c> constraint
- (the <c>is_subtype/2</c> guard) is the only guard constraint which can
- be used in the <c>'when'</c> part of a <c>'-spec'</c> attribute.
+ Currently, the <c>::</c> constraint (read as <c>is_subtype</c>) is
+ the only guard constraint which can be used in the <c>'when'</c>
+ part of a <c>'-spec'</c> attribute.
</p>
+ <note>
+ <p>
+ The above function specification, using multiple occurrences of
+ the same type variable, provides more type information than the
+ function specification below where the type variables are missing:
+ </p>
+ <pre> -spec id(tuple()) -> tuple().</pre>
+ <p>
+ The latter specification says that the function takes some tuple
+ and returns some tuple, while the one with the <c>X</c> type
+ variable specifies that the function takes a tuple and returns
+ <em>the same</em> tuple.
+ </p>
+ <p>
+ However, it's up to the tools that process the specs to choose
+ whether to take this extra information into account or ignore it.
+ </p>
+ </note>
<p>
The scope of an <c>::</c> constraint is the
<c>(...) -> RetType</c>
specification after which it appears. To avoid confusion,
we suggest that different variables are used in different
- constituents of an overloaded contract as in the example below:
+ constituents of an overloaded contract as in the example below:
</p>
<pre>
--spec foo({X, integer()}) -> X when X :: atom()
- ; ([Y]) -> Y when Y :: number().</pre>
+ -spec foo({X, integer()}) -> X when X :: atom()
+ ; ([Y]) -> Y when Y :: number().</pre>
+ <note>
+ For backwards compatibility the following form is also allowed:
+ <pre> -spec id(X) -> X when is_subtype(X, tuple()).</pre>
+ <p>
+ but its use is discouraged. It will be taken out in a future
+ Erlang/OTP release.
+ </p>
+ </note>
<p>
Some functions in Erlang are not meant to return;
either because they define servers or because they are used to
throw exceptions as the function below:
</p>
- <pre>
-my_error(Err) -> erlang:throw({error, Err}).</pre>
+ <pre> my_error(Err) -> erlang:throw({error, Err}).</pre>
<p>
- For such functions we recommend the use of the special no_return()
+ For such functions we recommend the use of the special <c>no_return()</c>
type for their "return", via a contract of the form:
</p>
- <pre>
--spec my_error(term()) -> no_return().</pre>
+ <pre> -spec my_error(term()) -> no_return().</pre>
</section>
</chapter>
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index 673ba44c94..37466fa2d9 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -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
@@ -162,7 +162,7 @@ $(MAN_INDEX): $(MAN_INDEX_SCRIPT)
#--------------------------------------------------------------------------
$(HTMLDIR)/highlights.html: highlights.xml
- date=`date +"%B %e %Y"`; \
+ date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --output $(@) \
--stringparam docgen "$(DOCGEN)" \
--stringparam topdocdir "$(TOPDOCDIR)" \
@@ -179,7 +179,7 @@ $(HTMLDIR)/highlights.html: highlights.xml
$(HTMLDIR)/incompatible.html: incompatible.xml
- date=`date +"%B %e %Y"`; \
+ date=`date +"%B %e, %Y"`; \
$(XSLTPROC) --output $(@) --stringparam docgen "$(DOCGEN)" \
--stringparam topdocdir "$(TOPDOCDIR)" \
--stringparam pdfdir "$(PDFREFDIR)" \
diff --git a/xcomp/erl-xcomp-powerpc64-bgq-linux.conf b/xcomp/erl-xcomp-powerpc64-bgq-linux.conf
new file mode 100644
index 0000000000..1c45aaf86b
--- /dev/null
+++ b/xcomp/erl-xcomp-powerpc64-bgq-linux.conf
@@ -0,0 +1,272 @@
+## -*-shell-script-*-
+##
+## %CopyrightBegin%
+##
+## Copyright Ericsson AB 2009-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%
+##
+## 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=powerpc64-bgq-linux
+
+# * `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').
+
+## This path should really be part of the user's PATH environment, but
+## since it is highly unlikely that it will differ between Blue Gene/Q
+## installations, the path is hard-coded here for convenience.
+TOP_BIN=/bgsys/drivers/ppcfloor/gnu-linux/bin
+
+## All variables in this section can also be used when native compiling.
+
+# * `CC' - C compiler.
+CC=${TOP_BIN}/${erl_xcomp_host}-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=${TOP_BIN}/${erl_xcomp_host}-g++
+
+# * `CXXFLAGS' - C++ compiler flags.
+#CXXFLAGS=
+
+# * `LD' - Linker.
+LD=${TOP_BIN}/${erl_xcomp_host}-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=${TOP_BIN}/${erl_xcomp_host}-ranlib
+
+# * `AR' - `ar' archiving tool.
+AR=${TOP_BIN}/${erl_xcomp_host}-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=
+
+# * `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_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.
+#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=
+
+# * `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=
+
+## -----------------------------------------------------------------------------